From 019c5d667798fcc43db9bf091ede54e313eb4f39 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Mon, 22 Jul 2024 09:12:36 -0500 Subject: [PATCH] use linked types from the cipher prototype (#10167) - When adding a new cipher the cipher view isn't defined yet --- .../custom-fields.component.spec.ts | 76 ++++++++++++++----- .../custom-fields/custom-fields.component.ts | 28 ++++++- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.spec.ts b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.spec.ts index 7befcd59b0..9ae7f10e8f 100644 --- a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.spec.ts +++ b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.spec.ts @@ -6,13 +6,19 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { CipherType, FieldType, LoginLinkedId } from "@bitwarden/common/vault/enums"; +import { + CardLinkedId, + CipherType, + FieldType, + IdentityLinkedId, + LoginLinkedId, +} from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FieldView } from "@bitwarden/common/vault/models/view/field.view"; -import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { DialogService } from "@bitwarden/components"; import { BitPasswordInputToggleDirective } from "../../../../../components/src/form-field/password-input-toggle.directive"; +import { CipherFormConfig } from "../../abstractions/cipher-form-config.service"; import { CipherFormContainer } from "../../cipher-form-container"; import { CustomField, CustomFieldsComponent } from "./custom-fields.component"; @@ -25,8 +31,6 @@ const mockFieldViews = [ ] as FieldView[]; let originalCipherView: CipherView | null = new CipherView(); -originalCipherView.type = CipherType.Login; -originalCipherView.login = new LoginView(); describe("CustomFieldsComponent", () => { let component: CustomFieldsComponent; @@ -34,14 +38,14 @@ describe("CustomFieldsComponent", () => { let open: jest.Mock; let announce: jest.Mock; let patchCipher: jest.Mock; + let config: CipherFormConfig; beforeEach(async () => { open = jest.fn(); announce = jest.fn().mockResolvedValue(null); patchCipher = jest.fn(); originalCipherView = new CipherView(); - originalCipherView.type = CipherType.Login; - originalCipherView.login = new LoginView(); + config = {} as CipherFormConfig; await TestBed.configureTestingModule({ imports: [CustomFieldsComponent], @@ -52,7 +56,7 @@ describe("CustomFieldsComponent", () => { }, { provide: CipherFormContainer, - useValue: { patchCipher, originalCipherView, registerChildForm: jest.fn(), config: {} }, + useValue: { patchCipher, originalCipherView, registerChildForm: jest.fn(), config }, }, { provide: LiveAnnouncer, @@ -73,20 +77,6 @@ describe("CustomFieldsComponent", () => { }); describe("initializing", () => { - it("populates linkedFieldOptions", () => { - originalCipherView.login.linkedFieldOptions = new Map([ - [1, { i18nKey: "one-i18", propertyKey: "one" }], - [2, { i18nKey: "two-i18", propertyKey: "two" }], - ]); - - component.ngOnInit(); - - expect(component.linkedFieldOptions).toEqual([ - { value: 1, name: "one-i18" }, - { value: 2, name: "two-i18" }, - ]); - }); - it("populates customFieldsForm", () => { originalCipherView.fields = mockFieldViews; @@ -130,6 +120,50 @@ describe("CustomFieldsComponent", () => { expect(button.nativeElement.disabled).toBe(true); }); + + describe("linkedFieldOptions", () => { + /** Retrieve the numerical values of an enum object */ + const getEnumValues = (enumType: object) => + Object.values(enumType).filter((v) => typeof v === "number"); + + it("populates for login ciphers", () => { + config.cipherType = CipherType.Login; + + component.ngOnInit(); + + expect(component.linkedFieldOptions.map((o) => o.value)).toEqual( + expect.arrayContaining(getEnumValues(LoginLinkedId)), + ); + }); + + it("populates for card ciphers", () => { + config.cipherType = CipherType.Card; + + component.ngOnInit(); + + expect(component.linkedFieldOptions.map((o) => o.value)).toEqual( + expect.arrayContaining(getEnumValues(CardLinkedId)), + ); + }); + + it("populates for identity ciphers", () => { + config.cipherType = CipherType.Identity; + + component.ngOnInit(); + + expect(component.linkedFieldOptions.map((o) => o.value)).toEqual( + expect.arrayContaining(getEnumValues(IdentityLinkedId)), + ); + }); + + it("sets an empty array for note ciphers", () => { + config.cipherType = CipherType.SecureNote; + + component.ngOnInit(); + + expect(component.linkedFieldOptions).toEqual([]); + }); + }); }); describe("adding new field", () => { diff --git a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts index ace8ce7987..7dc9629d4b 100644 --- a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts +++ b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts @@ -21,8 +21,11 @@ import { Subject, switchMap, take } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { FieldType, LinkedIdType } from "@bitwarden/common/vault/enums"; +import { CipherType, FieldType, LinkedIdType } from "@bitwarden/common/vault/enums"; +import { CardView } from "@bitwarden/common/vault/models/view/card.view"; import { FieldView } from "@bitwarden/common/vault/models/view/field.view"; +import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view"; +import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { DialogService, SectionComponent, @@ -131,10 +134,9 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit { } ngOnInit() { + const linkedFieldsOptionsForCipher = this.getLinkedFieldsOptionsForCipher(); // Populate options for linked custom fields - this.linkedFieldOptions = Array.from( - this.cipherFormContainer.originalCipherView?.linkedFieldOptions?.entries() ?? [], - ) + this.linkedFieldOptions = Array.from(linkedFieldsOptionsForCipher?.entries() ?? []) .map(([id, linkedFieldOption]) => ({ name: this.i18nService.t(linkedFieldOption.i18nKey), value: id, @@ -303,6 +305,24 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit { } } + /** + * Returns the linked field options for the current cipher type + * + * Note: Note ciphers do not have linked fields + */ + private getLinkedFieldsOptionsForCipher() { + switch (this.cipherFormContainer.config.cipherType) { + case CipherType.Login: + return LoginView.prototype.linkedFieldOptions; + case CipherType.Card: + return CardView.prototype.linkedFieldOptions; + case CipherType.Identity: + return IdentityView.prototype.linkedFieldOptions; + default: + return null; + } + } + /** Create `FieldView` from the form objects and update the cipher */ private updateCipher(fields: CustomField[]) { const newFields = fields.map((field: CustomField) => {