mirror of
https://github.com/ajurna/cbwebreader.git
synced 2026-11-02 18:46:53 +00:00
- Added Vitest configuration (`vitest.config.mjs`) for frontend unit testing with a `jsdom` environment. - Migrated and rewrote key frontend tests (`api.test.js`, `comiclist.test.js`, `comicreader.test.js`, `historytable.test.js`, `router.test.js`, `store.test.js`) to Vitest, improving maintainability. - Removed `tsconfig.json` and related TypeScript loaders from `webpack` configurations (`webpack.dev.js`, `webpack.prod.js`). - Adjusted `eslint.config.mjs` to support Vitest tests. - Updated `package-lock.json` to include Vitest, test utilities (`jsdom`, `@vue/test-utils`), and other associated packages. - Updated Nginx configuration for better compression on assets (`gzip` adjustments). - Enhanced `entrypoint.sh` to include first-run library indexing (`--if-empty` argument). - Simplified the `scan_comics` command with a dedicated test for empty database scans. This transition improves testing workflows and backend/frontend synergy while removing unused TypeScript dependencies.
115 lines
3.6 KiB
JavaScript
115 lines
3.6 KiB
JavaScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
const { api, store, router, interceptors } = vi.hoisted(() => {
|
|
const interceptors = { request: [], response: [] }
|
|
const api = {
|
|
request: vi.fn(),
|
|
interceptors: {
|
|
request: { use: (fn) => interceptors.request.push(fn) },
|
|
response: { use: (ok, err) => interceptors.response.push({ ok, err }) },
|
|
},
|
|
}
|
|
return {
|
|
api,
|
|
store: { dispatch: vi.fn(), commit: vi.fn() },
|
|
router: { push: vi.fn() },
|
|
interceptors,
|
|
}
|
|
})
|
|
|
|
vi.mock('axios', () => ({ default: { create: () => api } }))
|
|
vi.mock('@/store', () => ({ default: store }))
|
|
vi.mock('@/router', () => ({ default: router }))
|
|
|
|
import axiosApi from '@/api'
|
|
|
|
const requestInterceptor = interceptors.request[0]
|
|
const { ok: successHandler, err: errorHandler } = interceptors.response[0]
|
|
|
|
function error401(url) {
|
|
return { response: { status: 401 }, config: { url } }
|
|
}
|
|
|
|
beforeEach(() => {
|
|
document.cookie = 'csrftoken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
|
|
api.request.mockReset()
|
|
store.dispatch.mockReset()
|
|
store.commit.mockReset()
|
|
router.push.mockReset()
|
|
})
|
|
|
|
describe('request interceptor', () => {
|
|
it('uses the cookie session, so it sends no Authorization header', () => {
|
|
expect(axiosApi).toBe(api)
|
|
expect(requestInterceptor({ headers: {} }).headers).not.toHaveProperty('Authorization')
|
|
})
|
|
|
|
it('adds the CSRF header when a csrftoken cookie exists', () => {
|
|
document.cookie = 'csrftoken=abc123; path=/'
|
|
|
|
expect(requestInterceptor({ headers: {} }).headers['X-CSRFToken']).toBe('abc123')
|
|
})
|
|
|
|
it('leaves the headers untouched when there is no CSRF cookie', () => {
|
|
expect(requestInterceptor({ headers: {} }).headers).not.toHaveProperty('X-CSRFToken')
|
|
})
|
|
})
|
|
|
|
describe('response interceptor', () => {
|
|
it('passes successful responses through', async () => {
|
|
const response = { data: [1, 2] }
|
|
|
|
expect(await successHandler(response)).toBe(response)
|
|
expect(store.dispatch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('refreshes the session and retries once after a 401', async () => {
|
|
store.dispatch.mockResolvedValue(undefined)
|
|
api.request.mockResolvedValue({ data: 'retried' })
|
|
const error = error401('/api/browse/')
|
|
|
|
expect(await errorHandler(error)).toEqual({ data: 'retried' })
|
|
expect(store.dispatch).toHaveBeenCalledWith('refreshToken')
|
|
expect(api.request).toHaveBeenCalledWith(error.config)
|
|
expect(error.config._retried).toBe(true)
|
|
})
|
|
|
|
it('does not retry a request that has already been retried', async () => {
|
|
const error = error401('/api/browse/')
|
|
error.config._retried = true
|
|
|
|
await expect(errorHandler(error)).rejects.toBe(error)
|
|
expect(store.dispatch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('does not retry the token endpoints, which would loop', async () => {
|
|
const error = error401('/api/token/refresh/')
|
|
|
|
await expect(errorHandler(error)).rejects.toBe(error)
|
|
expect(store.dispatch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('logs out and goes to the login page when the refresh fails', async () => {
|
|
store.dispatch.mockRejectedValue(new Error('expired'))
|
|
const error = error401('/api/browse/')
|
|
|
|
await expect(errorHandler(error)).rejects.toBe(error)
|
|
|
|
expect(store.commit).toHaveBeenCalledWith('logOut')
|
|
expect(router.push).toHaveBeenCalledWith({
|
|
name: 'login',
|
|
query: {
|
|
next: '/api/browse/',
|
|
error: 'Your session has expired. Please log in again.',
|
|
},
|
|
})
|
|
})
|
|
|
|
it('leaves other error statuses alone', async () => {
|
|
const error = { response: { status: 403 }, config: { url: '/api/browse/' } }
|
|
|
|
await expect(errorHandler(error)).rejects.toBe(error)
|
|
expect(store.dispatch).not.toHaveBeenCalled()
|
|
})
|
|
})
|