mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-11 10:10:25 +01:00
[PM-10993] Browser Refresh - Fix duplicate password generation emissions in Firefox (#10704)
* [PM-10993] Avoid saving the generation options to state when toggling the password type * [PM-10993] Fix tests
This commit is contained in:
parent
9e093f88af
commit
fbf9c5abfa
@ -126,15 +126,22 @@ describe("CipherFormGeneratorComponent", () => {
|
|||||||
expect(fixture.nativeElement.querySelector("bit-toggle-group")).toBeTruthy();
|
expect(fixture.nativeElement.querySelector("bit-toggle-group")).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should save password options when the password type is updated", async () => {
|
it("should update the generated value when the password type is updated", fakeAsync(async () => {
|
||||||
mockLegacyPasswordGenerationService.generatePassword.mockResolvedValue("generated-password");
|
mockLegacyPasswordGenerationService.generatePassword
|
||||||
|
.mockResolvedValueOnce("first-password")
|
||||||
|
.mockResolvedValueOnce("second-password");
|
||||||
|
|
||||||
|
component.ngOnChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component["generatedValue"]).toBe("first-password");
|
||||||
|
|
||||||
await component["updatePasswordType"]("passphrase");
|
await component["updatePasswordType"]("passphrase");
|
||||||
|
tick();
|
||||||
|
|
||||||
expect(mockLegacyPasswordGenerationService.saveOptions).toHaveBeenCalledWith({
|
expect(component["generatedValue"]).toBe("second-password");
|
||||||
type: "passphrase",
|
expect(mockLegacyPasswordGenerationService.generatePassword).toHaveBeenCalledTimes(2);
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
it("should update the password history when a new password is generated", fakeAsync(() => {
|
it("should update the password history when a new password is generated", fakeAsync(() => {
|
||||||
mockLegacyPasswordGenerationService.generatePassword.mockResolvedValue("new-password");
|
mockLegacyPasswordGenerationService.generatePassword.mockResolvedValue("new-password");
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, DestroyRef, EventEmitter, Input, OnChanges, Output } from "@angular/core";
|
import { Component, DestroyRef, EventEmitter, Input, OnChanges, Output } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { firstValueFrom, map, startWith, Subject, Subscription, switchMap, tap } from "rxjs";
|
import {
|
||||||
|
combineLatest,
|
||||||
|
map,
|
||||||
|
merge,
|
||||||
|
shareReplay,
|
||||||
|
startWith,
|
||||||
|
Subject,
|
||||||
|
Subscription,
|
||||||
|
switchMap,
|
||||||
|
take,
|
||||||
|
tap,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@ -61,18 +72,13 @@ export class CipherFormGeneratorComponent implements OnChanges {
|
|||||||
|
|
||||||
protected regenerateButtonTitle: string;
|
protected regenerateButtonTitle: string;
|
||||||
protected regenerate$ = new Subject<void>();
|
protected regenerate$ = new Subject<void>();
|
||||||
|
protected passwordTypeSubject$ = new Subject<GeneratorType>();
|
||||||
/**
|
/**
|
||||||
* The currently generated value displayed to the user.
|
* The currently generated value displayed to the user.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected generatedValue: string = "";
|
protected generatedValue: string = "";
|
||||||
|
|
||||||
/**
|
|
||||||
* The current password generation options.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private passwordOptions$ = this.legacyPasswordGenerationService.getOptions$();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current username generation options.
|
* The current username generation options.
|
||||||
* @private
|
* @private
|
||||||
@ -80,10 +86,30 @@ export class CipherFormGeneratorComponent implements OnChanges {
|
|||||||
private usernameOptions$ = this.legacyUsernameGenerationService.getOptions$();
|
private usernameOptions$ = this.legacyUsernameGenerationService.getOptions$();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current password type specified by the password generation options.
|
* The current password type selected in the UI. Starts with the saved value from the service.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected passwordType$ = this.passwordOptions$.pipe(map(([options]) => options.type));
|
protected passwordType$ = merge(
|
||||||
|
this.legacyPasswordGenerationService.getOptions$().pipe(
|
||||||
|
take(1),
|
||||||
|
map(([options]) => options.type),
|
||||||
|
),
|
||||||
|
this.passwordTypeSubject$,
|
||||||
|
).pipe(shareReplay({ bufferSize: 1, refCount: false }));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current password generation options.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private passwordOptions$ = combineLatest([
|
||||||
|
this.legacyPasswordGenerationService.getOptions$(),
|
||||||
|
this.passwordType$,
|
||||||
|
]).pipe(
|
||||||
|
map(([[options], type]) => {
|
||||||
|
options.type = type;
|
||||||
|
return options;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks the regenerate$ subscription
|
* Tracks the regenerate$ subscription
|
||||||
@ -121,7 +147,7 @@ export class CipherFormGeneratorComponent implements OnChanges {
|
|||||||
.pipe(
|
.pipe(
|
||||||
startWith(null),
|
startWith(null),
|
||||||
switchMap(() => this.passwordOptions$),
|
switchMap(() => this.passwordOptions$),
|
||||||
switchMap(([options]) => this.legacyPasswordGenerationService.generatePassword(options)),
|
switchMap((options) => this.legacyPasswordGenerationService.generatePassword(options)),
|
||||||
tap(async (password) => {
|
tap(async (password) => {
|
||||||
await this.legacyPasswordGenerationService.addHistory(password);
|
await this.legacyPasswordGenerationService.addHistory(password);
|
||||||
}),
|
}),
|
||||||
@ -148,12 +174,10 @@ export class CipherFormGeneratorComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch the password generation type and save the options (generating a new password automatically).
|
* Switch the password generation type.
|
||||||
* @param value The new password generation type.
|
* @param value The new password generation type.
|
||||||
*/
|
*/
|
||||||
protected updatePasswordType = async (value: GeneratorType) => {
|
protected updatePasswordType = async (value: GeneratorType) => {
|
||||||
const [currentOptions] = await firstValueFrom(this.passwordOptions$);
|
this.passwordTypeSubject$.next(value);
|
||||||
currentOptions.type = value;
|
|
||||||
await this.legacyPasswordGenerationService.saveOptions(currentOptions);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user