Fix lint issues in unit test helpers
This commit is contained in:
@@ -59,7 +59,7 @@ describe('db/index initializeDatabase', () => {
|
|||||||
const { db } = await import('../../src/db');
|
const { db } = await import('../../src/db');
|
||||||
|
|
||||||
expect(version).toHaveBeenCalled();
|
expect(version).toHaveBeenCalled();
|
||||||
expect((version as any).mock.calls.length).toBeGreaterThanOrEqual(1);
|
expect(version.mock.calls.length).toBeGreaterThanOrEqual(1);
|
||||||
expect(db).toBeDefined();
|
expect(db).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { describe, expect, test, vi, beforeEach } from 'vitest';
|
import { describe, expect, test, vi, beforeEach } from 'vitest';
|
||||||
|
import type { StockFillDB } from '../../src/db';
|
||||||
import { makeMockDb } from '../utils/mockDb';
|
import { makeMockDb } from '../utils/mockDb';
|
||||||
|
|
||||||
vi.mock('uuid', () => ({ v4: vi.fn(() => 'mock-uuid') }));
|
vi.mock('uuid', () => ({ v4: vi.fn(() => 'mock-uuid') }));
|
||||||
@@ -11,15 +12,13 @@ describe('applyMigrations', () => {
|
|||||||
|
|
||||||
test('creates category for product name and updates product to use new id', async () => {
|
test('creates category for product name and updates product to use new id', async () => {
|
||||||
const db = makeMockDb();
|
const db = makeMockDb();
|
||||||
(db.categories.toArray as any)
|
db.categories.toArray
|
||||||
.mockResolvedValueOnce([])
|
.mockResolvedValueOnce([])
|
||||||
.mockResolvedValueOnce([{ id: 'mock-uuid', name: 'Beverages', created_at: 1, updated_at: 1 }]);
|
.mockResolvedValueOnce([{ id: 'mock-uuid', name: 'Beverages', created_at: 1, updated_at: 1 }]);
|
||||||
(db.products.toArray as any).mockResolvedValueOnce([
|
db.products.toArray.mockResolvedValueOnce([{ id: 'p1', category: 'Beverages', name: 'Cola' }]);
|
||||||
{ id: 'p1', category: 'Beverages', name: 'Cola' },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { applyMigrations } = await import('../../src/db/migrations');
|
const { applyMigrations } = await import('../../src/db/migrations');
|
||||||
await applyMigrations(db as any);
|
await applyMigrations(db as unknown as StockFillDB);
|
||||||
|
|
||||||
expect(db.categories.add).toHaveBeenCalledWith(
|
expect(db.categories.add).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ id: 'mock-uuid', name: 'Beverages' }),
|
expect.objectContaining({ id: 'mock-uuid', name: 'Beverages' }),
|
||||||
@@ -29,15 +28,13 @@ describe('applyMigrations', () => {
|
|||||||
|
|
||||||
test('skips update when product category already matches existing id', async () => {
|
test('skips update when product category already matches existing id', async () => {
|
||||||
const db = makeMockDb();
|
const db = makeMockDb();
|
||||||
(db.categories.toArray as any)
|
db.categories.toArray
|
||||||
.mockResolvedValueOnce([{ id: 'cat-1', name: 'Snacks' }])
|
.mockResolvedValueOnce([{ id: 'cat-1', name: 'Snacks' }])
|
||||||
.mockResolvedValueOnce([{ id: 'cat-1', name: 'Snacks' }]);
|
.mockResolvedValueOnce([{ id: 'cat-1', name: 'Snacks' }]);
|
||||||
(db.products.toArray as any).mockResolvedValueOnce([
|
db.products.toArray.mockResolvedValueOnce([{ id: 'p2', category: 'cat-1', name: 'Chips' }]);
|
||||||
{ id: 'p2', category: 'cat-1', name: 'Chips' },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { applyMigrations } = await import('../../src/db/migrations');
|
const { applyMigrations } = await import('../../src/db/migrations');
|
||||||
await applyMigrations(db as any);
|
await applyMigrations(db as unknown as StockFillDB);
|
||||||
|
|
||||||
expect(db.categories.add).not.toHaveBeenCalled();
|
expect(db.categories.add).not.toHaveBeenCalled();
|
||||||
expect(db.products.update).not.toHaveBeenCalled();
|
expect(db.products.update).not.toHaveBeenCalled();
|
||||||
@@ -45,16 +42,14 @@ describe('applyMigrations', () => {
|
|||||||
|
|
||||||
test('normalizes pickList categories names to ids', async () => {
|
test('normalizes pickList categories names to ids', async () => {
|
||||||
const db = makeMockDb();
|
const db = makeMockDb();
|
||||||
(db.categories.toArray as any)
|
db.categories.toArray
|
||||||
.mockResolvedValueOnce([{ id: 'cat-fruit', name: 'Fruit' }])
|
.mockResolvedValueOnce([{ id: 'cat-fruit', name: 'Fruit' }])
|
||||||
.mockResolvedValueOnce([{ id: 'cat-fruit', name: 'Fruit' }]);
|
.mockResolvedValueOnce([{ id: 'cat-fruit', name: 'Fruit' }]);
|
||||||
(db.products.toArray as any).mockResolvedValueOnce([]);
|
db.products.toArray.mockResolvedValueOnce([]);
|
||||||
(db.pickLists.toArray as any).mockResolvedValueOnce([
|
db.pickLists.toArray.mockResolvedValueOnce([{ id: 'pl-1', categories: ['Fruit'] }]);
|
||||||
{ id: 'pl-1', categories: ['Fruit'] },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { applyMigrations } = await import('../../src/db/migrations');
|
const { applyMigrations } = await import('../../src/db/migrations');
|
||||||
await applyMigrations(db as any);
|
await applyMigrations(db as unknown as StockFillDB);
|
||||||
|
|
||||||
expect(db.pickLists.update).toHaveBeenCalledWith('pl-1', { categories: ['cat-fruit'] });
|
expect(db.pickLists.update).toHaveBeenCalledWith('pl-1', { categories: ['cat-fruit'] });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,11 +3,18 @@ import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest';
|
|||||||
|
|
||||||
vi.mock('@zxing/browser', () => {
|
vi.mock('@zxing/browser', () => {
|
||||||
class MockReader {
|
class MockReader {
|
||||||
decodeFromVideoDevice = vi.fn(async (_device: any, _video: any, callback: any) => {
|
decodeFromVideoDevice = vi.fn(
|
||||||
callback({ getText: () => 'decoded-fallback' } as any);
|
async (
|
||||||
return { stop: vi.fn() } as any;
|
_device: string | undefined,
|
||||||
});
|
_video: HTMLVideoElement,
|
||||||
|
callback: (result: { getText: () => string }) => void,
|
||||||
|
) => {
|
||||||
|
callback({ getText: () => 'decoded-fallback' });
|
||||||
|
return { stop: vi.fn() };
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { BrowserMultiFormatReader: MockReader };
|
return { BrowserMultiFormatReader: MockReader };
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -15,11 +22,15 @@ describe('useBarcodeScanner', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
(globalThis as any).BarcodeDetector = undefined;
|
const globalWithBarcode = globalThis as typeof globalThis & {
|
||||||
|
BarcodeDetector?: new () => { detect: (source: ImageBitmap | HTMLCanvasElement) => Promise<Array<{ rawValue: string }>> };
|
||||||
|
};
|
||||||
|
globalWithBarcode.BarcodeDetector = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
delete (globalThis as any).BarcodeDetector;
|
const globalWithBarcode = globalThis as typeof globalThis & { BarcodeDetector?: unknown };
|
||||||
|
delete globalWithBarcode.BarcodeDetector;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('falls back to ZXing reader when BarcodeDetector is unavailable', async () => {
|
test('falls back to ZXing reader when BarcodeDetector is unavailable', async () => {
|
||||||
@@ -35,16 +46,22 @@ describe('useBarcodeScanner', () => {
|
|||||||
class MockBarcodeDetector {
|
class MockBarcodeDetector {
|
||||||
detect = detect;
|
detect = detect;
|
||||||
}
|
}
|
||||||
(globalThis as any).BarcodeDetector = MockBarcodeDetector as any;
|
const globalWithBarcode = globalThis as typeof globalThis & { BarcodeDetector?: typeof MockBarcodeDetector };
|
||||||
|
globalWithBarcode.BarcodeDetector = MockBarcodeDetector;
|
||||||
|
|
||||||
const play = vi.fn().mockResolvedValue(undefined);
|
const play = vi.fn().mockResolvedValue(undefined);
|
||||||
const getVideoTracks = vi.fn().mockReturnValue([{ stop: vi.fn() }]);
|
const getVideoTracks = vi.fn().mockReturnValue([{ stop: vi.fn() }]);
|
||||||
const getTracks = vi.fn().mockReturnValue([{ stop: vi.fn() }]);
|
const getTracks = vi.fn().mockReturnValue([{ stop: vi.fn() }]);
|
||||||
const stream = { getVideoTracks, getTracks } as any;
|
const stream = { getVideoTracks, getTracks };
|
||||||
navigator.mediaDevices = {
|
Object.defineProperty(navigator, 'mediaDevices', {
|
||||||
getUserMedia: vi.fn().mockResolvedValue(stream),
|
value: {
|
||||||
} as any;
|
getUserMedia: vi.fn().mockResolvedValue(stream),
|
||||||
(globalThis as any).createImageBitmap = vi.fn().mockResolvedValue({});
|
},
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
const createImageBitmap = vi.fn<[], Promise<ImageBitmap>>().mockResolvedValue({} as ImageBitmap);
|
||||||
|
const globalWithBitmap = globalThis as typeof globalThis & { createImageBitmap?: typeof createImageBitmap };
|
||||||
|
globalWithBitmap.createImageBitmap = createImageBitmap;
|
||||||
|
|
||||||
const { useBarcodeScanner } = await import('../../src/hooks/useBarcodeScanner');
|
const { useBarcodeScanner } = await import('../../src/hooks/useBarcodeScanner');
|
||||||
const { result } = renderHook(() => useBarcodeScanner());
|
const { result } = renderHook(() => useBarcodeScanner());
|
||||||
|
|||||||
+54
-22
@@ -1,45 +1,77 @@
|
|||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
|
|
||||||
export type TableMock<T = any> = {
|
type WhereClause<T> = {
|
||||||
toArray: ReturnType<typeof vi.fn>;
|
equals: ReturnType<typeof vi.fn<[T], { first: ReturnType<typeof vi.fn<[], Promise<T | undefined>>> }>>;
|
||||||
add: ReturnType<typeof vi.fn>;
|
|
||||||
update: ReturnType<typeof vi.fn>;
|
|
||||||
where: ReturnType<typeof vi.fn>;
|
|
||||||
filter: ReturnType<typeof vi.fn>;
|
|
||||||
count: ReturnType<typeof vi.fn>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createWhere = () => ({
|
type FilterClause<T> = {
|
||||||
equals: vi.fn().mockReturnValue({ first: vi.fn().mockResolvedValue(undefined) }),
|
first: ReturnType<typeof vi.fn<[], Promise<T | undefined>>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TableMock<T = unknown> = {
|
||||||
|
toArray: ReturnType<typeof vi.fn<[], Promise<T[]>>>;
|
||||||
|
add: ReturnType<typeof vi.fn<[T], Promise<unknown>>>;
|
||||||
|
update: ReturnType<typeof vi.fn<[string, Partial<T>], Promise<unknown>>>;
|
||||||
|
where: ReturnType<typeof vi.fn<[keyof T], WhereClause<T[keyof T]>>>;
|
||||||
|
filter: ReturnType<typeof vi.fn<[(item: T) => boolean], FilterClause<T>>>;
|
||||||
|
count: ReturnType<typeof vi.fn<[], Promise<number>>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createWhere = <T>(): WhereClause<T> => ({
|
||||||
|
equals: vi.fn<[T], { first: ReturnType<typeof vi.fn<[], Promise<T | undefined>>> }>().mockReturnValue({
|
||||||
|
first: vi.fn<[], Promise<T | undefined>>().mockResolvedValue(undefined),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const makeTableMock = <T = any>(items: T[] = []): TableMock<T> => ({
|
export const makeTableMock = <T = unknown>(items: T[] = []): TableMock<T> => ({
|
||||||
toArray: vi.fn().mockResolvedValue([...items]),
|
toArray: vi.fn<[], Promise<T[]>>().mockResolvedValue([...items]),
|
||||||
add: vi.fn().mockResolvedValue(undefined),
|
add: vi.fn<[T], Promise<unknown>>().mockResolvedValue(undefined),
|
||||||
update: vi.fn().mockResolvedValue(undefined),
|
update: vi.fn<[string, Partial<T>], Promise<unknown>>().mockResolvedValue(undefined),
|
||||||
where: vi.fn().mockImplementation(() => createWhere()),
|
where: vi.fn<[keyof T], WhereClause<T[keyof T]>>().mockImplementation(() => createWhere<T[keyof T]>()),
|
||||||
filter: vi.fn().mockReturnValue({ first: vi.fn().mockResolvedValue(undefined) }),
|
filter: vi
|
||||||
count: vi.fn().mockResolvedValue(0),
|
.fn<[(item: T) => boolean], FilterClause<T>>()
|
||||||
|
.mockReturnValue({ first: vi.fn<[], Promise<T | undefined>>().mockResolvedValue(undefined) }),
|
||||||
|
count: vi.fn<[], Promise<number>>().mockResolvedValue(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const makeMockDb = (overrides: Partial<Record<string, any>> = {}) => {
|
type MockDbOverrides = {
|
||||||
|
products: unknown[];
|
||||||
|
categories: unknown[];
|
||||||
|
pickLists: unknown[];
|
||||||
|
pickItems: unknown[];
|
||||||
|
importExportLogs: unknown[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type TransactionMock = ReturnType<typeof vi.fn<[string, ...unknown[]], Promise<unknown>>>;
|
||||||
|
|
||||||
|
type MockDb = {
|
||||||
|
products: TableMock;
|
||||||
|
categories: TableMock;
|
||||||
|
pickLists: TableMock;
|
||||||
|
pickItems: TableMock;
|
||||||
|
importExportLogs: TableMock;
|
||||||
|
transaction: TransactionMock;
|
||||||
|
open: ReturnType<typeof vi.fn<[], Promise<unknown>>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeMockDb = (overrides: Partial<MockDbOverrides> = {}): MockDb => {
|
||||||
const products = makeTableMock(overrides.products ?? []);
|
const products = makeTableMock(overrides.products ?? []);
|
||||||
const categories = makeTableMock(overrides.categories ?? []);
|
const categories = makeTableMock(overrides.categories ?? []);
|
||||||
const pickLists = makeTableMock(overrides.pickLists ?? []);
|
const pickLists = makeTableMock(overrides.pickLists ?? []);
|
||||||
const pickItems = makeTableMock(overrides.pickItems ?? []);
|
const pickItems = makeTableMock(overrides.pickItems ?? []);
|
||||||
const importExportLogs = makeTableMock(overrides.importExportLogs ?? []);
|
const importExportLogs = makeTableMock(overrides.importExportLogs ?? []);
|
||||||
|
|
||||||
const transaction = vi
|
const transaction = vi.fn<[string, ...unknown[]], Promise<unknown>>().mockImplementation(
|
||||||
.fn()
|
async (_mode, ...args) => {
|
||||||
.mockImplementation(async (_mode: any, ...args: any[]) => {
|
|
||||||
const cb = args[args.length - 1];
|
const cb = args[args.length - 1];
|
||||||
if (typeof cb === 'function') {
|
if (typeof cb === 'function') {
|
||||||
return cb();
|
return cb();
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const open = vi.fn().mockResolvedValue(undefined);
|
const open = vi.fn<[], Promise<unknown>>().mockResolvedValue(undefined);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
products,
|
products,
|
||||||
|
|||||||
Reference in New Issue
Block a user