1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-31 08:43:54 +00:00

[pm-8458] Register migration; draft tests

Signed-off-by: Ben Brooks <bbrooks@bitwarden.com>
This commit is contained in:
Ben Brooks
2025-12-03 09:38:05 -08:00
parent 0f892c70b3
commit 31d74379fa
2 changed files with 159 additions and 2 deletions

View File

@@ -70,12 +70,13 @@ import { RemoveAcBannersDismissed } from "./migrations/70-remove-ac-banner-dismi
import { RemoveNewCustomizationOptionsCalloutDismissed } from "./migrations/71-remove-new-customization-options-callout-dismissed";
import { RemoveAccountDeprovisioningBannerDismissed } from "./migrations/72-remove-account-deprovisioning-banner-dismissed";
import { AddMasterPasswordUnlockData } from "./migrations/73-add-master-password-unlock-data";
import { ClearClipboardDelayToStringMigrator } from "./migrations/74-clear-clipboard-delay-to-string";
import { MoveStateVersionMigrator } from "./migrations/8-move-state-version";
import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-settings-to-global";
import { MinVersionMigrator } from "./migrations/min-version";
export const MIN_VERSION = 3;
export const CURRENT_VERSION = 73;
export const CURRENT_VERSION = 74;
export type MinVersion = typeof MIN_VERSION;
export function createMigrationBuilder() {
@@ -150,7 +151,8 @@ export function createMigrationBuilder() {
.with(RemoveAcBannersDismissed, 69, 70)
.with(RemoveNewCustomizationOptionsCalloutDismissed, 70, 71)
.with(RemoveAccountDeprovisioningBannerDismissed, 71, 72)
.with(AddMasterPasswordUnlockData, 72, CURRENT_VERSION);
.with(AddMasterPasswordUnlockData, 72, 73)
.with(ClearClipboardDelayToStringMigrator, 73, CURRENT_VERSION);
}
export async function currentVersion(

View File

@@ -0,0 +1,155 @@
import { MockProxy } from "jest-mock-extended";
import { MigrationHelper } from "../migration-helper";
import { mockMigrationHelper } from "../migration-helper.spec";
import { ClearClipboardDelayToStringMigrator } from "./74-clear-clipboard-delay-to-string";
describe("ClearClipboardDelayToStringMigrator", () => {
let helper: MockProxy<MigrationHelper>;
let sut: ClearClipboardDelayToStringMigrator;
describe("migrate", () => {
beforeEach(() => {
helper = mockMigrationHelper({}, 73);
sut = new ClearClipboardDelayToStringMigrator(73, 74);
});
it("should call getAccounts", async () => {
await sut.migrate(helper);
// This should always be called in any migration
expect(helper.getAccounts).toHaveBeenCalled();
});
it("should handle empty accounts gracefully", async () => {
// Test with no accounts
helper.getAccounts.mockResolvedValue([]);
await expect(sut.migrate(helper)).resolves.not.toThrow();
});
it("should handle accounts with no clearClipboard settings", async () => {
// Test with accounts but no clearClipboard settings
helper.getAccounts.mockResolvedValue([
{ userId: "user-1", account: {} },
{ userId: "user-2", account: {} },
]);
await expect(sut.migrate(helper)).resolves.not.toThrow();
// Should still call getFromUser for each user
expect(helper.getFromUser).toHaveBeenCalledWith(
"user-1",
expect.objectContaining({
key: "clearClipboardDelay",
stateDefinition: expect.objectContaining({
name: "autofillSettingsLocal",
}),
}),
);
expect(helper.getFromUser).toHaveBeenCalledWith(
"user-2",
expect.objectContaining({
key: "clearClipboardDelay",
stateDefinition: expect.objectContaining({
name: "autofillSettingsLocal",
}),
}),
);
});
it("should migrate a single user with an integer value", async () => {
// Mock getAccounts to return one user
helper.getAccounts.mockResolvedValue([{ userId: "user-1", account: {} }]);
// Mock getFromUser to return an integer value
helper.getFromUser.mockResolvedValue(10);
await sut.migrate(helper);
expect(helper.getFromUser).toHaveBeenCalledWith(
"user-1",
expect.objectContaining({
key: "clearClipboardDelay",
stateDefinition: expect.objectContaining({
name: "autofillSettingsLocal",
}),
}),
);
expect(helper.setToUser).toHaveBeenCalledWith(
"user-1",
expect.objectContaining({
key: "clearClipboardDelay",
stateDefinition: expect.objectContaining({
name: "autofillSettingsLocal",
}),
}),
"tenSeconds",
);
});
it("should migrate null to fiveMinutes", async () => {
helper.getAccounts.mockResolvedValue([{ userId: "user-1", account: {} }]);
helper.getFromUser.mockResolvedValue(null);
await sut.migrate(helper);
expect(helper.setToUser).toHaveBeenCalledWith(
"user-1",
expect.objectContaining({
key: "clearClipboardDelay",
stateDefinition: expect.objectContaining({
name: "autofillSettingsLocal",
}),
}),
"fiveMinutes",
);
});
it("should not migrate undefined values", async () => {
helper.getAccounts.mockResolvedValue([{ userId: "user-1", account: {} }]);
helper.getFromUser.mockResolvedValue(undefined);
await sut.migrate(helper);
expect(helper.setToUser).not.toHaveBeenCalled();
});
});
describe("rollback", () => {
beforeEach(() => {
helper = mockMigrationHelper({}, 74);
sut = new ClearClipboardDelayToStringMigrator(73, 74);
});
it("should call getAccounts", async () => {
await sut.rollback(helper);
expect(helper.getAccounts).toHaveBeenCalled();
});
it("should rollback a string value to integer", async () => {
helper.getAccounts.mockResolvedValue([{ userId: "user-1", account: {} }]);
helper.getFromUser.mockResolvedValue("tenSeconds");
await sut.rollback(helper);
expect(helper.setToUser).toHaveBeenCalledWith(
"user-1",
expect.objectContaining({
key: "clearClipboardDelay",
stateDefinition: expect.objectContaining({
name: "autofillSettingsLocal",
}),
}),
10,
);
});
});
});