1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 21:50:15 +00:00

Test account service

This commit is contained in:
Matt Gibson
2023-09-28 18:55:00 -04:00
committed by Justin Baur
parent 932e4b3707
commit 196411eeb5
3 changed files with 130 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
// eslint-disable-next-line no-restricted-imports
import { Substitute, Arg } from "@fluffy-spoon/substitute";
import { Observable } from "rxjs";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
@@ -41,3 +42,22 @@ export function makeStaticByteArray(length: number, start = 0) {
* Use to mock a return value of a static fromJSON method.
*/
export const mockFromJson = (stub: any) => (stub + "_fromJSON") as any;
export function trackEmissions<T>(observable: Observable<T>): T[] {
const emissions: T[] = [];
observable.subscribe((value) => {
switch (typeof value) {
case "string":
case "number":
case "boolean":
emissions.push(value);
break;
case "object":
emissions.push({ ...value });
break;
default:
emissions.push(JSON.parse(JSON.stringify(value)));
}
});
return emissions;
}

View File

@@ -0,0 +1,109 @@
import { MockProxy, mock } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { trackEmissions } from "../../../spec/utils";
import { LogService } from "../../platform/abstractions/log.service";
import { MessagingService } from "../../platform/abstractions/messaging.service";
import { UserId } from "../../types/guid";
import { AuthenticationStatus } from "../enums/authentication-status";
import { AccountServiceImplementation } from "./account.service";
describe("accountService", () => {
let messagingService: MockProxy<MessagingService>;
let logService: MockProxy<LogService>;
let sut: AccountServiceImplementation;
const userId = "userId" as UserId;
beforeEach(() => {
messagingService = mock<MessagingService>();
logService = mock<LogService>();
sut = new AccountServiceImplementation(messagingService, logService);
});
afterEach(() => {
jest.resetAllMocks();
});
describe("setAccountStatus", () => {
let sutAccounts: BehaviorSubject<Record<UserId, AuthenticationStatus>>;
let accountsNext: jest.SpyInstance;
let emissions: Record<UserId, AuthenticationStatus>[];
beforeEach(() => {
sutAccounts = sut["accounts"];
accountsNext = jest.spyOn(sutAccounts, "next");
emissions = [];
sutAccounts.subscribe((value) => emissions.push(JSON.parse(JSON.stringify(value))));
});
it("should not emit if the status is the same", () => {
sut.setAccountStatus(userId, AuthenticationStatus.Locked);
sut.setAccountStatus(userId, AuthenticationStatus.Locked);
expect(sutAccounts.value).toEqual({ userId: AuthenticationStatus.Locked });
expect(accountsNext).toHaveBeenCalledTimes(1);
});
it("should emit if the status is different", () => {
const emissions = trackEmissions(sutAccounts);
sut.setAccountStatus(userId, AuthenticationStatus.Unlocked);
sut.setAccountStatus(userId, AuthenticationStatus.Locked);
expect(emissions).toEqual([
{}, // initial value
{ userId: AuthenticationStatus.Unlocked },
{ userId: AuthenticationStatus.Locked },
]);
});
it("should emit logout if the status is logged out", () => {
const emissions = trackEmissions(sut.accountLogout$);
sut.setAccountStatus(userId, AuthenticationStatus.Unlocked);
sut.setAccountStatus(userId, AuthenticationStatus.LoggedOut);
expect(emissions).toEqual([userId]);
});
it("should emit lock if the status is locked", () => {
const emissions = trackEmissions(sut.accountLock$);
sut.setAccountStatus(userId, AuthenticationStatus.Unlocked);
sut.setAccountStatus(userId, AuthenticationStatus.Locked);
expect(emissions).toEqual([userId]);
});
});
describe("switchAccount", () => {
let emissions: { id: string; status: AuthenticationStatus }[];
beforeEach(() => {
emissions = [];
sut.activeAccount$.subscribe((value) => emissions.push(value));
});
it("should emit undefined if no account is provided", () => {
sut.switchAccount(undefined);
expect(emissions).toEqual([undefined]);
});
it("should emit the active account and status", () => {
sut.setAccountStatus(userId, AuthenticationStatus.Unlocked);
sut.switchAccount(userId);
sut.setAccountStatus(userId, AuthenticationStatus.Locked);
sut.switchAccount(undefined);
sut.switchAccount(undefined);
expect(emissions).toEqual([
undefined, // initial value
{ id: userId, status: AuthenticationStatus.Unlocked },
{ id: userId, status: AuthenticationStatus.Locked },
]);
});
it("should throw if switched to an unknown account", () => {
expect(() => sut.switchAccount(userId)).toThrowError("Account does not exist");
});
});
});

View File

@@ -23,7 +23,7 @@ export class AccountServiceImplementation implements InternalAccountService {
activeAccount$ = this.activeAccountId.pipe(
combineLatestWith(this.accounts$),
map(([id, accounts]) => (id ? { id, status: accounts[id] } : undefined)),
distinctUntilChanged((a, b) => a.id === b.id && a.status === b.status),
distinctUntilChanged(),
share()
);
accountLock$ = this.lock.asObservable();