Files
ajurna 80566e657e **Add continuous reading mode and enhance accessibility in TheComicReader**
- Introduced a continuous reading mode ("scroll"), allowing users to read without pagination.
- Implemented a toggle in the UI to switch between "paged" and "continuous" modes, with preferences saved in `localStorage`.
- Enhanced keyboard navigation by throttling repeated actions and ignoring irrelevant keypresses during form interactions.
- Integrated `IntersectionObserver` to handle lazy-loading of images and dynamically extending rendered content in continuous mode.
- Updated tests to validate both reading modes, ensuring proper state management and observer handling.
- Adjusted animations and transitions for smoother visual feedback in both modes, respecting users' motion-reduction preferences.
2026-10-16 09:30:46 +01:00

128 lines
4.1 KiB
JavaScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
const { api, router } = vi.hoisted(() => ({
api: { get: vi.fn(), post: vi.fn() },
router: { currentRoute: { value: { query: {}, fullPath: '/account' } }, push: vi.fn() },
}))
vi.mock('@/api', () => ({ default: api }))
vi.mock('@/router', () => ({ default: router }))
import store from '@/store'
beforeEach(() => {
localStorage.clear()
store.commit('logOut')
api.get.mockReset()
api.post.mockReset()
router.push.mockReset()
})
describe('session state', () => {
it('is not authenticated when there is no stored profile', () => {
expect(store.getters.isAuthenticated).toBe(false)
expect(store.getters.is_superuser).toBe(false)
})
it('keeps the profile in localStorage so a reload is not a blank page', () => {
store.commit('updateUser', { id: 1, is_superuser: false })
expect(store.getters.isAuthenticated).toBe(true)
expect(JSON.parse(localStorage.getItem('u'))).toEqual({ id: 1, is_superuser: false })
})
it('logOut clears both the profile and localStorage', () => {
store.commit('updateUser', { id: 1, is_superuser: true })
store.commit('logOut')
expect(store.getters.isAuthenticated).toBe(false)
expect(localStorage.getItem('u')).toBeNull()
})
})
describe('reader mode', () => {
it('starts in paged mode when nothing is stored', async () => {
vi.resetModules()
const fresh = (await import('@/store')).default
expect(fresh.state.reader_mode).toBe('paged')
})
it('keeps the chosen mode in localStorage', () => {
store.commit('setReaderMode', 'scroll')
expect(store.state.reader_mode).toBe('scroll')
expect(localStorage.getItem('reader_mode')).toBe('scroll')
})
it('ignores a mode it does not recognise', () => {
store.commit('setReaderMode', 'scroll')
store.commit('setReaderMode', 'banana')
expect(store.state.reader_mode).toBe('scroll')
expect(localStorage.getItem('reader_mode')).toBe('scroll')
})
})
describe('restoreSession', () => {
it('refreshes the stored profile from the cookie session', async () => {
store.commit('updateUser', { id: 1, is_superuser: false })
api.get.mockResolvedValue({ data: { id: 1, is_superuser: true } })
await store.dispatch('restoreSession')
expect(api.get).toHaveBeenCalledWith('/api/account')
expect(store.getters.is_superuser).toBe(true)
})
it('logs out when the cookie no longer identifies the user', async () => {
store.commit('updateUser', { id: 1, is_superuser: false })
api.get.mockRejectedValue({ response: { status: 401 } })
await store.dispatch('restoreSession')
expect(store.getters.isAuthenticated).toBe(false)
expect(router.push).toHaveBeenCalledWith({ name: 'login' })
})
it('does nothing when there is no stored profile', async () => {
await store.dispatch('restoreSession')
expect(api.get).not.toHaveBeenCalled()
})
})
describe('token handling', () => {
it('rejects and logs out when the refresh cookie is no longer valid', async () => {
store.commit('updateUser', { id: 1, is_superuser: false })
api.post.mockRejectedValue({ response: { status: 401 } })
await expect(store.dispatch('refreshToken')).rejects.toBeTruthy()
expect(store.getters.isAuthenticated).toBe(false)
expect(router.push).toHaveBeenCalledWith(expect.objectContaining({ name: 'login' }))
})
it('does not call the API when there is no session', async () => {
await expect(store.dispatch('refreshToken')).rejects.toBeTruthy()
expect(api.post).not.toHaveBeenCalled()
})
it('does not blacklist a token for a session that does not exist', async () => {
await store.dispatch('logout')
expect(api.post).not.toHaveBeenCalled()
expect(store.getters.isAuthenticated).toBe(false)
})
it('blacklists the token when a session existed', async () => {
store.commit('updateUser', { id: 1, is_superuser: false })
api.post.mockResolvedValue({ data: {} })
await store.dispatch('logout')
expect(api.post).toHaveBeenCalledWith('/api/token/blacklist/', {})
expect(store.getters.isAuthenticated).toBe(false)
})
})