1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-06-25 10:25:36 +02:00

[ADR-0006][AC-319] Migrate all tests to use jest mock instead of substitute (#6520)

Standardize on using jest mock instead of having two mocking frameworks which can be confusing.
This commit is contained in:
Oscar Hinton 2023-10-17 19:02:33 +02:00 committed by GitHub
parent 5cacd79d8c
commit ffb67be0a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 198 additions and 208 deletions

View File

@ -104,10 +104,7 @@
]
}
],
"no-restricted-imports": [
"error",
{ "patterns": ["src/**/*"], "paths": ["@fluffy-spoon/substitute"] }
]
"no-restricted-imports": ["error", { "patterns": ["src/**/*"] }]
}
},
{

View File

@ -1,10 +1,9 @@
// eslint-disable-next-line no-restricted-imports
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
import BrowserLocalStorageService from "./browser-local-storage.service";
import BrowserMemoryStorageService from "./browser-memory-storage.service";
@ -12,8 +11,8 @@ import { KeyGenerationService } from "./key-generation.service";
import { LocalBackedSessionStorageService } from "./local-backed-session-storage.service";
describe("Browser Session Storage Service", () => {
let encryptService: SubstituteOf<EncryptServiceImplementation>;
let keyGenerationService: SubstituteOf<KeyGenerationService>;
let encryptService: MockProxy<EncryptService>;
let keyGenerationService: MockProxy<KeyGenerationService>;
let cache: Map<string, any>;
const testObj = { a: 1, b: 2 };
@ -28,8 +27,8 @@ describe("Browser Session Storage Service", () => {
let sut: LocalBackedSessionStorageService;
beforeEach(() => {
encryptService = Substitute.for();
keyGenerationService = Substitute.for();
encryptService = mock<EncryptService>();
keyGenerationService = mock<KeyGenerationService>();
sut = new LocalBackedSessionStorageService(encryptService, keyGenerationService);
@ -138,6 +137,8 @@ describe("Browser Session Storage Service", () => {
jest.spyOn(sessionStorage, "get").mockResolvedValue(null);
jest.spyOn(localStorage, "save").mockResolvedValue();
jest.spyOn(sessionStorage, "save").mockResolvedValue();
encryptService.encrypt.mockResolvedValue(mockEnc("{}"));
});
it("should remove key from cache if value is null", async () => {
@ -205,7 +206,7 @@ describe("Browser Session Storage Service", () => {
describe("new key creation", () => {
beforeEach(() => {
jest.spyOn(sessionStorage, "get").mockResolvedValue(null);
keyGenerationService.makeEphemeralKey().resolves(key);
keyGenerationService.makeEphemeralKey.mockResolvedValue(key);
jest.spyOn(sut, "setSessionEncKey").mockResolvedValue();
});
@ -213,7 +214,7 @@ describe("Browser Session Storage Service", () => {
const result = await sut.getSessionEncKey();
expect(result).toStrictEqual(key);
keyGenerationService.received(1).makeEphemeralKey();
expect(keyGenerationService.makeEphemeralKey).toBeCalledTimes(1);
});
it("should store a symmetric crypto key if it makes one", async () => {
@ -244,19 +245,23 @@ describe("Browser Session Storage Service", () => {
});
it("should decrypt returned sessions", async () => {
encryptService.decryptToUtf8(encSession, key).resolves(decryptedSession);
encryptService.decryptToUtf8
.calledWith(expect.anything(), key)
.mockResolvedValue(decryptedSession);
await sut.getLocalSession(key);
encryptService.received(1).decryptToUtf8(encSession, key);
expect(encryptService.decryptToUtf8).toHaveBeenNthCalledWith(1, encSession, key);
});
it("should parse session", async () => {
encryptService.decryptToUtf8(encSession, key).resolves(decryptedSession);
encryptService.decryptToUtf8
.calledWith(expect.anything(), key)
.mockResolvedValue(decryptedSession);
const result = await sut.getLocalSession(key);
expect(result).toEqual(session);
});
it("should remove state if decryption fails", async () => {
encryptService.decryptToUtf8(Arg.any(), Arg.any()).resolves(null);
encryptService.decryptToUtf8.mockResolvedValue(null);
const setSessionEncKeySpy = jest.spyOn(sut, "setSessionEncKey").mockResolvedValue();
const removeLocalSessionSpy = jest.spyOn(localStorage, "remove").mockResolvedValue();
@ -274,15 +279,15 @@ describe("Browser Session Storage Service", () => {
const testJSON = JSON.stringify(testSession);
it("should encrypt a stringified session", async () => {
encryptService.encrypt(Arg.any(), Arg.any()).mimicks(mockEnc);
encryptService.encrypt.mockImplementation(mockEnc);
jest.spyOn(localStorage, "save").mockResolvedValue();
await sut.setLocalSession(testSession, key);
encryptService.received(1).encrypt(testJSON, key);
expect(encryptService.encrypt).toHaveBeenNthCalledWith(1, testJSON, key);
});
it("should remove local session if null", async () => {
encryptService.encrypt(Arg.any(), Arg.any()).resolves(null);
encryptService.encrypt.mockResolvedValue(null);
const spy = jest.spyOn(localStorage, "remove").mockResolvedValue();
await sut.setLocalSession(null, key);
@ -290,7 +295,7 @@ describe("Browser Session Storage Service", () => {
});
it("should save encrypted string", async () => {
encryptService.encrypt(Arg.any(), Arg.any()).mimicks(mockEnc);
encryptService.encrypt.mockImplementation(mockEnc);
const spy = jest.spyOn(localStorage, "save").mockResolvedValue();
await sut.setLocalSession(testSession, key);

View File

@ -1,8 +1,6 @@
import { NO_ERRORS_SCHEMA } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ActivatedRoute } from "@angular/router";
// eslint-disable-next-line no-restricted-imports
import { Substitute } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended";
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
@ -28,15 +26,15 @@ describe("GeneratorComponent", () => {
providers: [
{
provide: PasswordGenerationServiceAbstraction,
useClass: Substitute.for<PasswordGenerationServiceAbstraction>(),
useValue: mock<PasswordGenerationServiceAbstraction>(),
},
{
provide: UsernameGenerationServiceAbstraction,
useClass: Substitute.for<UsernameGenerationServiceAbstraction>(),
useValue: mock<UsernameGenerationServiceAbstraction>(),
},
{
provide: StateService,
useClass: Substitute.for<StateService>(),
useValue: mock<StateService>(),
},
{
provide: PlatformUtilsService,
@ -44,15 +42,15 @@ describe("GeneratorComponent", () => {
},
{
provide: I18nService,
useClass: Substitute.for<I18nService>(),
useValue: mock<I18nService>(),
},
{
provide: ActivatedRoute,
useClass: Substitute.for<ActivatedRoute>(),
useValue: mock<ActivatedRoute>(),
},
{
provide: LogService,
useClass: Substitute.for<LogService>(),
useValue: mock<LogService>(),
},
],
schemas: [NO_ERRORS_SCHEMA],

View File

@ -15,8 +15,7 @@
"@bitwarden/web-vault/*",
"src/**/*",
"bitwarden_license"
],
"paths": ["@fluffy-spoon/substitute"]
]
}
]
}

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { Substitute, Arg } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
@ -22,11 +21,11 @@ export function BuildTestObject<T, K extends keyof T = keyof T>(
return Object.assign(constructor === null ? {} : new constructor(), def) as T;
}
export function mockEnc(s: string): EncString {
const mock = Substitute.for<EncString>();
mock.decrypt(Arg.any(), Arg.any()).resolves(s);
export function mockEnc(s: string): MockProxy<EncString> {
const mocked = mock<EncString>();
mocked.decrypt.mockResolvedValue(s);
return mock;
return mocked;
}
export function makeStaticByteArray(length: number, start = 0) {

View File

@ -1,7 +1,6 @@
// eslint-disable-next-line no-restricted-imports
import { Substitute, Arg } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended";
import { makeStaticByteArray } from "../../../../spec";
import { EncryptionType } from "../../../enums";
import { EncryptService } from "../../../platform/abstractions/encrypt.service";
import {
@ -64,11 +63,16 @@ describe("EncString", () => {
describe("decrypt", () => {
const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data");
const cryptoService = Substitute.for<CryptoService>();
cryptoService.getOrgKey(null).resolves(null);
const cryptoService = mock<CryptoService>();
cryptoService.hasUserKey.mockResolvedValue(true);
cryptoService.getUserKeyWithLegacySupport.mockResolvedValue(
new SymmetricCryptoKey(makeStaticByteArray(32)) as UserKey
);
const encryptService = Substitute.for<EncryptService>();
encryptService.decryptToUtf8(encString, Arg.any()).resolves("decrypted");
const encryptService = mock<EncryptService>();
encryptService.decryptToUtf8
.calledWith(encString, expect.anything())
.mockResolvedValue("decrypted");
beforeEach(() => {
(window as any).bitwardenContainerService = new ContainerService(
@ -85,7 +89,7 @@ describe("EncString", () => {
it("result should be cached", async () => {
const decrypted = await encString.decrypt(null);
encryptService.received(1).decryptToUtf8(Arg.any(), Arg.any());
expect(encryptService.decryptToUtf8).toBeCalledTimes(1);
expect(decrypted).toBe("decrypted");
});

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { Substitute } from "@fluffy-spoon/substitute";
import { mock } from "jest-mock-extended";
import { Utils } from "../../platform/misc/utils";
import { PlatformUtilsService } from "../abstractions/platform-utils.service";
@ -582,8 +581,8 @@ function testRsaGenerateKeyPair(length: 1024 | 2048 | 4096) {
}
function getWebCryptoFunctionService() {
const platformUtilsMock = Substitute.for<PlatformUtilsService>();
platformUtilsMock.isEdge().mimicks(() => navigator.userAgent.indexOf(" Edg/") !== -1);
const platformUtilsMock = mock<PlatformUtilsService>();
platformUtilsMock.isEdge.mockImplementation(() => navigator.userAgent.indexOf(" Edg/") !== -1);
return new WebCryptoFunctionService(window);
}

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { OrganizationService } from "../admin-console/abstractions/organization/organization.service.abstraction";
@ -22,19 +21,19 @@ import { StateService } from "../platform/services/state.service";
describe("PolicyService", () => {
let policyService: PolicyService;
let cryptoService: SubstituteOf<CryptoService>;
let stateService: SubstituteOf<StateService>;
let organizationService: SubstituteOf<OrganizationService>;
let encryptService: SubstituteOf<EncryptService>;
let cryptoService: MockProxy<CryptoService>;
let stateService: MockProxy<StateService>;
let organizationService: MockProxy<OrganizationService>;
let encryptService: MockProxy<EncryptService>;
let activeAccount: BehaviorSubject<string>;
let activeAccountUnlocked: BehaviorSubject<boolean>;
beforeEach(() => {
stateService = Substitute.for();
organizationService = Substitute.for();
organizationService
.getAll("user")
.resolves([
stateService = mock<StateService>();
organizationService = mock<OrganizationService>();
organizationService.getAll
.calledWith("user")
.mockResolvedValue([
new Organization(
organizationData(
"test-organization",
@ -45,24 +44,24 @@ describe("PolicyService", () => {
)
),
]);
organizationService.getAll(undefined).resolves([]);
organizationService.getAll(null).resolves([]);
organizationService.getAll.calledWith(undefined).mockResolvedValue([]);
organizationService.getAll.calledWith(null).mockResolvedValue([]);
activeAccount = new BehaviorSubject("123");
activeAccountUnlocked = new BehaviorSubject(true);
stateService.getDecryptedPolicies({ userId: "user" }).resolves(null);
stateService.getEncryptedPolicies({ userId: "user" }).resolves({
stateService.getDecryptedPolicies.calledWith({ userId: "user" }).mockResolvedValue(null);
stateService.getEncryptedPolicies.calledWith({ userId: "user" }).mockResolvedValue({
"1": policyData("1", "test-organization", PolicyType.MaximumVaultTimeout, true, {
minutes: 14,
}),
});
stateService.getEncryptedPolicies().resolves({
stateService.getEncryptedPolicies.mockResolvedValue({
"1": policyData("1", "test-organization", PolicyType.MaximumVaultTimeout, true, {
minutes: 14,
}),
});
stateService.activeAccount$.returns(activeAccount);
stateService.activeAccountUnlocked$.returns(activeAccountUnlocked);
stateService.getUserId().resolves("user");
stateService.activeAccount$ = activeAccount;
stateService.activeAccountUnlocked$ = activeAccountUnlocked;
stateService.getUserId.mockResolvedValue("user");
(window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService);
policyService = new PolicyService(stateService, organizationService);
@ -120,7 +119,7 @@ describe("PolicyService", () => {
it("null userId", async () => {
await policyService.clear();
stateService.received(1).setEncryptedPolicies(Arg.any(), Arg.any());
expect(stateService.setEncryptedPolicies).toBeCalledTimes(1);
expect((await firstValueFrom(policyService.policies$)).length).toBe(0);
});
@ -128,7 +127,7 @@ describe("PolicyService", () => {
it("matching userId", async () => {
await policyService.clear("user");
stateService.received(1).setEncryptedPolicies(Arg.any(), Arg.any());
expect(stateService.setEncryptedPolicies).toBeCalledTimes(1);
expect((await firstValueFrom(policyService.policies$)).length).toBe(0);
});
@ -136,7 +135,7 @@ describe("PolicyService", () => {
it("mismatching userId", async () => {
await policyService.clear("12");
stateService.received(1).setEncryptedPolicies(Arg.any(), Arg.any());
expect(stateService.setEncryptedPolicies).toBeCalledTimes(1);
expect((await firstValueFrom(policyService.policies$)).length).toBe(1);
});

View File

@ -1,20 +1,19 @@
// eslint-disable-next-line no-restricted-imports
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { CryptoService } from "../platform/abstractions/crypto.service";
import { EncryptService } from "../platform/abstractions/encrypt.service";
import { StateService } from "../platform/abstractions/state.service";
import { ContainerService } from "../platform/services/container.service";
import { StateService } from "../platform/services/state.service";
import { SettingsService } from "./settings.service";
describe("SettingsService", () => {
let settingsService: SettingsService;
let cryptoService: SubstituteOf<CryptoService>;
let encryptService: SubstituteOf<EncryptService>;
let stateService: SubstituteOf<StateService>;
let cryptoService: MockProxy<CryptoService>;
let encryptService: MockProxy<EncryptService>;
let stateService: MockProxy<StateService>;
let activeAccount: BehaviorSubject<string>;
let activeAccountUnlocked: BehaviorSubject<boolean>;
@ -25,15 +24,15 @@ describe("SettingsService", () => {
];
beforeEach(() => {
cryptoService = Substitute.for();
encryptService = Substitute.for();
stateService = Substitute.for();
cryptoService = mock<CryptoService>();
encryptService = mock<EncryptService>();
stateService = mock<StateService>();
activeAccount = new BehaviorSubject("123");
activeAccountUnlocked = new BehaviorSubject(true);
stateService.getSettings().resolves({ equivalentDomains: mockEquivalentDomains });
stateService.activeAccount$.returns(activeAccount);
stateService.activeAccountUnlocked$.returns(activeAccountUnlocked);
stateService.getSettings.mockResolvedValue({ equivalentDomains: mockEquivalentDomains });
stateService.activeAccount$ = activeAccount;
stateService.activeAccountUnlocked$ = activeAccountUnlocked;
(window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService);
settingsService = new SettingsService(stateService);
@ -66,7 +65,7 @@ describe("SettingsService", () => {
it("setEquivalentDomains", async () => {
await settingsService.setEquivalentDomains([["test2"], ["domains2"]]);
stateService.received(1).setSettings(Arg.any());
expect(stateService.setSettings).toBeCalledTimes(1);
expect((await firstValueFrom(settingsService.settings$)).equivalentDomains).toEqual([
["test2"],
@ -77,7 +76,7 @@ describe("SettingsService", () => {
it("clear", async () => {
await settingsService.clear();
stateService.received(1).setSettings(Arg.any(), Arg.any());
expect(stateService.setSettings).toBeCalledTimes(1);
expect(await firstValueFrom(settingsService.settings$)).toEqual({});
});

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { Substitute, Arg } from "@fluffy-spoon/substitute";
import { mock } from "jest-mock-extended";
import { mockEnc } from "../../../../../spec";
import { SendType } from "../../enums/send-type";
@ -61,8 +60,8 @@ describe("SendAccess", () => {
sendAccess.type = SendType.Text;
sendAccess.name = mockEnc("name");
const text = Substitute.for<SendText>();
text.decrypt(Arg.any()).resolves({} as any);
const text = mock<SendText>();
text.decrypt.mockResolvedValue({} as any);
sendAccess.text = text;
sendAccess.expirationDate = new Date("2022-01-31T12:00:00.000Z");
@ -70,7 +69,7 @@ describe("SendAccess", () => {
const view = await sendAccess.decrypt(null);
text.received(1).decrypt(Arg.any());
expect(text.decrypt).toHaveBeenCalledTimes(1);
expect(view).toEqual({
id: "id",

View File

@ -1,10 +1,8 @@
// eslint-disable-next-line no-restricted-imports
import { Substitute, Arg, SubstituteOf } from "@fluffy-spoon/substitute";
import { mock } from "jest-mock-extended";
import { makeStaticByteArray, mockEnc } from "../../../../../spec";
import { CryptoService } from "../../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
import { EncString } from "../../../../platform/models/domain/enc-string";
import { ContainerService } from "../../../../platform/services/container.service";
import { SendType } from "../../enums/send-type";
import { SendData } from "../data/send.data";
@ -89,8 +87,8 @@ describe("Send", () => {
});
it("Decrypt", async () => {
const text = Substitute.for<SendText>();
text.decrypt(Arg.any()).resolves("textView" as any);
const text = mock<SendText>();
text.decrypt.mockResolvedValue("textView" as any);
const send = new Send();
send.id = "id";
@ -108,18 +106,20 @@ describe("Send", () => {
send.disabled = false;
send.hideEmail = true;
const cryptoService = Substitute.for<CryptoService>();
cryptoService.decryptToBytes(send.key, null).resolves(makeStaticByteArray(32));
cryptoService.makeSendKey(Arg.any()).resolves("cryptoKey" as any);
const cryptoService = mock<CryptoService>();
cryptoService.decryptToBytes
.calledWith(send.key, null)
.mockResolvedValue(makeStaticByteArray(32));
cryptoService.makeSendKey.mockResolvedValue("cryptoKey" as any);
const encryptService = Substitute.for<EncryptService>();
const encryptService = mock<EncryptService>();
(window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService);
const view = await send.decrypt();
text.received(1).decrypt("cryptoKey" as any);
(send.name as SubstituteOf<EncString>).received(1).decrypt(null, "cryptoKey" as any);
expect(text.decrypt).toHaveBeenNthCalledWith(1, "cryptoKey");
expect(send.name.decrypt).toHaveBeenNthCalledWith(1, null, "cryptoKey");
expect(view).toMatchObject({
id: "id",

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { Substitute, Arg } from "@fluffy-spoon/substitute";
import { mock } from "jest-mock-extended";
import { Jsonify } from "type-fest";
import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec/utils";
@ -219,15 +218,15 @@ describe("Cipher DTO", () => {
loginView.username = "username";
loginView.password = "password";
const login = Substitute.for<Login>();
login.decrypt(Arg.any(), Arg.any()).resolves(loginView);
const login = mock<Login>();
login.decrypt.mockResolvedValue(loginView);
cipher.login = login;
const cryptoService = Substitute.for<CryptoService>();
const encryptService = Substitute.for<EncryptService>();
const cipherService = Substitute.for<CipherService>();
const cryptoService = mock<CryptoService>();
const encryptService = mock<EncryptService>();
const cipherService = mock<CipherService>();
encryptService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(64));
encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64));
(window as any).bitwardenContainerService = new ContainerService(
cryptoService,
@ -343,11 +342,11 @@ describe("Cipher DTO", () => {
cipher.secureNote.type = SecureNoteType.Generic;
cipher.key = mockEnc("EncKey");
const cryptoService = Substitute.for<CryptoService>();
const encryptService = Substitute.for<EncryptService>();
const cipherService = Substitute.for<CipherService>();
const cryptoService = mock<CryptoService>();
const encryptService = mock<EncryptService>();
const cipherService = mock<CipherService>();
encryptService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(64));
encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64));
(window as any).bitwardenContainerService = new ContainerService(
cryptoService,
@ -477,15 +476,15 @@ describe("Cipher DTO", () => {
cardView.cardholderName = "cardholderName";
cardView.number = "4111111111111111";
const card = Substitute.for<Card>();
card.decrypt(Arg.any(), Arg.any()).resolves(cardView);
const card = mock<Card>();
card.decrypt.mockResolvedValue(cardView);
cipher.card = card;
const cryptoService = Substitute.for<CryptoService>();
const encryptService = Substitute.for<EncryptService>();
const cipherService = Substitute.for<CipherService>();
const cryptoService = mock<CryptoService>();
const encryptService = mock<EncryptService>();
const cipherService = mock<CipherService>();
encryptService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(64));
encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64));
(window as any).bitwardenContainerService = new ContainerService(
cryptoService,
@ -639,15 +638,15 @@ describe("Cipher DTO", () => {
identityView.firstName = "firstName";
identityView.lastName = "lastName";
const identity = Substitute.for<Identity>();
identity.decrypt(Arg.any(), Arg.any()).resolves(identityView);
const identity = mock<Identity>();
identity.decrypt.mockResolvedValue(identityView);
cipher.identity = identity;
const cryptoService = Substitute.for<CryptoService>();
const encryptService = Substitute.for<EncryptService>();
const cipherService = Substitute.for<CipherService>();
const cryptoService = mock<CryptoService>();
const encryptService = mock<EncryptService>();
const cipherService = mock<CipherService>();
encryptService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(64));
encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64));
(window as any).bitwardenContainerService = new ContainerService(
cryptoService,

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { Substitute, Arg } from "@fluffy-spoon/substitute";
import { mock } from "jest-mock-extended";
import { mockEnc, mockFromJson } from "../../../../spec";
import { UriMatchType } from "../../../enums";
@ -51,10 +50,10 @@ describe("Login DTO", () => {
});
it("Decrypts correctly", async () => {
const loginUri = Substitute.for<LoginUri>();
const loginUri = mock<LoginUri>();
const loginUriView = new LoginUriView();
loginUriView.uri = "decrypted uri";
loginUri.decrypt(Arg.any()).resolves(loginUriView);
loginUri.decrypt.mockResolvedValue(loginUriView);
const login = new Login();
login.uris = [loginUri];

View File

@ -1,13 +1,14 @@
// eslint-disable-next-line no-restricted-imports
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { makeStaticByteArray } from "../../../../spec";
import { CryptoService } from "../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../platform/abstractions/encrypt.service";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { StateService } from "../../../platform/abstractions/state.service";
import { EncString } from "../../../platform/models/domain/enc-string";
import { SymmetricCryptoKey, UserKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { ContainerService } from "../../../platform/services/container.service";
import { StateService } from "../../../platform/services/state.service";
import { CipherService } from "../../abstractions/cipher.service";
import { FolderData } from "../../models/data/folder.data";
import { FolderView } from "../../models/view/folder.view";
@ -16,28 +17,36 @@ import { FolderService } from "../../services/folder/folder.service";
describe("Folder Service", () => {
let folderService: FolderService;
let cryptoService: SubstituteOf<CryptoService>;
let encryptService: SubstituteOf<EncryptService>;
let i18nService: SubstituteOf<I18nService>;
let cipherService: SubstituteOf<CipherService>;
let stateService: SubstituteOf<StateService>;
let cryptoService: MockProxy<CryptoService>;
let encryptService: MockProxy<EncryptService>;
let i18nService: MockProxy<I18nService>;
let cipherService: MockProxy<CipherService>;
let stateService: MockProxy<StateService>;
let activeAccount: BehaviorSubject<string>;
let activeAccountUnlocked: BehaviorSubject<boolean>;
beforeEach(() => {
cryptoService = Substitute.for();
encryptService = Substitute.for();
i18nService = Substitute.for();
cipherService = Substitute.for();
stateService = Substitute.for();
cryptoService = mock<CryptoService>();
encryptService = mock<EncryptService>();
i18nService = mock<I18nService>();
cipherService = mock<CipherService>();
stateService = mock<StateService>();
activeAccount = new BehaviorSubject("123");
activeAccountUnlocked = new BehaviorSubject(true);
stateService.getEncryptedFolders().resolves({
i18nService.collator = new Intl.Collator("en");
stateService.getEncryptedFolders.mockResolvedValue({
"1": folderData("1", "test"),
});
stateService.activeAccount$.returns(activeAccount);
stateService.activeAccountUnlocked$.returns(activeAccountUnlocked);
stateService.activeAccount$ = activeAccount;
stateService.activeAccountUnlocked$ = activeAccountUnlocked;
cryptoService.hasUserKey.mockResolvedValue(true);
cryptoService.getUserKeyWithLegacySupport.mockResolvedValue(
new SymmetricCryptoKey(makeStaticByteArray(32)) as UserKey
);
encryptService.decryptToUtf8.mockResolvedValue("DEC");
(window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService);
folderService = new FolderService(cryptoService, i18nService, cipherService, stateService);
@ -48,8 +57,8 @@ describe("Folder Service", () => {
model.id = "2";
model.name = "Test Folder";
cryptoService.encrypt(Arg.any()).resolves(new EncString("ENC"));
cryptoService.decryptToUtf8(Arg.any()).resolves("DEC");
cryptoService.encrypt.mockResolvedValue(new EncString("ENC"));
cryptoService.decryptToUtf8.mockResolvedValue("DEC");
const result = await folderService.encrypt(model);
@ -69,9 +78,9 @@ describe("Folder Service", () => {
expect(result).toEqual({
id: "1",
name: {
decryptedValue: [],
encryptedString: "test",
encryptionType: 0,
decryptedValue: "DEC",
},
revisionDate: null,
});
@ -91,27 +100,27 @@ describe("Folder Service", () => {
{
id: "1",
name: {
decryptedValue: [],
encryptedString: "test",
encryptionType: 0,
decryptedValue: "DEC",
},
revisionDate: null,
},
{
id: "2",
name: {
decryptedValue: [],
encryptedString: "test 2",
encryptionType: 0,
decryptedValue: "DEC",
},
revisionDate: null,
},
]);
expect(await firstValueFrom(folderService.folderViews$)).toEqual([
{ id: "1", name: [], revisionDate: null },
{ id: "2", name: [], revisionDate: null },
{ id: null, name: [], revisionDate: null },
{ id: "1", name: "DEC", revisionDate: null },
{ id: "2", name: "DEC", revisionDate: null },
{ id: null, name: undefined, revisionDate: null },
]);
});
@ -122,7 +131,7 @@ describe("Folder Service", () => {
{
id: "2",
name: {
decryptedValue: [],
decryptedValue: "DEC",
encryptedString: "test 2",
encryptionType: 0,
},
@ -131,8 +140,8 @@ describe("Folder Service", () => {
]);
expect(await firstValueFrom(folderService.folderViews$)).toEqual([
{ id: "2", name: [], revisionDate: null },
{ id: null, name: [], revisionDate: null },
{ id: "2", name: "DEC", revisionDate: null },
{ id: null, name: undefined, revisionDate: null },
]);
});
@ -142,7 +151,7 @@ describe("Folder Service", () => {
expect((await firstValueFrom(folderService.folders$)).length).toBe(0);
expect(await firstValueFrom(folderService.folderViews$)).toEqual([
{ id: null, name: [], revisionDate: null },
{ id: null, name: undefined, revisionDate: null },
]);
});
@ -166,17 +175,17 @@ describe("Folder Service", () => {
it("null userId", async () => {
await folderService.clear();
stateService.received(1).setEncryptedFolders(Arg.any(), Arg.any());
expect(stateService.setEncryptedFolders).toBeCalledTimes(1);
expect((await firstValueFrom(folderService.folders$)).length).toBe(0);
expect((await firstValueFrom(folderService.folderViews$)).length).toBe(0);
});
it("matching userId", async () => {
stateService.getUserId().resolves("1");
stateService.getUserId.mockResolvedValue("1");
await folderService.clear("1");
stateService.received(1).setEncryptedFolders(Arg.any(), Arg.any());
expect(stateService.setEncryptedFolders).toBeCalledTimes(1);
expect((await firstValueFrom(folderService.folders$)).length).toBe(0);
expect((await firstValueFrom(folderService.folderViews$)).length).toBe(0);
@ -185,7 +194,7 @@ describe("Folder Service", () => {
it("missmatching userId", async () => {
await folderService.clear("12");
stateService.received(1).setEncryptedFolders(Arg.any(), Arg.any());
expect(stateService.setEncryptedFolders).toBeCalledTimes(1);
expect((await firstValueFrom(folderService.folders$)).length).toBe(1);
expect((await firstValueFrom(folderService.folderViews$)).length).toBe(2);

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
@ -142,25 +141,26 @@ function expectEqualFolders(folders: Folder[], jsonResult: string) {
describe("VaultExportService", () => {
let exportService: VaultExportService;
let apiService: SubstituteOf<ApiService>;
let cryptoFunctionService: SubstituteOf<CryptoFunctionService>;
let cipherService: SubstituteOf<CipherService>;
let folderService: SubstituteOf<FolderService>;
let cryptoService: SubstituteOf<CryptoService>;
let stateService: SubstituteOf<StateService>;
let apiService: MockProxy<ApiService>;
let cryptoFunctionService: MockProxy<CryptoFunctionService>;
let cipherService: MockProxy<CipherService>;
let folderService: MockProxy<FolderService>;
let cryptoService: MockProxy<CryptoService>;
let stateService: MockProxy<StateService>;
beforeEach(() => {
apiService = Substitute.for<ApiService>();
cryptoFunctionService = Substitute.for<CryptoFunctionService>();
cipherService = Substitute.for<CipherService>();
folderService = Substitute.for<FolderService>();
cryptoService = Substitute.for<CryptoService>();
stateService = Substitute.for<StateService>();
apiService = mock<ApiService>();
cryptoFunctionService = mock<CryptoFunctionService>();
cipherService = mock<CipherService>();
folderService = mock<FolderService>();
cryptoService = mock<CryptoService>();
stateService = mock<StateService>();
folderService.getAllDecryptedFromState().resolves(UserFolderViews);
folderService.getAllFromState().resolves(UserFolders);
stateService.getKdfType().resolves(KdfType.PBKDF2_SHA256);
stateService.getKdfConfig().resolves(new KdfConfig(DEFAULT_PBKDF2_ITERATIONS));
folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews);
folderService.getAllFromState.mockResolvedValue(UserFolders);
stateService.getKdfType.mockResolvedValue(KdfType.PBKDF2_SHA256);
stateService.getKdfConfig.mockResolvedValue(new KdfConfig(DEFAULT_PBKDF2_ITERATIONS));
cryptoService.encrypt.mockResolvedValue(new EncString("encrypted"));
exportService = new VaultExportService(
folderService,
@ -173,7 +173,7 @@ describe("VaultExportService", () => {
});
it("exports unencrypted user ciphers", async () => {
cipherService.getAllDecrypted().resolves(UserCipherViews.slice(0, 1));
cipherService.getAllDecrypted.mockResolvedValue(UserCipherViews.slice(0, 1));
const actual = await exportService.getExport("json");
@ -181,7 +181,7 @@ describe("VaultExportService", () => {
});
it("exports encrypted json user ciphers", async () => {
cipherService.getAll().resolves(UserCipherDomains.slice(0, 1));
cipherService.getAll.mockResolvedValue(UserCipherDomains.slice(0, 1));
const actual = await exportService.getExport("encrypted_json");
@ -189,7 +189,7 @@ describe("VaultExportService", () => {
});
it("does not unencrypted export trashed user items", async () => {
cipherService.getAllDecrypted().resolves(UserCipherViews);
cipherService.getAllDecrypted.mockResolvedValue(UserCipherViews);
const actual = await exportService.getExport("json");
@ -197,7 +197,7 @@ describe("VaultExportService", () => {
});
it("does not encrypted export trashed user items", async () => {
cipherService.getAll().resolves(UserCipherDomains);
cipherService.getAll.mockResolvedValue(UserCipherDomains);
const actual = await exportService.getExport("encrypted_json");
@ -207,21 +207,21 @@ describe("VaultExportService", () => {
describe("password protected export", () => {
let exportString: string;
let exportObject: any;
let mac: SubstituteOf<EncString>;
let data: SubstituteOf<EncString>;
let mac: MockProxy<EncString>;
let data: MockProxy<EncString>;
const password = "password";
const salt = "salt";
describe("export json object", () => {
beforeEach(async () => {
mac = Substitute.for<EncString>();
data = Substitute.for<EncString>();
mac = mock<EncString>();
data = mock<EncString>();
mac.encryptedString.returns("mac" as EncryptedString);
data.encryptedString.returns("encData" as EncryptedString);
mac.encryptedString = "mac" as EncryptedString;
data.encryptedString = "encData" as EncryptedString;
jest.spyOn(Utils, "fromBufferToB64").mockReturnValue(salt);
cipherService.getAllDecrypted().resolves(UserCipherViews.slice(0, 1));
cipherService.getAllDecrypted.mockResolvedValue(UserCipherViews.slice(0, 1));
exportString = await exportService.getPasswordProtectedExport(password);
exportObject = JSON.parse(exportString);
@ -248,7 +248,7 @@ describe("VaultExportService", () => {
});
it("has a mac property", async () => {
cryptoService.encrypt(Arg.any(), Arg.any()).resolves(mac);
cryptoService.encrypt.mockResolvedValue(mac);
exportString = await exportService.getPasswordProtectedExport(password);
exportObject = JSON.parse(exportString);
@ -256,7 +256,7 @@ describe("VaultExportService", () => {
});
it("has data property", async () => {
cryptoService.encrypt(Arg.any(), Arg.any()).resolves(data);
cryptoService.encrypt.mockResolvedValue(data);
exportString = await exportService.getPasswordProtectedExport(password);
exportObject = JSON.parse(exportString);
@ -271,7 +271,7 @@ describe("VaultExportService", () => {
});
it("exported unencrypted object contains folders", async () => {
cipherService.getAllDecrypted().resolves(UserCipherViews.slice(0, 1));
cipherService.getAllDecrypted.mockResolvedValue(UserCipherViews.slice(0, 1));
await folderService.getAllDecryptedFromState();
const actual = await exportService.getExport("json");
@ -279,7 +279,7 @@ describe("VaultExportService", () => {
});
it("exported encrypted json contains folders", async () => {
cipherService.getAll().resolves(UserCipherDomains.slice(0, 1));
cipherService.getAll.mockResolvedValue(UserCipherDomains.slice(0, 1));
await folderService.getAllFromState();
const actual = await exportService.getExport("encrypted_json");

14
package-lock.json generated
View File

@ -79,7 +79,6 @@
"@compodoc/compodoc": "1.1.21",
"@electron/notarize": "1.2.4",
"@electron/rebuild": "3.2.13",
"@fluffy-spoon/substitute": "1.208.0",
"@ngtools/webpack": "15.2.9",
"@storybook/addon-a11y": "7.3.0",
"@storybook/addon-actions": "7.3.0",
@ -6011,19 +6010,6 @@
"integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==",
"dev": true
},
"node_modules/@fluffy-spoon/substitute": {
"version": "1.208.0",
"resolved": "https://registry.npmjs.org/@fluffy-spoon/substitute/-/substitute-1.208.0.tgz",
"integrity": "sha512-BU5vKRoK4OYlKzDtyg4HbtWnUNLOvV0ntqEZIphz+mq2G0HlVFywwJ7M+FbIcnJVDbUReS01FyL5x8R01r7zBg==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/substitute-js#section-contribute"
}
},
"node_modules/@foliojs-fork/fontkit": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz",

View File

@ -44,7 +44,6 @@
"@compodoc/compodoc": "1.1.21",
"@electron/notarize": "1.2.4",
"@electron/rebuild": "3.2.13",
"@fluffy-spoon/substitute": "1.208.0",
"@ngtools/webpack": "15.2.9",
"@storybook/addon-a11y": "7.3.0",
"@storybook/addon-actions": "7.3.0",