use linked types from the cipher prototype (#10167)
- When adding a new cipher the cipher view isn't defined yet
This commit is contained in:
parent
457c0795be
commit
019c5d6677
|
@ -6,13 +6,19 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { By } from "@angular/platform-browser";
|
import { By } from "@angular/platform-browser";
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
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 { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { FieldView } from "@bitwarden/common/vault/models/view/field.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 { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { BitPasswordInputToggleDirective } from "../../../../../components/src/form-field/password-input-toggle.directive";
|
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 { CipherFormContainer } from "../../cipher-form-container";
|
||||||
|
|
||||||
import { CustomField, CustomFieldsComponent } from "./custom-fields.component";
|
import { CustomField, CustomFieldsComponent } from "./custom-fields.component";
|
||||||
|
@ -25,8 +31,6 @@ const mockFieldViews = [
|
||||||
] as FieldView[];
|
] as FieldView[];
|
||||||
|
|
||||||
let originalCipherView: CipherView | null = new CipherView();
|
let originalCipherView: CipherView | null = new CipherView();
|
||||||
originalCipherView.type = CipherType.Login;
|
|
||||||
originalCipherView.login = new LoginView();
|
|
||||||
|
|
||||||
describe("CustomFieldsComponent", () => {
|
describe("CustomFieldsComponent", () => {
|
||||||
let component: CustomFieldsComponent;
|
let component: CustomFieldsComponent;
|
||||||
|
@ -34,14 +38,14 @@ describe("CustomFieldsComponent", () => {
|
||||||
let open: jest.Mock;
|
let open: jest.Mock;
|
||||||
let announce: jest.Mock;
|
let announce: jest.Mock;
|
||||||
let patchCipher: jest.Mock;
|
let patchCipher: jest.Mock;
|
||||||
|
let config: CipherFormConfig;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
open = jest.fn();
|
open = jest.fn();
|
||||||
announce = jest.fn().mockResolvedValue(null);
|
announce = jest.fn().mockResolvedValue(null);
|
||||||
patchCipher = jest.fn();
|
patchCipher = jest.fn();
|
||||||
originalCipherView = new CipherView();
|
originalCipherView = new CipherView();
|
||||||
originalCipherView.type = CipherType.Login;
|
config = {} as CipherFormConfig;
|
||||||
originalCipherView.login = new LoginView();
|
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CustomFieldsComponent],
|
imports: [CustomFieldsComponent],
|
||||||
|
@ -52,7 +56,7 @@ describe("CustomFieldsComponent", () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: CipherFormContainer,
|
provide: CipherFormContainer,
|
||||||
useValue: { patchCipher, originalCipherView, registerChildForm: jest.fn(), config: {} },
|
useValue: { patchCipher, originalCipherView, registerChildForm: jest.fn(), config },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: LiveAnnouncer,
|
provide: LiveAnnouncer,
|
||||||
|
@ -73,20 +77,6 @@ describe("CustomFieldsComponent", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("initializing", () => {
|
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", () => {
|
it("populates customFieldsForm", () => {
|
||||||
originalCipherView.fields = mockFieldViews;
|
originalCipherView.fields = mockFieldViews;
|
||||||
|
|
||||||
|
@ -130,6 +120,50 @@ describe("CustomFieldsComponent", () => {
|
||||||
|
|
||||||
expect(button.nativeElement.disabled).toBe(true);
|
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", () => {
|
describe("adding new field", () => {
|
||||||
|
|
|
@ -21,8 +21,11 @@ import { Subject, switchMap, take } 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";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
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 { 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 {
|
import {
|
||||||
DialogService,
|
DialogService,
|
||||||
SectionComponent,
|
SectionComponent,
|
||||||
|
@ -131,10 +134,9 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
const linkedFieldsOptionsForCipher = this.getLinkedFieldsOptionsForCipher();
|
||||||
// Populate options for linked custom fields
|
// Populate options for linked custom fields
|
||||||
this.linkedFieldOptions = Array.from(
|
this.linkedFieldOptions = Array.from(linkedFieldsOptionsForCipher?.entries() ?? [])
|
||||||
this.cipherFormContainer.originalCipherView?.linkedFieldOptions?.entries() ?? [],
|
|
||||||
)
|
|
||||||
.map(([id, linkedFieldOption]) => ({
|
.map(([id, linkedFieldOption]) => ({
|
||||||
name: this.i18nService.t(linkedFieldOption.i18nKey),
|
name: this.i18nService.t(linkedFieldOption.i18nKey),
|
||||||
value: id,
|
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 */
|
/** Create `FieldView` from the form objects and update the cipher */
|
||||||
private updateCipher(fields: CustomField[]) {
|
private updateCipher(fields: CustomField[]) {
|
||||||
const newFields = fields.map((field: CustomField) => {
|
const newFields = fields.map((field: CustomField) => {
|
||||||
|
|
Loading…
Reference in New Issue