mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 05:13:29 +00:00
[PM-16171] Simplelogin alias generation only generate random words instead the domain name (#13024)
* Exposes URI property from the cipher form. * Updates credential generator to accept the URI using a `website` attribute --------- Co-authored-by: ✨ Audrey ✨ <audrey@audreyality.com>
This commit is contained in:
@@ -9,6 +9,7 @@ export abstract class CipherFormGenerationService {
|
||||
|
||||
/**
|
||||
* Generates a random username. Called when the user clicks the "Generate Username" button in the UI.
|
||||
* @param uri The URI associated with the username generation request.
|
||||
*/
|
||||
abstract generateUsername(): Promise<string | null>;
|
||||
abstract generateUsername(uri: string): Promise<string | null>;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,12 @@ export abstract class CipherFormContainer {
|
||||
group: Exclude<CipherForm[K], undefined>,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* The website that the component publishes to edit email and username workflows.
|
||||
* Returns `null` when the cipher isn't bound to a website.
|
||||
*/
|
||||
abstract get website(): string | null;
|
||||
|
||||
/**
|
||||
* Method to update the cipherView with the new values. This method should be called by the child form components
|
||||
* @param updateFn - A function that takes the current cipherView and returns the updated cipherView
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { ChangeDetectorRef } from "@angular/core";
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { ReactiveFormsModule } from "@angular/forms";
|
||||
|
||||
import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
|
||||
import { CipherFormService } from "../abstractions/cipher-form.service";
|
||||
import { CipherFormCacheService } from "../services/default-cipher-form-cache.service";
|
||||
|
||||
import { CipherFormComponent } from "./cipher-form.component";
|
||||
|
||||
describe("CipherFormComponent", () => {
|
||||
let component: CipherFormComponent;
|
||||
let fixture: ComponentFixture<CipherFormComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CipherFormComponent, ReactiveFormsModule],
|
||||
providers: [
|
||||
{ provide: ChangeDetectorRef, useValue: {} },
|
||||
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
||||
{ provide: ToastService, useValue: { showToast: jest.fn() } },
|
||||
{ provide: CipherFormService, useValue: { saveCipher: jest.fn() } },
|
||||
{
|
||||
provide: CipherFormCacheService,
|
||||
useValue: { init: jest.fn(), getCachedCipherView: jest.fn() },
|
||||
},
|
||||
{ provide: ViewCacheService, useValue: { signal: jest.fn(() => () => null) } },
|
||||
{ provide: ConfigService, useValue: {} },
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CipherFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should create the component", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe("website", () => {
|
||||
it("should return null if updatedCipherView is null", () => {
|
||||
component["updatedCipherView"] = null as any;
|
||||
expect(component.website).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null if updatedCipherView.login is undefined", () => {
|
||||
component["updatedCipherView"] = new CipherView();
|
||||
delete component["updatedCipherView"].login;
|
||||
expect(component.website).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null if updatedCipherView.login is null", () => {
|
||||
component["updatedCipherView"] = new CipherView();
|
||||
component["updatedCipherView"].login = null as any;
|
||||
expect(component.website).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null if updatedCipherView.login.uris is undefined", () => {
|
||||
component["updatedCipherView"] = new CipherView();
|
||||
component["updatedCipherView"].login = { uris: undefined } as any;
|
||||
expect(component.website).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null if updatedCipherView.login.uris is null", () => {
|
||||
component["updatedCipherView"] = new CipherView();
|
||||
component["updatedCipherView"].login = { uris: null } as any;
|
||||
expect(component.website).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null if updatedCipherView.login.uris is an empty array", () => {
|
||||
component["updatedCipherView"] = new CipherView();
|
||||
component["updatedCipherView"].login = { uris: [] } as any;
|
||||
expect(component.website).toBeNull();
|
||||
});
|
||||
|
||||
it("should return updatedCipherView if login.uris contains at least one URI", () => {
|
||||
component["updatedCipherView"] = new CipherView();
|
||||
component["updatedCipherView"].login = { uris: [{ uri: "https://example.com" }] } as any;
|
||||
expect(component.website).toEqual("https://example.com");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -133,6 +133,10 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci
|
||||
*/
|
||||
protected updatedCipherView: CipherView | null;
|
||||
|
||||
get website(): string | null {
|
||||
return this.updatedCipherView?.login?.uris?.[0]?.uri ?? null;
|
||||
}
|
||||
|
||||
protected loading: boolean = true;
|
||||
|
||||
CipherType = CipherType;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
></tools-password-generator>
|
||||
<tools-username-generator
|
||||
*ngIf="type === 'username'"
|
||||
[website]="uri"
|
||||
[disableMargin]="disableMargin"
|
||||
(onGenerated)="onCredentialGenerated($event)"
|
||||
(onAlgorithm)="onAlgorithmSelected($event)"
|
||||
|
||||
@@ -21,6 +21,9 @@ export class CipherFormGeneratorComponent {
|
||||
@Input()
|
||||
onAlgorithmSelected: (selected: AlgorithmInfo) => void;
|
||||
|
||||
@Input()
|
||||
uri: string = "";
|
||||
|
||||
/**
|
||||
* The type of generator form to show.
|
||||
*/
|
||||
|
||||
@@ -46,7 +46,10 @@ describe("LoginDetailsSectionComponent", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
getInitialCipherView.mockClear();
|
||||
cipherFormContainer = mock<CipherFormContainer>({ getInitialCipherView });
|
||||
cipherFormContainer = mock<CipherFormContainer>({
|
||||
getInitialCipherView,
|
||||
website: "example.com",
|
||||
});
|
||||
|
||||
generationService = mock<CipherFormGenerationService>();
|
||||
auditService = mock<AuditService>();
|
||||
|
||||
@@ -243,7 +243,9 @@ export class LoginDetailsSectionComponent implements OnInit {
|
||||
* TODO: Browser extension needs a means to cache the current form so values are not lost upon navigating to the generator.
|
||||
*/
|
||||
generateUsername = async () => {
|
||||
const newUsername = await this.generationService.generateUsername();
|
||||
const newUsername = await this.generationService.generateUsername(
|
||||
this.cipherFormContainer.website,
|
||||
);
|
||||
if (newUsername) {
|
||||
this.loginDetailsForm.controls.username.patchValue(newUsername);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user