Add Vitest coverage reporting to Woodpecker CI
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { apiRequest, buildKitchenApiUrl, getPath } from '../../src/api/client.js';
|
||||
|
||||
function createStore(overrides = {}) {
|
||||
return {
|
||||
config: {
|
||||
baseUrl: '',
|
||||
database: 'kitchen-db',
|
||||
...(overrides.config || {}),
|
||||
},
|
||||
session: {
|
||||
applicationKey: 'app-key',
|
||||
hasValidated: false,
|
||||
state: 'pending_validation',
|
||||
...(overrides.session || {}),
|
||||
},
|
||||
activeKitchen: {
|
||||
id: 'kitchen-1',
|
||||
...(overrides.activeKitchen || {}),
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('api/client', () => {
|
||||
let authFailureSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
authFailureSpy = vi.fn();
|
||||
globalThis.window = {
|
||||
location: {
|
||||
origin: 'https://app.local',
|
||||
},
|
||||
__loncApp: {
|
||||
handleAuthFailure: authFailureSpy,
|
||||
},
|
||||
};
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
delete globalThis.window;
|
||||
});
|
||||
|
||||
it('returns configured path constants', () => {
|
||||
expect(getPath('items')).toBe('kitchen/items');
|
||||
expect(getPath('userApplication')).toBe('user/application/');
|
||||
});
|
||||
|
||||
it('builds kitchen urls with encoded path segments and query values', () => {
|
||||
const store = createStore({
|
||||
config: {
|
||||
baseUrl: 'https://api.example.com',
|
||||
database: 'my db',
|
||||
},
|
||||
activeKitchen: {
|
||||
id: 'kitchen/01',
|
||||
},
|
||||
});
|
||||
|
||||
const url = buildKitchenApiUrl(store, 'kitchen/items/grouped', {
|
||||
search_name: 'Milk + eggs',
|
||||
expanded: 1,
|
||||
ignored: '',
|
||||
});
|
||||
|
||||
expect(url).toBe(
|
||||
'https://api.example.com/my%20db/kitchen/kitchen%2F01/kitchen/items/grouped?search_name=Milk+%2B+eggs&expanded=1',
|
||||
);
|
||||
});
|
||||
|
||||
it('sends json request data and auth header through fetch', async () => {
|
||||
const store = createStore();
|
||||
const fetchSpy = vi.fn(async () =>
|
||||
new Response(JSON.stringify({ ok: true }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
}),
|
||||
);
|
||||
vi.stubGlobal('fetch', fetchSpy);
|
||||
|
||||
const payload = await apiRequest(store, getPath('items'), {
|
||||
method: 'POST',
|
||||
body: {
|
||||
name: 'Rice',
|
||||
},
|
||||
query: {
|
||||
label: 1,
|
||||
},
|
||||
});
|
||||
|
||||
expect(payload).toEqual({ ok: true });
|
||||
|
||||
const [url, request] = fetchSpy.mock.calls[0];
|
||||
expect(url).toBe('/kitchen-db/kitchen/kitchen-1/kitchen/items?label=1');
|
||||
expect(request.method).toBe('POST');
|
||||
expect(request.body).toBe('{"name":"Rice"}');
|
||||
expect(request.headers.get('Accept')).toBe('application/json');
|
||||
expect(request.headers.get('Content-Type')).toBe('application/json');
|
||||
expect(request.headers.get('Authorization')).toBe('Bearer app-key');
|
||||
});
|
||||
|
||||
it('normalizes api error payload into ApiRequestError details', async () => {
|
||||
const store = createStore();
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
errors: {
|
||||
name: ['Required'],
|
||||
quantity: 'Must be positive',
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 400,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await expect(apiRequest(store, getPath('items'))).rejects.toMatchObject({
|
||||
name: 'ApiRequestError',
|
||||
status: 400,
|
||||
details: {
|
||||
name: 'Required',
|
||||
quantity: 'Must be positive',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('triggers auth failure handler after validated session receives auth errors', async () => {
|
||||
const store = createStore({
|
||||
session: {
|
||||
applicationKey: 'app-key',
|
||||
hasValidated: true,
|
||||
state: 'connected',
|
||||
},
|
||||
});
|
||||
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async () =>
|
||||
new Response(JSON.stringify({ detail: 'Unauthorized' }), {
|
||||
status: 401,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
await expect(apiRequest(store, getPath('items'))).rejects.toMatchObject({
|
||||
name: 'ApiRequestError',
|
||||
status: 401,
|
||||
});
|
||||
|
||||
expect(authFailureSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user