mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-08 19:18:02 +01:00
Clean up and flush out register form tests.
This commit is contained in:
parent
28aae75abf
commit
2f29903136
@ -1,9 +1,11 @@
|
|||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { ReactiveFormsModule } from "@angular/forms";
|
import { ReactiveFormsModule } from "@angular/forms";
|
||||||
import { RouterTestingModule } from "@angular/router/testing";
|
import { RouterTestingModule } from "@angular/router/testing";
|
||||||
|
import { mock } from "jest-mock-extended";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
|
|
||||||
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
||||||
|
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/auth/components/register.component";
|
||||||
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
|
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
|
||||||
import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common";
|
import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@ -11,8 +13,10 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
|||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
|
import { RegisterRequest } from "@bitwarden/common/models/request/register.request";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
@ -23,59 +27,72 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv
|
|||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state";
|
import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state";
|
||||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { ToastService } from "@bitwarden/components";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||||
|
|
||||||
import { SharedModule } from "../../shared";
|
import { SharedModule } from "../../shared";
|
||||||
import { AcceptOrganizationInviteService } from "../organization-invite/accept-organization.service";
|
import { AcceptOrganizationInviteService } from "../organization-invite/accept-organization.service";
|
||||||
|
import { OrganizationInvite } from "../organization-invite/organization-invite";
|
||||||
|
|
||||||
import { RegisterFormComponent } from "./register-form.component";
|
import { RegisterFormComponent } from "./register-form.component";
|
||||||
|
|
||||||
// Mock I18nService
|
|
||||||
class MockI18nService {
|
|
||||||
t(key: string): string {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock PlatformUtilsService
|
|
||||||
class MockPlatformUtilsService {
|
|
||||||
isSelfHost(): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock ValidationService
|
|
||||||
class MockValidationService {}
|
|
||||||
|
|
||||||
// Mock PasswordStrengthServiceAbstraction
|
|
||||||
class MockPasswordStrengthServiceAbstraction {
|
|
||||||
getPasswordStrength(password: string, email: string, name: string[]): any {
|
|
||||||
return { score: 3 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("RegisterFormComponent", () => {
|
describe("RegisterFormComponent", () => {
|
||||||
let component: RegisterFormComponent;
|
let component: RegisterFormComponent;
|
||||||
let fixture: ComponentFixture<RegisterFormComponent>;
|
let fixture: ComponentFixture<RegisterFormComponent>;
|
||||||
|
let policyServiceMock: jest.Mocked<PolicyService>;
|
||||||
|
let toastServiceMock: jest.Mocked<ToastService>;
|
||||||
|
let acceptOrgInviteServiceMock: jest.Mocked<AcceptOrganizationInviteService>;
|
||||||
|
let logServiceMock: jest.Mocked<LogService>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const authServiceMock = {
|
const authServiceMock = mock<AuthService>({ authStatuses$: of({}) });
|
||||||
authStatuses$: of([]),
|
const encryptServiceMock = mock<EncryptService>();
|
||||||
};
|
const policyApiServiceMock = mock<PolicyApiServiceAbstraction>();
|
||||||
const encryptServiceMock = {};
|
const organizationApiServiceMock = mock<OrganizationApiServiceAbstraction>();
|
||||||
const policyApiServiceMock = {};
|
const organizationUserApiServiceMock = mock<OrganizationUserApiService>();
|
||||||
const organizationApiServiceMock = {};
|
const globalStateProviderMock = mock<GlobalStateProvider>({
|
||||||
const organizationUserApiServiceMock = {};
|
|
||||||
const globalStateProviderMock = {
|
|
||||||
get: jest.fn().mockReturnValue(of({})),
|
get: jest.fn().mockReturnValue(of({})),
|
||||||
};
|
});
|
||||||
const accountServiceMock = {};
|
const accountServiceMock = mock<AccountService>();
|
||||||
const stateProviderMock = {
|
const stateProviderMock = mock<StateProvider>({
|
||||||
getGlobal: jest.fn().mockReturnValue({
|
getGlobal: jest.fn().mockReturnValue({ state$: of({}) }),
|
||||||
state$: of({}),
|
});
|
||||||
}),
|
const i18nServiceMock = mock<I18nService>();
|
||||||
};
|
i18nServiceMock.t.mockImplementation((key: string) => key);
|
||||||
|
const platformUtilsServiceMock = mock<PlatformUtilsService>();
|
||||||
|
platformUtilsServiceMock.isSelfHost.mockReturnValue(false);
|
||||||
|
const validationServiceMock = mock<ValidationService>();
|
||||||
|
const passwordStrengthServiceMock = mock<PasswordStrengthServiceAbstraction>();
|
||||||
|
passwordStrengthServiceMock.getPasswordStrength.mockReturnValue({
|
||||||
|
score: 3,
|
||||||
|
guesses: 1000,
|
||||||
|
guesses_log10: 3,
|
||||||
|
crack_times_seconds: {
|
||||||
|
online_throttling_100_per_hour: 36000,
|
||||||
|
online_no_throttling_10_per_second: 100,
|
||||||
|
offline_slow_hashing_1e4_per_second: 0.1,
|
||||||
|
offline_fast_hashing_1e10_per_second: 0.0001,
|
||||||
|
},
|
||||||
|
crack_times_display: {
|
||||||
|
online_throttling_100_per_hour: "10 hours",
|
||||||
|
online_no_throttling_10_per_second: "1 minute",
|
||||||
|
offline_slow_hashing_1e4_per_second: "0.1 seconds",
|
||||||
|
offline_fast_hashing_1e10_per_second: "less than a second",
|
||||||
|
},
|
||||||
|
feedback: { warning: "", suggestions: [] },
|
||||||
|
calc_time: 100,
|
||||||
|
sequence: [],
|
||||||
|
});
|
||||||
|
const toastServiceMockTemp = mock<ToastService>();
|
||||||
|
logServiceMock = mock<LogService>();
|
||||||
|
const cryptoServiceMock = mock<CryptoService>({
|
||||||
|
makeMasterKey: jest.fn().mockResolvedValue({} as any),
|
||||||
|
});
|
||||||
|
policyServiceMock = mock<PolicyService>();
|
||||||
|
toastServiceMock = mock<ToastService>();
|
||||||
|
acceptOrgInviteServiceMock = mock<AcceptOrganizationInviteService>();
|
||||||
|
logServiceMock = mock<LogService>();
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ReactiveFormsModule, RouterTestingModule, SharedModule],
|
imports: [ReactiveFormsModule, RouterTestingModule, SharedModule],
|
||||||
@ -89,26 +106,26 @@ describe("RegisterFormComponent", () => {
|
|||||||
{ provide: GlobalStateProvider, useValue: globalStateProviderMock },
|
{ provide: GlobalStateProvider, useValue: globalStateProviderMock },
|
||||||
{ provide: AccountService, useValue: accountServiceMock },
|
{ provide: AccountService, useValue: accountServiceMock },
|
||||||
{ provide: StateProvider, useValue: stateProviderMock },
|
{ provide: StateProvider, useValue: stateProviderMock },
|
||||||
{ provide: I18nService, useClass: MockI18nService },
|
{ provide: I18nService, useValue: i18nServiceMock },
|
||||||
{ provide: PlatformUtilsService, useClass: MockPlatformUtilsService },
|
{ provide: PlatformUtilsService, useValue: platformUtilsServiceMock },
|
||||||
{ provide: ValidationService, useClass: MockValidationService },
|
{ provide: ValidationService, useValue: validationServiceMock },
|
||||||
{
|
{ provide: PasswordStrengthServiceAbstraction, useValue: passwordStrengthServiceMock },
|
||||||
provide: PasswordStrengthServiceAbstraction,
|
{ provide: ToastService, useValue: toastServiceMockTemp },
|
||||||
useClass: MockPasswordStrengthServiceAbstraction,
|
{ provide: LogService, useValue: logServiceMock },
|
||||||
},
|
{ provide: CryptoService, useValue: cryptoServiceMock },
|
||||||
FormValidationErrorsService,
|
FormValidationErrorsService,
|
||||||
LoginStrategyServiceAbstraction,
|
LoginStrategyServiceAbstraction,
|
||||||
ApiService,
|
ApiService,
|
||||||
AuditService,
|
AuditService,
|
||||||
PolicyService,
|
PolicyService,
|
||||||
CryptoService,
|
|
||||||
EnvironmentService,
|
EnvironmentService,
|
||||||
LogService,
|
|
||||||
StateService,
|
StateService,
|
||||||
DialogService,
|
|
||||||
ToastService,
|
|
||||||
PasswordGenerationServiceAbstraction,
|
PasswordGenerationServiceAbstraction,
|
||||||
AcceptOrganizationInviteService,
|
AcceptOrganizationInviteService,
|
||||||
|
{ provide: PolicyService, useValue: policyServiceMock },
|
||||||
|
{ provide: ToastService, useValue: toastServiceMock },
|
||||||
|
{ provide: AcceptOrganizationInviteService, useValue: acceptOrgInviteServiceMock },
|
||||||
|
{ provide: LogService, useValue: logServiceMock },
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
@ -116,7 +133,131 @@ describe("RegisterFormComponent", () => {
|
|||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("creates without error", () => {
|
it("creates component", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("ngOnInit", () => {
|
||||||
|
it("sets email from queryParamEmail", async () => {
|
||||||
|
component.queryParamEmail = "test@bitwarden.com";
|
||||||
|
await component.ngOnInit();
|
||||||
|
expect(component.formGroup.get("email").value).toBe("test@bitwarden.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets characterMinimumMessage based on enforcedPolicyOptions", async () => {
|
||||||
|
const testCases: Array<{
|
||||||
|
enforcedPolicyOptions: MasterPasswordPolicyOptions | null;
|
||||||
|
minimumLength?: number;
|
||||||
|
expected: string;
|
||||||
|
}> = [
|
||||||
|
{ enforcedPolicyOptions: null, minimumLength: 8, expected: "characterMinimum" },
|
||||||
|
{ enforcedPolicyOptions: { minLength: 10 } as MasterPasswordPolicyOptions, expected: "" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { enforcedPolicyOptions, minimumLength, expected } of testCases) {
|
||||||
|
component.enforcedPolicyOptions = enforcedPolicyOptions;
|
||||||
|
component.minimumLength = minimumLength;
|
||||||
|
await component.ngOnInit();
|
||||||
|
expect(component.characterMinimumMessage).toBe(expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("submit", () => {
|
||||||
|
it("shows error toast when password policy is not met", async () => {
|
||||||
|
setupPasswordPolicyTest(false, 2, "weak");
|
||||||
|
await component.submit();
|
||||||
|
expect(toastServiceMock.showToast).toHaveBeenCalledWith({
|
||||||
|
variant: "error",
|
||||||
|
title: "errorOccurred",
|
||||||
|
message: "masterPasswordPolicyRequirementsNotMet",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls super.submit when password policy is met", async () => {
|
||||||
|
const submitSpy = setupPasswordPolicyTest(true, 4, "superStrongPassword123!");
|
||||||
|
await component.submit();
|
||||||
|
expect(submitSpy).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("modifyRegisterRequest", () => {
|
||||||
|
it("adds organization invite details to register request when invite exists", async () => {
|
||||||
|
const request = {
|
||||||
|
email: "test@bitwarden.com",
|
||||||
|
masterPasswordHash: "hashedPassword",
|
||||||
|
masterPasswordHint: "hint",
|
||||||
|
key: "encryptedKey",
|
||||||
|
kdf: 0,
|
||||||
|
kdfIterations: 100000,
|
||||||
|
referenceData: null,
|
||||||
|
captchaResponse: null,
|
||||||
|
keys: {
|
||||||
|
publicKey: "mockPublicKey",
|
||||||
|
encryptedPrivateKey: "mockEncryptedPrivateKey",
|
||||||
|
},
|
||||||
|
token: null,
|
||||||
|
organizationUserId: null,
|
||||||
|
name: null,
|
||||||
|
} as RegisterRequest;
|
||||||
|
const orgInvite: OrganizationInvite = {
|
||||||
|
organizationUserId: "123",
|
||||||
|
token: "abc123",
|
||||||
|
email: "test@bitwarden.com",
|
||||||
|
initOrganization: false,
|
||||||
|
orgSsoIdentifier: null,
|
||||||
|
orgUserHasExistingUser: false,
|
||||||
|
organizationId: "456",
|
||||||
|
organizationName: "Test Org",
|
||||||
|
};
|
||||||
|
acceptOrgInviteServiceMock.getOrganizationInvite.mockResolvedValue(orgInvite);
|
||||||
|
|
||||||
|
await component["modifyRegisterRequest"](request);
|
||||||
|
|
||||||
|
expect(request.token).toBe(orgInvite.token);
|
||||||
|
expect(request.organizationUserId).toBe(orgInvite.organizationUserId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not modify request when no organization invite exists", async () => {
|
||||||
|
const request = {
|
||||||
|
email: "test@bitwarden.com",
|
||||||
|
masterPasswordHash: "hashedPassword",
|
||||||
|
masterPasswordHint: "hint",
|
||||||
|
key: "encryptedKey",
|
||||||
|
kdf: 0,
|
||||||
|
kdfIterations: 100000,
|
||||||
|
referenceData: null,
|
||||||
|
captchaResponse: null,
|
||||||
|
keys: {
|
||||||
|
publicKey: "mockPublicKey",
|
||||||
|
encryptedPrivateKey: "mockEncryptedPrivateKey",
|
||||||
|
},
|
||||||
|
token: null,
|
||||||
|
organizationUserId: null,
|
||||||
|
name: null,
|
||||||
|
} as RegisterRequest;
|
||||||
|
acceptOrgInviteServiceMock.getOrganizationInvite.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await component["modifyRegisterRequest"](request);
|
||||||
|
|
||||||
|
expect(request.token).toBeNull();
|
||||||
|
expect(request.organizationUserId).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the password policy test.
|
||||||
|
*
|
||||||
|
* @param policyMet - Whether the password policy is met.
|
||||||
|
* @param passwordScore - The score of the password.
|
||||||
|
* @param password - The password to set.
|
||||||
|
* @returns The spy on the submit method.
|
||||||
|
*/
|
||||||
|
function setupPasswordPolicyTest(policyMet: boolean, passwordScore: number, password: string) {
|
||||||
|
policyServiceMock.evaluateMasterPassword.mockReturnValue(policyMet);
|
||||||
|
component.enforcedPolicyOptions = { minLength: 10 } as MasterPasswordPolicyOptions;
|
||||||
|
component.passwordStrengthResult = { score: passwordScore };
|
||||||
|
component.formGroup.patchValue({ masterPassword: password });
|
||||||
|
return jest.spyOn(BaseRegisterComponent.prototype, "submit");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -78,7 +78,6 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
|
|
||||||
protected captchaBypassToken: string = null;
|
protected captchaBypassToken: string = null;
|
||||||
|
|
||||||
// allows for extending classes to modify the register request before sending
|
|
||||||
// allows for extending classes to modify the register request before sending
|
// allows for extending classes to modify the register request before sending
|
||||||
// currently used by web to add organization invitation details
|
// currently used by web to add organization invitation details
|
||||||
protected modifyRegisterRequest: (request: RegisterRequest) => Promise<void>;
|
protected modifyRegisterRequest: (request: RegisterRequest) => Promise<void>;
|
||||||
|
Loading…
Reference in New Issue
Block a user