From 68b68d3efd7fa9733f28b6ecea91c41ca3ad7fdf Mon Sep 17 00:00:00 2001 From: KoloMl Date: Thu, 20 Feb 2025 23:52:26 +0400 Subject: [PATCH] Added initial implementation of mocks for chrome StorageArea, adding first tests for storage helper --- tests/lib/browser/StorageHelper.spec.ts | 40 ++++++++++++++ tests/mocks/ChromeEvent.ts | 9 ++++ tests/mocks/ChromeLocalStorageArea.ts | 5 ++ tests/mocks/ChromeStorageArea.ts | 71 +++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 tests/lib/browser/StorageHelper.spec.ts create mode 100644 tests/mocks/ChromeEvent.ts create mode 100644 tests/mocks/ChromeLocalStorageArea.ts create mode 100644 tests/mocks/ChromeStorageArea.ts diff --git a/tests/lib/browser/StorageHelper.spec.ts b/tests/lib/browser/StorageHelper.spec.ts new file mode 100644 index 0000000..c73cedd --- /dev/null +++ b/tests/lib/browser/StorageHelper.spec.ts @@ -0,0 +1,40 @@ +import ChromeStorageArea from "$tests/mocks/ChromeStorageArea"; +import StorageHelper from "$lib/browser/StorageHelper"; +import { expect } from "vitest"; + +describe('StorageHelper', () => { + let storageAreaMock: ChromeStorageArea; + let storageHelper: StorageHelper; + + beforeEach(() => { + storageAreaMock = new ChromeStorageArea(); + storageHelper = new StorageHelper(storageAreaMock); + }); + + it("should return value when data exists", async () => { + const key = 'existingKey'; + const value = 'test value'; + + storageAreaMock.insertMockedData({[key]: value}); + + expect(await storageHelper.read(key)).toBe(value); + }); + + it('should return default when data is not present', async () => { + const fallbackValue = 'fallback'; + + expect(await storageHelper.read('nonexistent', fallbackValue)).toBe(fallbackValue); + }); + + it('should treat falsy values as existing values', async () => { + const falsyValues = [false, '', 0]; + const key = 'testedKey'; + const fallbackValue = 'fallback'; + + for (let testedValue of falsyValues) { + storageAreaMock.insertMockedData({[key]: testedValue}); + + expect(await storageHelper.read(key, fallbackValue)).toBe(testedValue); + } + }); +}); diff --git a/tests/mocks/ChromeEvent.ts b/tests/mocks/ChromeEvent.ts new file mode 100644 index 0000000..7c3029b --- /dev/null +++ b/tests/mocks/ChromeEvent.ts @@ -0,0 +1,9 @@ +export default class ChromeEvent implements chrome.events.Event { + addListener = vi.fn(); + getRules = vi.fn(); + hasListener = vi.fn(); + removeRules = vi.fn(); + addRules = vi.fn(); + removeListener = vi.fn(); + hasListeners = vi.fn(); +} diff --git a/tests/mocks/ChromeLocalStorageArea.ts b/tests/mocks/ChromeLocalStorageArea.ts new file mode 100644 index 0000000..8aa4f77 --- /dev/null +++ b/tests/mocks/ChromeLocalStorageArea.ts @@ -0,0 +1,5 @@ +import ChromeStorageArea from "$tests/mocks/ChromeStorageArea"; + +export class ChromeLocalStorageArea extends ChromeStorageArea implements chrome.storage.LocalStorageArea { + QUOTA_BYTES = 100000; +} diff --git a/tests/mocks/ChromeStorageArea.ts b/tests/mocks/ChromeStorageArea.ts new file mode 100644 index 0000000..1372fd3 --- /dev/null +++ b/tests/mocks/ChromeStorageArea.ts @@ -0,0 +1,71 @@ +import ChromeEvent from "./ChromeEvent"; + +type ChangedEventCallback = (changes: chrome.storage.StorageChange) => void + +export default class ChromeStorageArea implements chrome.storage.StorageArea { + #mockedData: Record = {}; + + getBytesInUse = vi.fn(); + clear = vi.fn((): Promise => { + return new Promise(resolve => { + this.#mockedData = {}; + resolve(); + }) + }); + set = vi.fn((...args: any[]): Promise => { + return new Promise((resolve, reject) => { + this.#mockedData = Object.assign(this.#mockedData, args[0]); + resolve(); + }) + }); + remove = vi.fn((...args: any[]): Promise => { + return new Promise((resolve, reject) => { + const key = args[0]; + + if (typeof key === 'string') { + delete this.#mockedData[key]; + resolve(); + } + + reject(new Error('This behavior is not mocked!')); + }); + }); + get = vi.fn((...args: any[]) => { + return new Promise((resolve, reject) => { + const key = args[0]; + + if (!key) { + resolve(this.#mockedData); + return; + } + + if (typeof key === 'string') { + resolve({[key]: this.#mockedData[key]}); + return; + } + + if (Array.isArray(key)) { + resolve( + (key as string[]).reduce((entries, key) => { + entries[key] = this.#mockedData[key]; + return entries; + }, {} as Record) + ); + return; + } + + reject(new Error('This behavior is not implemented by the mock.')); + }); + }); + setAccessLevel = vi.fn(); + onChanged = new ChromeEvent(); + getKeys = vi.fn(); + + insertMockedData(data: Record) { + this.#mockedData = data; + } + + get mockedData(): Record { + return this.#mockedData; + } +}