✅(service-worker) add unit tests
Add unit tests for: - ApiPlugin - RequestSerializer - SyncManager
This commit is contained in:
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { ApiPlugin } from '../ApiPlugin';
|
||||
import { RequestSerializer } from '../RequestSerializer';
|
||||
|
||||
const mockedGet = jest.fn().mockResolvedValue({});
|
||||
const mockedGetAllKeys = jest.fn().mockResolvedValue([]);
|
||||
const mockedPut = jest.fn().mockResolvedValue({});
|
||||
const mockedDelete = jest.fn().mockResolvedValue({});
|
||||
const mockedClose = jest.fn().mockResolvedValue({});
|
||||
const mockedOpendDB = jest.fn().mockResolvedValue({
|
||||
get: mockedGet,
|
||||
getAllKeys: mockedGetAllKeys,
|
||||
getAll: jest.fn().mockResolvedValue([]),
|
||||
put: mockedPut,
|
||||
delete: mockedDelete,
|
||||
clear: jest.fn().mockResolvedValue({}),
|
||||
close: mockedClose,
|
||||
});
|
||||
|
||||
jest.mock('idb', () => ({
|
||||
...jest.requireActual('idb'),
|
||||
openDB: () => mockedOpendDB(),
|
||||
}));
|
||||
|
||||
describe('ApiPlugin', () => {
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
['doc-item', 'doc-list'].forEach((type) => {
|
||||
it(`calls fetchDidSucceed with type ${type} and status 200`, async () => {
|
||||
const apiPlugin = new ApiPlugin({
|
||||
tableName: type as any,
|
||||
type: 'list',
|
||||
syncManager: jest.fn() as any,
|
||||
});
|
||||
|
||||
const body = { lastName: 'Doe' };
|
||||
const bodyBuffer = RequestSerializer.objectToArrayBuffer(body);
|
||||
|
||||
const response = await apiPlugin.fetchDidSucceed?.({
|
||||
request: {
|
||||
url: 'test-url',
|
||||
body,
|
||||
} as unknown as Request,
|
||||
response: new Response(bodyBuffer, {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}),
|
||||
} as any);
|
||||
|
||||
expect(mockedPut).toHaveBeenCalledWith(type, body, 'test-url');
|
||||
expect(mockedClose).toHaveBeenCalled();
|
||||
expect(response?.status).toBe(200);
|
||||
});
|
||||
|
||||
it(`calls fetchDidSucceed with type ${type} and status other that 200`, async () => {
|
||||
const apiPlugin = new ApiPlugin({
|
||||
tableName: type as any,
|
||||
type: 'list',
|
||||
syncManager: jest.fn() as any,
|
||||
});
|
||||
|
||||
const body = { lastName: 'Doe' };
|
||||
const bodyBuffer = RequestSerializer.objectToArrayBuffer(body);
|
||||
|
||||
const response = await apiPlugin.fetchDidSucceed?.({
|
||||
request: {
|
||||
url: 'test-url',
|
||||
body,
|
||||
} as unknown as Request,
|
||||
response: new Response(bodyBuffer, {
|
||||
status: 400,
|
||||
statusText: 'OK',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}),
|
||||
} as any);
|
||||
|
||||
expect(mockedPut).not.toHaveBeenCalled();
|
||||
expect(response?.status).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
[
|
||||
{ type: 'update', withClone: true },
|
||||
{ type: 'delete', withClone: true },
|
||||
{ type: 'create', withClone: true },
|
||||
{ type: 'list', withClone: false },
|
||||
{ type: 'item', withClone: false },
|
||||
].forEach(({ type, withClone }) => {
|
||||
it(`calls requestWillFetch with type ${type}`, async () => {
|
||||
const mockedSync = jest.fn().mockResolvedValue({});
|
||||
|
||||
const apiPlugin = new ApiPlugin({
|
||||
type: 'update',
|
||||
syncManager: {
|
||||
sync: () => mockedSync(),
|
||||
} as any,
|
||||
});
|
||||
|
||||
const mockedClone = jest.fn().mockResolvedValue({});
|
||||
const requestInit = {
|
||||
request: {
|
||||
url: 'test-url',
|
||||
clone: () => mockedClone(),
|
||||
} as unknown as Request,
|
||||
} as any;
|
||||
const request = await apiPlugin.requestWillFetch?.(requestInit);
|
||||
|
||||
if (withClone) {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(mockedClone).toHaveBeenCalled();
|
||||
}
|
||||
|
||||
expect(mockedSync).toHaveBeenCalled();
|
||||
expect(request?.url).toBe('test-url');
|
||||
});
|
||||
});
|
||||
|
||||
it(`checks getApiCatchHandler`, async () => {
|
||||
const response = ApiPlugin.getApiCatchHandler();
|
||||
expect(await response.json()).toEqual({ error: 'Network is unavailable.' });
|
||||
});
|
||||
|
||||
[
|
||||
{ type: 'list', tableName: 'doc-list' },
|
||||
{ type: 'item', tableName: 'doc-item' },
|
||||
].forEach(({ type, tableName }) => {
|
||||
it(`checks handlerDidError with type ${type}`, async () => {
|
||||
const requestInit = {
|
||||
request: {
|
||||
url: 'test-url',
|
||||
} as unknown as Request,
|
||||
} as any;
|
||||
|
||||
const apiPlugin = new ApiPlugin({
|
||||
type: type as 'list' | 'item' | 'update' | 'create' | 'delete',
|
||||
tableName: tableName as 'doc-list' | 'doc-item',
|
||||
syncManager: {} as any,
|
||||
});
|
||||
|
||||
await apiPlugin.fetchDidFail?.({} as any);
|
||||
const response = await apiPlugin.handlerDidError?.(requestInit);
|
||||
expect(mockedGet).toHaveBeenCalledWith(tableName, 'test-url');
|
||||
expect(response?.status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
it(`checks handlerDidError with type update`, async () => {
|
||||
const requestInit = {
|
||||
request: {
|
||||
url: 'http://test.jest/documents/123456/',
|
||||
clone: () => mockedClone(),
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
arrayBuffer: () =>
|
||||
RequestSerializer.objectToArrayBuffer({
|
||||
test: 'test',
|
||||
}),
|
||||
json: () => ({
|
||||
test: 'test',
|
||||
}),
|
||||
} as unknown as Request,
|
||||
} as any;
|
||||
|
||||
const mockedClone = jest.fn().mockReturnValue(requestInit.request);
|
||||
|
||||
const mockedSync = jest.fn().mockResolvedValue({});
|
||||
const apiPlugin = new ApiPlugin({
|
||||
type: 'update',
|
||||
syncManager: {
|
||||
sync: () => mockedSync(),
|
||||
} as any,
|
||||
});
|
||||
|
||||
mockedGetAllKeys.mockResolvedValue(['http://test.jest/documents/?page=1']);
|
||||
mockedGet.mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
id: '123456',
|
||||
title: 'test',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await apiPlugin.requestWillFetch?.(requestInit);
|
||||
await apiPlugin.fetchDidFail?.({} as any);
|
||||
const response = await apiPlugin.handlerDidError?.(requestInit);
|
||||
expect(mockedGet).toHaveBeenCalledWith(
|
||||
'doc-item',
|
||||
'http://test.jest/documents/123456/',
|
||||
);
|
||||
expect(mockedGetAllKeys).toHaveBeenCalledWith('doc-list');
|
||||
|
||||
expect(mockedPut).toHaveBeenCalledWith(
|
||||
'doc-mutation',
|
||||
expect.objectContaining({
|
||||
key: expect.any(String),
|
||||
requestData: expect.objectContaining({
|
||||
url: 'http://test.jest/documents/123456/',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
expect.any(String),
|
||||
);
|
||||
expect(mockedPut).toHaveBeenCalledWith(
|
||||
'doc-item',
|
||||
{ results: [{ id: '123456', title: 'test' }], test: 'test' },
|
||||
'http://test.jest/documents/123456/',
|
||||
);
|
||||
expect(mockedPut).toHaveBeenCalledWith(
|
||||
'doc-list',
|
||||
{ results: [{ id: '123456', test: 'test', title: 'test' }] },
|
||||
'http://test.jest/documents/?page=1',
|
||||
);
|
||||
|
||||
expect(mockedPut).toHaveBeenCalledTimes(3);
|
||||
expect(mockedClose).toHaveBeenCalled();
|
||||
expect(response?.status).toBe(200);
|
||||
});
|
||||
|
||||
it(`checks handlerDidError with type delete`, async () => {
|
||||
const requestInit = {
|
||||
request: {
|
||||
url: 'http://test.jest/documents/123456/',
|
||||
clone: () => mockedClone(),
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
arrayBuffer: () =>
|
||||
RequestSerializer.objectToArrayBuffer({
|
||||
test: 'test',
|
||||
}),
|
||||
json: () => ({
|
||||
test: 'test',
|
||||
}),
|
||||
} as unknown as Request,
|
||||
} as any;
|
||||
|
||||
const mockedClone = jest.fn().mockReturnValue(requestInit.request);
|
||||
|
||||
const mockedSync = jest.fn().mockResolvedValue({});
|
||||
const apiPlugin = new ApiPlugin({
|
||||
type: 'delete',
|
||||
syncManager: {
|
||||
sync: () => mockedSync(),
|
||||
} as any,
|
||||
});
|
||||
|
||||
mockedGetAllKeys.mockResolvedValue(['http://test.jest/documents/?page=1']);
|
||||
mockedGet.mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
id: '123456',
|
||||
title: 'test',
|
||||
},
|
||||
{
|
||||
id: 'another-id',
|
||||
title: 'test-2',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await apiPlugin.requestWillFetch?.(requestInit);
|
||||
await apiPlugin.fetchDidFail?.({} as any);
|
||||
const response = await apiPlugin.handlerDidError?.(requestInit);
|
||||
expect(mockedDelete).toHaveBeenCalledWith(
|
||||
'doc-item',
|
||||
'http://test.jest/documents/123456/',
|
||||
);
|
||||
expect(mockedGetAllKeys).toHaveBeenCalledWith('doc-list');
|
||||
expect(mockedGet).toHaveBeenCalledWith(
|
||||
'doc-list',
|
||||
'http://test.jest/documents/?page=1',
|
||||
);
|
||||
|
||||
expect(mockedPut).toHaveBeenCalledWith(
|
||||
'doc-mutation',
|
||||
expect.objectContaining({
|
||||
key: expect.any(String),
|
||||
requestData: expect.objectContaining({
|
||||
url: 'http://test.jest/documents/123456/',
|
||||
}),
|
||||
}),
|
||||
expect.any(String),
|
||||
);
|
||||
expect(mockedPut).toHaveBeenCalledWith(
|
||||
'doc-list',
|
||||
expect.objectContaining({
|
||||
results: expect.arrayContaining([
|
||||
{
|
||||
id: 'another-id',
|
||||
title: 'test-2',
|
||||
},
|
||||
]),
|
||||
}),
|
||||
'http://test.jest/documents/?page=1',
|
||||
);
|
||||
|
||||
expect(mockedPut).toHaveBeenCalledTimes(2);
|
||||
expect(mockedClose).toHaveBeenCalled();
|
||||
expect(response?.status).toBe(204);
|
||||
});
|
||||
|
||||
it(`checks handlerDidError with type create`, async () => {
|
||||
Object.defineProperty(global, 'self', {
|
||||
value: {
|
||||
crypto: {
|
||||
randomUUID: jest.fn().mockReturnValue('444555'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const requestInit = {
|
||||
request: {
|
||||
url: 'http://test.jest/documents/',
|
||||
clone: () => mockedClone(),
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
arrayBuffer: () =>
|
||||
RequestSerializer.objectToArrayBuffer({
|
||||
title: 'my new doc',
|
||||
}),
|
||||
json: () => ({
|
||||
title: 'my new doc',
|
||||
}),
|
||||
} as unknown as Request,
|
||||
} as any;
|
||||
|
||||
const mockedClone = jest.fn().mockReturnValue(requestInit.request);
|
||||
|
||||
const mockedSync = jest.fn().mockResolvedValue({});
|
||||
const apiPlugin = new ApiPlugin({
|
||||
type: 'create',
|
||||
syncManager: {
|
||||
sync: () => mockedSync(),
|
||||
} as any,
|
||||
});
|
||||
|
||||
mockedGetAllKeys.mockResolvedValue(['http://test.jest/documents/?page=1']);
|
||||
mockedGet.mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
id: '123456',
|
||||
title: 'test',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await apiPlugin.requestWillFetch?.(requestInit);
|
||||
await apiPlugin.fetchDidFail?.({} as any);
|
||||
const response = await apiPlugin.handlerDidError?.(requestInit);
|
||||
expect(mockedPut).toHaveBeenCalledWith(
|
||||
'doc-mutation',
|
||||
expect.objectContaining({
|
||||
key: expect.any(String),
|
||||
requestData: expect.any(Object),
|
||||
}),
|
||||
expect.any(String),
|
||||
);
|
||||
expect(mockedPut).toHaveBeenCalledWith(
|
||||
'doc-item',
|
||||
expect.objectContaining({
|
||||
title: 'my new doc',
|
||||
}),
|
||||
'http://test.jest/documents/444555/',
|
||||
);
|
||||
expect(mockedPut).toHaveBeenCalledWith(
|
||||
'doc-list',
|
||||
expect.objectContaining({
|
||||
results: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: '444555',
|
||||
title: 'my new doc',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
'http://test.jest/documents/?page=1',
|
||||
);
|
||||
expect(mockedGetAllKeys).toHaveBeenCalledWith('doc-list');
|
||||
expect(mockedGet).toHaveBeenCalledWith(
|
||||
'doc-list',
|
||||
'http://test.jest/documents/?page=1',
|
||||
);
|
||||
expect(mockedPut).toHaveBeenCalledTimes(3);
|
||||
expect(mockedClose).toHaveBeenCalled();
|
||||
expect(response?.status).toBe(201);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { RequestSerializer } from '../RequestSerializer';
|
||||
|
||||
describe('RequestSerializer', () => {
|
||||
it('checks RequestSerializer.fromRequest', async () => {
|
||||
const request = new Request('http://test.jest', {
|
||||
method: 'GET',
|
||||
referrer: 'http://test.jest/referer',
|
||||
referrerPolicy: 'no-referrer',
|
||||
mode: 'cors',
|
||||
credentials: 'omit',
|
||||
cache: 'default',
|
||||
redirect: 'follow',
|
||||
integrity: 'integrity',
|
||||
keepalive: true,
|
||||
});
|
||||
|
||||
const requestSerializer = await RequestSerializer.fromRequest(request);
|
||||
|
||||
expect(requestSerializer.toObject()).toStrictEqual({
|
||||
cache: 'default',
|
||||
credentials: 'omit',
|
||||
headers: {},
|
||||
integrity: 'integrity',
|
||||
keepalive: true,
|
||||
method: 'GET',
|
||||
mode: 'cors',
|
||||
redirect: 'follow',
|
||||
referrer: 'http://test.jest/referer',
|
||||
referrerPolicy: 'no-referrer',
|
||||
url: 'http://test.jest/',
|
||||
});
|
||||
|
||||
expect(requestSerializer.toRequest()).toBeInstanceOf(Request);
|
||||
expect(requestSerializer.clone()).toBeInstanceOf(RequestSerializer);
|
||||
});
|
||||
|
||||
it('checks RequestSerializer.arrayBufferToString', async () => {
|
||||
const request = new Request('http://test.jest', {
|
||||
body: JSON.stringify({ test: 'test' }),
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
const body = await request.clone().arrayBuffer();
|
||||
const bodyString = RequestSerializer.arrayBufferToString(body);
|
||||
|
||||
expect(bodyString).toBe(JSON.stringify({ test: 'test' }));
|
||||
});
|
||||
|
||||
it('checks RequestSerializer.arrayBufferToJson', async () => {
|
||||
const request = new Request('http://test.jest', {
|
||||
body: JSON.stringify({ test: 'test' }),
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
const body = await request.clone().arrayBuffer();
|
||||
const bodyJson = RequestSerializer.arrayBufferToJson(body);
|
||||
|
||||
expect(bodyJson).toStrictEqual({ test: 'test' });
|
||||
});
|
||||
|
||||
it('checks RequestSerializer.stringToArrayBuffer', () => {
|
||||
const bodyString = RequestSerializer.stringToArrayBuffer(
|
||||
JSON.stringify({ test: 'test' }),
|
||||
);
|
||||
|
||||
expect(bodyString).toBeInstanceOf(ArrayBuffer);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { SyncManager } from '../SyncManager';
|
||||
|
||||
const mockedSleep = jest.fn();
|
||||
jest.mock('@/utils/system', () => ({
|
||||
sleep: jest.fn().mockImplementation((ms) => mockedSleep(ms)),
|
||||
}));
|
||||
|
||||
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
describe('SyncManager', () => {
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
it('checks SyncManager no sync to do', async () => {
|
||||
const toSync = jest.fn();
|
||||
const hasSyncToDo = jest.fn().mockResolvedValue(false);
|
||||
new SyncManager(toSync, hasSyncToDo);
|
||||
|
||||
await delay(100);
|
||||
|
||||
expect(hasSyncToDo).toHaveBeenCalled();
|
||||
expect(toSync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('checks SyncManager sync to do', async () => {
|
||||
const toSync = jest.fn();
|
||||
const hasSyncToDo = jest.fn().mockResolvedValue(true);
|
||||
new SyncManager(toSync, hasSyncToDo);
|
||||
|
||||
await delay(100);
|
||||
|
||||
expect(hasSyncToDo).toHaveBeenCalled();
|
||||
expect(toSync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('checks SyncManager sync to do trigger error', async () => {
|
||||
jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const toSync = jest.fn().mockRejectedValue(new Error('error'));
|
||||
const hasSyncToDo = jest.fn().mockResolvedValue(true);
|
||||
new SyncManager(toSync, hasSyncToDo);
|
||||
|
||||
await delay(100);
|
||||
|
||||
expect(hasSyncToDo).toHaveBeenCalled();
|
||||
expect(toSync).toHaveBeenCalled();
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
'SW-DEV: SyncManager.sync failed:',
|
||||
new Error('error'),
|
||||
);
|
||||
});
|
||||
|
||||
it('checks SyncManager multiple sync to do', async () => {
|
||||
const toSync = jest.fn().mockReturnValue(delay(200));
|
||||
const hasSyncToDo = jest.fn().mockResolvedValue(true);
|
||||
const syncManager = new SyncManager(toSync, hasSyncToDo);
|
||||
|
||||
await syncManager.sync();
|
||||
|
||||
expect(hasSyncToDo).toHaveBeenCalled();
|
||||
expect(mockedSleep).toHaveBeenCalledWith(300);
|
||||
expect(mockedSleep).toHaveBeenCalledTimes(15);
|
||||
expect(toSync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user