1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 05:30:01 +00:00

added testing suite & all expected FFs

This commit is contained in:
Isaac Ivins
2026-01-23 10:52:57 -05:00
parent f84f35be6a
commit 8e6f2c8545
4 changed files with 133 additions and 16 deletions

View File

@@ -6,7 +6,7 @@
<bit-callout type="warning" title="{{ 'warning' | i18n }}">
{{ "deleteAccountWarning" | i18n }}
</bit-callout>
@if (migrationMilestone3) {
@if (migrationMilestone4) {
<app-user-verification-form-input
formControlName="verification"
verificationType="server"

View File

@@ -0,0 +1,121 @@
import { FormBuilder } from "@angular/forms";
import { MockProxy, mock } from "jest-mock-extended";
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
import { VerificationWithSecret } from "@bitwarden/common/auth/types/verification";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ToastService } from "@bitwarden/components";
import { DeleteAccountComponent } from "./delete-account.component";
describe("DeleteAccountComponent", () => {
let component: DeleteAccountComponent;
let i18nService: MockProxy<I18nService>;
let formBuilder: FormBuilder;
let accountApiService: MockProxy<AccountApiService>;
let toastService: MockProxy<ToastService>;
let configService: MockProxy<ConfigService>;
beforeEach(() => {
jest.clearAllMocks();
i18nService = mock<I18nService>();
formBuilder = new FormBuilder();
accountApiService = mock<AccountApiService>();
toastService = mock<ToastService>();
configService = mock<ConfigService>();
i18nService.t.mockImplementation((key: any) => key);
component = new DeleteAccountComponent(
i18nService,
formBuilder,
accountApiService,
toastService,
configService,
);
});
describe("submit", () => {
const mockVerification: VerificationWithSecret = {
type: 0,
secret: "masterPassword123",
};
beforeEach(() => {
component.deleteForm.patchValue({
verification: mockVerification,
});
});
describe("when feature flag is enabled", () => {
beforeEach(() => {
component["migrationMilestone4"] = true;
});
it("should delete account and show success toast on successful deletion", async () => {
accountApiService.deleteAccount.mockResolvedValue(undefined);
await component.submit();
expect(accountApiService.deleteAccount).toHaveBeenCalledWith(mockVerification);
expect(toastService.showToast).toHaveBeenCalledWith({
variant: "success",
title: "accountDeleted",
message: "accountDeletedDesc",
});
expect(component["invalidSecret"]).toBe(false);
});
it("should set invalidSecret to true when deletion fails", async () => {
accountApiService.deleteAccount.mockRejectedValue(new Error("Invalid credentials"));
await component.submit();
expect(accountApiService.deleteAccount).toHaveBeenCalledWith(mockVerification);
expect(toastService.showToast).not.toHaveBeenCalled();
expect(component["invalidSecret"]).toBe(true);
});
it("should reset invalidSecret to false before attempting deletion", async () => {
component["invalidSecret"] = true;
accountApiService.deleteAccount.mockResolvedValue(undefined);
await component.submit();
expect(component["invalidSecret"]).toBe(false);
});
});
describe("when feature flag is disabled", () => {
beforeEach(() => {
component["migrationMilestone4"] = false;
});
it("should delete account and show success toast on successful deletion", async () => {
accountApiService.deleteAccount.mockResolvedValue(undefined);
await component.submit();
expect(accountApiService.deleteAccount).toHaveBeenCalledWith(mockVerification);
expect(toastService.showToast).toHaveBeenCalledWith({
variant: "success",
title: "accountDeleted",
message: "accountDeletedDesc",
});
});
it("should not set invalidSecret when deletion fails", async () => {
const initialInvalidSecret = component["invalidSecret"];
accountApiService.deleteAccount.mockRejectedValue(new Error("Invalid credentials"));
await component.submit();
expect(accountApiService.deleteAccount).toHaveBeenCalledWith(mockVerification);
expect(toastService.showToast).not.toHaveBeenCalled();
expect(component["invalidSecret"]).toBe(initialInvalidSecret);
});
});
});
});

View File

@@ -50,9 +50,9 @@ export class DeleteAccountComponent implements OnInit {
protected invalidSecret = false;
/**
* Feature flag for UI Migration Milestone 3
* Feature flag for UI Migration Milestone 4
*/
protected migrationMilestone3 = false;
protected migrationMilestone4 = false;
constructor(
private i18nService: I18nService,
@@ -63,8 +63,8 @@ export class DeleteAccountComponent implements OnInit {
) {}
async ngOnInit() {
this.migrationMilestone3 = await this.configService.getFeatureFlag(
FeatureFlag.DesktopUiMigrationMilestone3,
this.migrationMilestone4 = await this.configService.getFeatureFlag(
FeatureFlag.DesktopUiMigrationMilestone4,
);
}
@@ -78,8 +78,8 @@ export class DeleteAccountComponent implements OnInit {
submit = async () => {
try {
if (this.migrationMilestone3) {
this.invalidSecret = false; // Reset error state before attempting
if (this.migrationMilestone4) {
this.invalidSecret = false;
}
const verification = this.deleteForm.get("verification").value;
await this.accountApiService.deleteAccount(verification);
@@ -89,7 +89,7 @@ export class DeleteAccountComponent implements OnInit {
message: this.i18nService.t("accountDeletedDesc"),
});
} catch {
if (this.migrationMilestone3) {
if (this.migrationMilestone4) {
this.invalidSecret = true;
}
}

View File

@@ -77,8 +77,9 @@ export enum FeatureFlag {
/* Desktop */
DesktopUiMigrationMilestone1 = "desktop-ui-migration-milestone-1",
DesktopUiMigrationMilestone3 = "desktop-ui-migration-milestone-3",
DesktopUiMigrationMilestone2 = "desktop-ui-migration-milestone-2",
DesktopUiMigrationMilestone3 = "desktop-ui-migration-milestone-3",
DesktopUiMigrationMilestone4 = "desktop-ui-migration-milestone-4",
/* UIF */
RouterFocusManagement = "router-focus-management",
@@ -168,8 +169,9 @@ export const DefaultFeatureFlagValue = {
/* Desktop */
[FeatureFlag.DesktopUiMigrationMilestone1]: FALSE,
[FeatureFlag.DesktopUiMigrationMilestone3]: FALSE,
[FeatureFlag.DesktopUiMigrationMilestone2]: FALSE,
[FeatureFlag.DesktopUiMigrationMilestone3]: FALSE,
[FeatureFlag.DesktopUiMigrationMilestone4]: FALSE,
/* UIF */
[FeatureFlag.RouterFocusManagement]: FALSE,
@@ -186,12 +188,6 @@ export function getFeatureFlagValue<Flag extends FeatureFlag>(
serverConfig: ServerConfig | null,
flag: Flag,
) {
if (flag === FeatureFlag.DesktopUiMigrationMilestone1) {
return true;
}
if (flag === FeatureFlag.DesktopUiMigrationMilestone3) {
return true;
}
if (serverConfig?.featureStates == null || serverConfig.featureStates[flag] == null) {
return DefaultFeatureFlagValue[flag];
}