1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 09:13:33 +00:00

[PM-9111] Extension: persist add/edit form (#12236)

* remove todo

* Retrieve cache cipher for add-edit form

* user prefilled cipher for add-edit form

* add listener for clearing view cache

* clear local cache when clearing global state

* track initial value of cache for down stream logic that should only occur on non-cached values

* add feature flag for edit form persistence

* add tests for cipher form cache service

* fix optional initialValues

* add services to cipher form storybook

* fix strict types

* rename variables to be platform agnostic

* use deconstructed collectionIds variable to avoid them be overwritten

* use the originalCipherView for initial values

* add comment about signal equality

* prevent events from being emitted when adding uris to the existing form

- This stops other values from being overwrote in the initialization process

* add check for cached cipher when adding initial uris
This commit is contained in:
Nick Krantz
2025-01-22 10:49:07 -06:00
committed by GitHub
parent 1dfae06856
commit 5c32e5020d
22 changed files with 460 additions and 139 deletions

View File

@@ -0,0 +1,97 @@
import { signal } from "@angular/core";
import { TestBed } from "@angular/core/testing";
import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CipherFormCacheService } from "./default-cipher-form-cache.service";
describe("CipherFormCacheService", () => {
let service: CipherFormCacheService;
let testBed: TestBed;
const cacheSignal = signal<CipherView | null>(null);
const getCacheSignal = jest.fn().mockReturnValue(cacheSignal);
const getFeatureFlag = jest.fn().mockResolvedValue(false);
const cacheSetMock = jest.spyOn(cacheSignal, "set");
beforeEach(() => {
getCacheSignal.mockClear();
getFeatureFlag.mockClear();
cacheSetMock.mockClear();
testBed = TestBed.configureTestingModule({
providers: [
{ provide: ViewCacheService, useValue: { signal: getCacheSignal } },
{ provide: ConfigService, useValue: { getFeatureFlag } },
CipherFormCacheService,
],
});
});
describe("feature enabled", () => {
beforeEach(async () => {
getFeatureFlag.mockResolvedValue(true);
});
it("`getCachedCipherView` returns the cipher", async () => {
cacheSignal.set({ id: "cipher-4" } as CipherView);
service = testBed.inject(CipherFormCacheService);
await service.init();
expect(service.getCachedCipherView()).toEqual({ id: "cipher-4" });
});
it("updates the signal value", async () => {
service = testBed.inject(CipherFormCacheService);
await service.init();
service.cacheCipherView({ id: "cipher-5" } as CipherView);
expect(cacheSignal.set).toHaveBeenCalledWith({ id: "cipher-5" });
});
describe("initializedWithValue", () => {
it("sets `initializedWithValue` to true when there is a cached cipher", async () => {
cacheSignal.set({ id: "cipher-3" } as CipherView);
service = testBed.inject(CipherFormCacheService);
await service.init();
expect(service.initializedWithValue).toBe(true);
});
it("sets `initializedWithValue` to false when there is not a cached cipher", async () => {
cacheSignal.set(null);
service = testBed.inject(CipherFormCacheService);
await service.init();
expect(service.initializedWithValue).toBe(false);
});
});
});
describe("featured disabled", () => {
beforeEach(async () => {
cacheSignal.set({ id: "cipher-1" } as CipherView);
getFeatureFlag.mockResolvedValue(false);
cacheSetMock.mockClear();
service = testBed.inject(CipherFormCacheService);
await service.init();
});
it("sets `initializedWithValue` to false", () => {
expect(service.initializedWithValue).toBe(false);
});
it("`getCachedCipherView` returns null", () => {
expect(service.getCachedCipherView()).toBeNull();
});
it("does not update the signal value", () => {
service.cacheCipherView({ id: "cipher-2" } as CipherView);
expect(cacheSignal.set).not.toHaveBeenCalled();
});
});
});