mirror of
https://github.com/ajurna/cbwebreader.git
synced 2026-11-02 10:36:55 +00:00
**Add Vitest support and migrate frontend tests**
- 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.
This commit is contained in:
1 parent
ce809ed1a9
commit
cc557543c5
19 files changed
+2388
-586
No files matched your search
@@ -0,0 +1,167 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
|
||||
const api = vi.hoisted(() => ({ get: vi.fn(), put: vi.fn() }))
|
||||
const router = vi.hoisted(() => ({ push: vi.fn() }))
|
||||
const deck = vi.hoisted(() => ({
|
||||
initialize: vi.fn(() => Promise.resolve()),
|
||||
slide: vi.fn(),
|
||||
on: vi.fn(),
|
||||
off: vi.fn(),
|
||||
destroy: vi.fn(),
|
||||
isFirstSlide: vi.fn(() => false),
|
||||
isLastSlide: vi.fn(() => false),
|
||||
}))
|
||||
const hammertime = vi.hoisted(() => ({ on: vi.fn(), off: vi.fn() }))
|
||||
const reveal = vi.hoisted(() => vi.fn(() => deck))
|
||||
|
||||
vi.mock('@/api', () => ({ default: api }))
|
||||
vi.mock('reveal.js', () => ({ default: reveal }))
|
||||
vi.mock('hammerjs', () => ({ default: vi.fn(function () { return hammertime }) }))
|
||||
|
||||
import TheComicReader from '@/components/TheComicReader.vue'
|
||||
|
||||
const COMIC = {
|
||||
title: 'Alpha',
|
||||
last_read_page: 2,
|
||||
pages: 5,
|
||||
prev_comic: { route: 'browse', selector: 'p1' },
|
||||
next_comic: { route: 'browse', selector: 'n1' },
|
||||
}
|
||||
|
||||
const mounted = []
|
||||
|
||||
function mountReader() {
|
||||
const wrapper = mount(TheComicReader, {
|
||||
props: { selector: 'a1' },
|
||||
global: {
|
||||
mocks: { $router: router },
|
||||
stubs: { 'comic-paginate': true },
|
||||
},
|
||||
})
|
||||
mounted.push(wrapper)
|
||||
return wrapper
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
HTMLElement.prototype.scrollIntoView = vi.fn()
|
||||
api.get.mockReset()
|
||||
api.put.mockReset()
|
||||
api.get.mockResolvedValue({ data: { ...COMIC } })
|
||||
api.put.mockResolvedValue({ data: {} })
|
||||
router.push.mockReset()
|
||||
reveal.mockClear()
|
||||
for (const fn of Object.values(deck)) {
|
||||
if (typeof fn === 'function') fn.mockClear?.()
|
||||
}
|
||||
for (const fn of Object.values(hammertime)) fn.mockClear()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mounted.forEach((wrapper) => {
|
||||
if (!wrapper.vm?.isUnmounted) wrapper.unmount()
|
||||
})
|
||||
mounted.length = 0
|
||||
})
|
||||
|
||||
describe('TheComicReader', () => {
|
||||
it('loads the book, starts the deck and records the page it resumed on', async () => {
|
||||
const wrapper = mountReader()
|
||||
await flushPromises()
|
||||
|
||||
expect(api.get).toHaveBeenCalledWith('/api/read/a1/')
|
||||
expect(deck.initialize).toHaveBeenCalled()
|
||||
expect(wrapper.vm.current_page).toBe(2)
|
||||
expect(deck.slide).toHaveBeenCalledWith(2)
|
||||
expect(api.put).toHaveBeenCalledWith('/api/read/a1/set_page/', { page: 2 })
|
||||
})
|
||||
|
||||
it('keeps the page indicator in step with the deck', async () => {
|
||||
const wrapper = mountReader()
|
||||
await flushPromises()
|
||||
|
||||
wrapper.vm.setPage(4)
|
||||
expect(deck.slide).toHaveBeenCalledWith(3)
|
||||
await flushPromises()
|
||||
expect(wrapper.vm.paginate_page).toBe(4)
|
||||
})
|
||||
|
||||
it('debounces arrow keys so held keys do not spam the deck', async () => {
|
||||
const wrapper = mountReader()
|
||||
await flushPromises()
|
||||
|
||||
window.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }))
|
||||
window.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }))
|
||||
vi.advanceTimersByTime(50)
|
||||
|
||||
expect(deck.slide).toHaveBeenCalledTimes(2)
|
||||
expect(wrapper.vm.current_page).toBe(3)
|
||||
})
|
||||
|
||||
it('moves to the next book when the last slide is reached', async () => {
|
||||
const wrapper = mountReader()
|
||||
await flushPromises()
|
||||
|
||||
deck.isLastSlide.mockReturnValueOnce(true)
|
||||
wrapper.vm.keyPress({ key: 'ArrowRight' })
|
||||
|
||||
expect(router.push).toHaveBeenCalledWith({ name: 'browse', params: { selector: 'n1' } })
|
||||
})
|
||||
|
||||
it('moves to the previous book when the first slide is reached', async () => {
|
||||
const wrapper = mountReader()
|
||||
await flushPromises()
|
||||
|
||||
deck.isFirstSlide.mockReturnValueOnce(true)
|
||||
wrapper.vm.keyPress({ key: 'ArrowLeft' })
|
||||
|
||||
expect(router.push).toHaveBeenCalledWith({ name: 'browse', params: { selector: 'p1' } })
|
||||
})
|
||||
|
||||
it('records the page again whenever the visible slide changes', async () => {
|
||||
mountReader()
|
||||
await flushPromises()
|
||||
|
||||
const handler = deck.on.mock.calls[0][1]
|
||||
api.put.mockClear()
|
||||
|
||||
handler({ indexh: 4 })
|
||||
|
||||
expect(api.put).toHaveBeenCalledWith('/api/read/a1/set_page/', { page: 4 })
|
||||
})
|
||||
|
||||
it('removes every listener and the deck when it is unmounted', async () => {
|
||||
const addSpy = vi.spyOn(window, 'addEventListener')
|
||||
const removeSpy = vi.spyOn(window, 'removeEventListener')
|
||||
|
||||
const wrapper = mountReader()
|
||||
await flushPromises()
|
||||
|
||||
expect(addSpy).toHaveBeenCalledWith('keyup', wrapper.vm.keyPressDebounce)
|
||||
|
||||
wrapper.unmount()
|
||||
|
||||
expect(removeSpy).toHaveBeenCalledWith('keyup', wrapper.vm.keyPressDebounce)
|
||||
expect(deck.off).toHaveBeenCalledWith('slidechanged', wrapper.vm.slideChanged)
|
||||
expect(deck.destroy).toHaveBeenCalled()
|
||||
expect(hammertime.off).toHaveBeenCalledWith('swipeleft')
|
||||
expect(hammertime.off).toHaveBeenCalledWith('swiperight')
|
||||
expect(hammertime.off).toHaveBeenCalledWith('tap')
|
||||
|
||||
addSpy.mockRestore()
|
||||
removeSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('does not act on a key press that was debounced just before unmounting', async () => {
|
||||
const wrapper = mountReader()
|
||||
await flushPromises()
|
||||
deck.slide.mockClear()
|
||||
|
||||
window.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }))
|
||||
wrapper.unmount()
|
||||
vi.advanceTimersByTime(100)
|
||||
|
||||
expect(deck.slide).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user