[PM-10700] Navigate to View after cipher creation (#10484)

* refactor params subscription variable names

* refactor param subscription to return a tuple

- this is going to be helpful when multiple params are involved

* navigate the user back to the vault when a new cipher is created

* add unit tests for view-v2 component

* prefer replaceUrl to avoid having to pass a query param

* Fix grammar of mocking comment
This commit is contained in:
Nick Krantz 2024-08-13 07:51:43 -05:00 committed by GitHub
parent 7b508b1ad7
commit baf919a4e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 116 additions and 7 deletions

View File

@ -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 {

View File

@ -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<ViewV2Component>;
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<LogService>() },
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
{ provide: ConfigService, useValue: mock<ConfigService>() },
{ 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");
}));
});
});

View File

@ -54,7 +54,6 @@ import { PopupPageComponent } from "./../../../../../platform/popup/layout/popup
})
export class ViewV2Component {
headerText: string;
cipherId: string;
cipher: CipherView;
organization$: Observable<Organization>;
folder$: Observable<FolderView>;
@ -75,14 +74,14 @@ export class ViewV2Component {
subscribeToParams(): void {
this.route.queryParams
.pipe(
switchMap((param) => {
return this.getCipherData(param.cipherId);
switchMap(async (params): Promise<CipherView> => {
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);
});
}