diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index 938cc7d8e7c..1c4519f4307 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -228,7 +228,10 @@ export class AddEditV2Component implements OnInit { return; } - this.location.back(); + await this.router.navigate(["/view-cipher"], { + replaceUrl: true, + queryParams: { cipherId: cipher.id }, + }); } subscribeToParams(): void { diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts new file mode 100644 index 00000000000..b7f3aa2f430 --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts @@ -0,0 +1,107 @@ +import { ComponentFixture, fakeAsync, flush, TestBed } from "@angular/core/testing"; +import { ActivatedRoute, Router } from "@angular/router"; +import { mock } from "jest-mock-extended"; +import { Subject } from "rxjs"; + +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherType } from "@bitwarden/common/vault/enums"; + +import { ViewV2Component } from "./view-v2.component"; + +// 'qrcode-parser' is used by `BrowserTotpCaptureService` but is an es6 module that jest can't compile. +// Mock the entire module here to prevent jest from throwing an error. I wasn't able to find a way to mock the +// `BrowserTotpCaptureService` where jest would not load the file in the first place. +jest.mock("qrcode-parser", () => {}); + +describe("ViewV2Component", () => { + let component: ViewV2Component; + let fixture: ComponentFixture; + const params$ = new Subject(); + const mockNavigate = jest.fn(); + + const mockCipher = { + id: "122-333-444", + type: CipherType.Login, + }; + + const mockCipherService = { + get: jest.fn().mockResolvedValue({ decrypt: jest.fn().mockResolvedValue(mockCipher) }), + getKeyForCipherKeyDecryption: jest.fn().mockResolvedValue({}), + }; + + beforeEach(async () => { + mockNavigate.mockClear(); + + await TestBed.configureTestingModule({ + imports: [ViewV2Component], + providers: [ + { provide: Router, useValue: { navigate: mockNavigate } }, + { provide: CipherService, useValue: mockCipherService }, + { provide: LogService, useValue: mock() }, + { provide: PlatformUtilsService, useValue: mock() }, + { provide: ConfigService, useValue: mock() }, + { provide: ActivatedRoute, useValue: { queryParams: params$ } }, + { + provide: I18nService, + useValue: { + t: (key: string, ...rest: string[]) => { + if (rest?.length) { + return `${key} ${rest.join(" ")}`; + } + return key; + }, + }, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ViewV2Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + describe("queryParams", () => { + it("loads an existing cipher", fakeAsync(() => { + params$.next({ cipherId: "122-333-444" }); + + flush(); // Resolve all promises + + expect(mockCipherService.get).toHaveBeenCalledWith("122-333-444"); + expect(component.cipher).toEqual(mockCipher); + })); + + it("sets the correct header text", fakeAsync(() => { + // Set header text for a login + mockCipher.type = CipherType.Login; + params$.next({ cipherId: mockCipher.id }); + flush(); // Resolve all promises + + expect(component.headerText).toEqual("viewItemHeader typelogin"); + + // Set header text for a card + mockCipher.type = CipherType.Card; + params$.next({ cipherId: mockCipher.id }); + flush(); // Resolve all promises + + expect(component.headerText).toEqual("viewItemHeader typecard"); + + // Set header text for an identity + mockCipher.type = CipherType.Identity; + params$.next({ cipherId: mockCipher.id }); + flush(); // Resolve all promises + + expect(component.headerText).toEqual("viewItemHeader typeidentity"); + + // Set header text for a secure note + mockCipher.type = CipherType.SecureNote; + params$.next({ cipherId: mockCipher.id }); + flush(); // Resolve all promises + + expect(component.headerText).toEqual("viewItemHeader note"); + })); + }); +}); diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts index 8039ac18651..ccc2658e59e 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts @@ -54,7 +54,6 @@ import { PopupPageComponent } from "./../../../../../platform/popup/layout/popup }) export class ViewV2Component { headerText: string; - cipherId: string; cipher: CipherView; organization$: Observable; folder$: Observable; @@ -75,14 +74,14 @@ export class ViewV2Component { subscribeToParams(): void { this.route.queryParams .pipe( - switchMap((param) => { - return this.getCipherData(param.cipherId); + switchMap(async (params): Promise => { + return await this.getCipherData(params.cipherId); }), takeUntilDestroyed(), ) - .subscribe((data) => { - this.cipher = data; - this.headerText = this.setHeader(data.type); + .subscribe((cipher) => { + this.cipher = cipher; + this.headerText = this.setHeader(cipher.type); }); }