mirror of
https://github.com/bitwarden/browser
synced 2026-01-03 17:13:47 +00:00
feat(accounts): Add creationDate of account to AccountInfo
* Add creationDate of account to AccountInfo * Added initialization of creationDate. * Removed extra changes. * Fixed tests to initialize creation date * Added helper method to abstract account initialization in tests. * More test updates. * Linting * Additional test fixes. * Fixed spec reference * Fixed imports * Linting. * Fixed browser test. * Modified tsconfig to reference spec file. * Fixed import. * Removed dependency on os. This is necessary so that the @bitwarden/common/spec lib package can be referenced in tests without node. * Revert "Removed dependency on os. This is necessary so that the @bitwarden/common/spec lib package can be referenced in tests without node." This reverts commit669f6557b6. * Updated stories to hard-code new field. * Removed changes to tsconfig * Revert "Removed changes to tsconfig" This reverts commitb7d916e8dc.
This commit is contained in:
@@ -2,14 +2,11 @@ import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../types/guid";
|
||||
|
||||
/**
|
||||
* Holds information about an account for use in the AccountService
|
||||
* if more information is added, be sure to update the equality method.
|
||||
*/
|
||||
export type AccountInfo = {
|
||||
email: string;
|
||||
emailVerified: boolean;
|
||||
name: string | undefined;
|
||||
creationDate: string | undefined;
|
||||
};
|
||||
|
||||
export type Account = { id: UserId } & AccountInfo;
|
||||
@@ -75,6 +72,12 @@ export abstract class AccountService {
|
||||
* @param emailVerified
|
||||
*/
|
||||
abstract setAccountEmailVerified(userId: UserId, emailVerified: boolean): Promise<void>;
|
||||
/**
|
||||
* updates the `accounts$` observable with the creation date for the account.
|
||||
* @param userId
|
||||
* @param creationDate
|
||||
*/
|
||||
abstract setAccountCreationDate(userId: UserId, creationDate: string): Promise<void>;
|
||||
/**
|
||||
* updates the `accounts$` observable with the new VerifyNewDeviceLogin property for the account.
|
||||
* @param userId
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { MockProxy, mock } from "jest-mock-extended";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { mockAccountInfoWith } from "../../../spec/fake-account-service";
|
||||
import { FakeGlobalState } from "../../../spec/fake-state";
|
||||
import {
|
||||
FakeGlobalStateProvider,
|
||||
@@ -27,7 +28,7 @@ import {
|
||||
} from "./account.service";
|
||||
|
||||
describe("accountInfoEqual", () => {
|
||||
const accountInfo: AccountInfo = { name: "name", email: "email", emailVerified: true };
|
||||
const accountInfo = mockAccountInfoWith();
|
||||
|
||||
it("compares nulls", () => {
|
||||
expect(accountInfoEqual(null, null)).toBe(true);
|
||||
@@ -64,6 +65,23 @@ describe("accountInfoEqual", () => {
|
||||
expect(accountInfoEqual(accountInfo, same)).toBe(true);
|
||||
expect(accountInfoEqual(accountInfo, different)).toBe(false);
|
||||
});
|
||||
|
||||
it("compares creationDate", () => {
|
||||
const same = { ...accountInfo };
|
||||
const different = { ...accountInfo, creationDate: "2024-12-31T00:00:00.000Z" };
|
||||
|
||||
expect(accountInfoEqual(accountInfo, same)).toBe(true);
|
||||
expect(accountInfoEqual(accountInfo, different)).toBe(false);
|
||||
});
|
||||
|
||||
it("compares undefined creationDate", () => {
|
||||
const accountWithoutCreationDate = mockAccountInfoWith({ creationDate: undefined });
|
||||
const same = { ...accountWithoutCreationDate };
|
||||
const different = { ...accountWithoutCreationDate, creationDate: "2024-01-01T00:00:00.000Z" };
|
||||
|
||||
expect(accountInfoEqual(accountWithoutCreationDate, same)).toBe(true);
|
||||
expect(accountInfoEqual(accountWithoutCreationDate, different)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("accountService", () => {
|
||||
@@ -76,7 +94,10 @@ describe("accountService", () => {
|
||||
let activeAccountIdState: FakeGlobalState<UserId>;
|
||||
let accountActivityState: FakeGlobalState<Record<UserId, Date>>;
|
||||
const userId = Utils.newGuid() as UserId;
|
||||
const userInfo = { email: "email", name: "name", emailVerified: true };
|
||||
const userInfo = mockAccountInfoWith({
|
||||
email: "email",
|
||||
name: "name",
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
messagingService = mock();
|
||||
@@ -253,6 +274,56 @@ describe("accountService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("setCreationDate", () => {
|
||||
const initialState = { [userId]: userInfo };
|
||||
beforeEach(() => {
|
||||
accountsState.stateSubject.next(initialState);
|
||||
});
|
||||
|
||||
it("should update the account with a new creation date", async () => {
|
||||
const newCreationDate = "2024-12-31T00:00:00.000Z";
|
||||
await sut.setAccountCreationDate(userId, newCreationDate);
|
||||
const currentState = await firstValueFrom(accountsState.state$);
|
||||
|
||||
expect(currentState).toEqual({
|
||||
[userId]: { ...userInfo, creationDate: newCreationDate },
|
||||
});
|
||||
});
|
||||
|
||||
it("should not update if the creation date is the same", async () => {
|
||||
await sut.setAccountCreationDate(userId, userInfo.creationDate);
|
||||
const currentState = await firstValueFrom(accountsState.state$);
|
||||
|
||||
expect(currentState).toEqual(initialState);
|
||||
});
|
||||
|
||||
it("should update from undefined to a defined creation date", async () => {
|
||||
const accountWithoutCreationDate = mockAccountInfoWith({
|
||||
...userInfo,
|
||||
creationDate: undefined,
|
||||
});
|
||||
accountsState.stateSubject.next({ [userId]: accountWithoutCreationDate });
|
||||
|
||||
const newCreationDate = "2024-06-15T12:30:00.000Z";
|
||||
await sut.setAccountCreationDate(userId, newCreationDate);
|
||||
const currentState = await firstValueFrom(accountsState.state$);
|
||||
|
||||
expect(currentState).toEqual({
|
||||
[userId]: { ...accountWithoutCreationDate, creationDate: newCreationDate },
|
||||
});
|
||||
});
|
||||
|
||||
it("should update to a different creation date string format", async () => {
|
||||
const newCreationDate = "2023-03-15T08:45:30.123Z";
|
||||
await sut.setAccountCreationDate(userId, newCreationDate);
|
||||
const currentState = await firstValueFrom(accountsState.state$);
|
||||
|
||||
expect(currentState).toEqual({
|
||||
[userId]: { ...userInfo, creationDate: newCreationDate },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("setAccountVerifyNewDeviceLogin", () => {
|
||||
const initialState = true;
|
||||
beforeEach(() => {
|
||||
@@ -294,6 +365,7 @@ describe("accountService", () => {
|
||||
email: "",
|
||||
emailVerified: false,
|
||||
name: undefined,
|
||||
creationDate: undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,6 +62,7 @@ const LOGGED_OUT_INFO: AccountInfo = {
|
||||
email: "",
|
||||
emailVerified: false,
|
||||
name: undefined,
|
||||
creationDate: undefined,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -167,6 +168,10 @@ export class AccountServiceImplementation implements InternalAccountService {
|
||||
await this.setAccountInfo(userId, { emailVerified });
|
||||
}
|
||||
|
||||
async setAccountCreationDate(userId: UserId, creationDate: string): Promise<void> {
|
||||
await this.setAccountInfo(userId, { creationDate });
|
||||
}
|
||||
|
||||
async clean(userId: UserId) {
|
||||
await this.setAccountInfo(userId, LOGGED_OUT_INFO);
|
||||
await this.removeAccountActivity(userId);
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
SystemNotificationEvent,
|
||||
SystemNotificationsService,
|
||||
} from "@bitwarden/common/platform/system-notifications/system-notifications.service";
|
||||
import { mockAccountInfoWith } from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { AuthRequestAnsweringService } from "./auth-request-answering.service";
|
||||
@@ -48,14 +49,16 @@ describe("AuthRequestAnsweringService", () => {
|
||||
|
||||
// Common defaults
|
||||
authService.activeAccountStatus$ = of(AuthenticationStatus.Locked);
|
||||
accountService.activeAccount$ = of({
|
||||
id: userId,
|
||||
const accountInfo = mockAccountInfoWith({
|
||||
email: "user@example.com",
|
||||
emailVerified: true,
|
||||
name: "User",
|
||||
});
|
||||
accountService.activeAccount$ = of({
|
||||
id: userId,
|
||||
...accountInfo,
|
||||
});
|
||||
accountService.accounts$ = of({
|
||||
[userId]: { email: "user@example.com", emailVerified: true, name: "User" },
|
||||
[userId]: accountInfo,
|
||||
});
|
||||
(masterPasswordService.forceSetPasswordReason$ as jest.Mock).mockReturnValue(
|
||||
of(ForceSetPasswordReason.None),
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
makeStaticByteArray,
|
||||
mockAccountServiceWith,
|
||||
trackEmissions,
|
||||
mockAccountInfoWith,
|
||||
} from "../../../spec";
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { MessagingService } from "../../platform/abstractions/messaging.service";
|
||||
@@ -58,9 +59,10 @@ describe("AuthService", () => {
|
||||
const accountInfo = {
|
||||
status: AuthenticationStatus.Unlocked,
|
||||
id: userId,
|
||||
email: "email",
|
||||
emailVerified: false,
|
||||
name: "name",
|
||||
...mockAccountInfoWith({
|
||||
email: "email",
|
||||
name: "name",
|
||||
}),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -112,9 +114,10 @@ describe("AuthService", () => {
|
||||
const accountInfo2 = {
|
||||
status: AuthenticationStatus.Unlocked,
|
||||
id: Utils.newGuid() as UserId,
|
||||
email: "email2",
|
||||
emailVerified: false,
|
||||
name: "name2",
|
||||
...mockAccountInfoWith({
|
||||
email: "email2",
|
||||
name: "name2",
|
||||
}),
|
||||
};
|
||||
|
||||
const emissions = trackEmissions(sut.activeAccountStatus$);
|
||||
@@ -131,11 +134,13 @@ describe("AuthService", () => {
|
||||
it("requests auth status for all known users", async () => {
|
||||
const userId2 = Utils.newGuid() as UserId;
|
||||
|
||||
await accountService.addAccount(userId2, {
|
||||
email: "email2",
|
||||
emailVerified: false,
|
||||
name: "name2",
|
||||
});
|
||||
await accountService.addAccount(
|
||||
userId2,
|
||||
mockAccountInfoWith({
|
||||
email: "email2",
|
||||
name: "name2",
|
||||
}),
|
||||
);
|
||||
|
||||
const mockFn = jest.fn().mockReturnValue(of(AuthenticationStatus.Locked));
|
||||
sut.authStatusFor$ = mockFn;
|
||||
|
||||
@@ -8,12 +8,13 @@ import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { mockAccountInfoWith } from "../../../spec/fake-account-service";
|
||||
import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { OrganizationAutoEnrollStatusResponse } from "../../admin-console/models/response/organization-auto-enroll-status.response";
|
||||
import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service";
|
||||
import { I18nService } from "../../platform/abstractions/i18n.service";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { Account, AccountInfo, AccountService } from "../abstractions/account.service";
|
||||
import { Account, AccountService } from "../abstractions/account.service";
|
||||
|
||||
import { PasswordResetEnrollmentServiceImplementation } from "./password-reset-enrollment.service.implementation";
|
||||
|
||||
@@ -96,11 +97,10 @@ describe("PasswordResetEnrollmentServiceImplementation", () => {
|
||||
const encryptedKey = { encryptedString: "encryptedString" };
|
||||
organizationApiService.getKeys.mockResolvedValue(orgKeyResponse as any);
|
||||
|
||||
const user1AccountInfo: AccountInfo = {
|
||||
const user1AccountInfo = mockAccountInfoWith({
|
||||
name: "Test User 1",
|
||||
email: "test1@email.com",
|
||||
emailVerified: true,
|
||||
};
|
||||
});
|
||||
activeAccountSubject.next(Object.assign(user1AccountInfo, { id: "userId" as UserId }));
|
||||
|
||||
keyService.userKey$.mockReturnValue(of({ key: "key" } as any));
|
||||
|
||||
Reference in New Issue
Block a user