feat: router, tab shell, setup and login pages
This commit is contained in:
99
web/test/router.test.tsx
Normal file
99
web/test/router.test.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import App from '../src/App'
|
||||
|
||||
const fetchMock = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.mockReset()
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
window.history.replaceState(null, '', '/')
|
||||
})
|
||||
|
||||
function json(status: number, body: unknown) {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
})
|
||||
}
|
||||
|
||||
function loggedInServer() {
|
||||
fetchMock.mockImplementation((url: string) => {
|
||||
if (url === '/api/setup') return Promise.resolve(json(200, { needed: false }))
|
||||
if (url === '/api/me') return Promise.resolve(json(200, { user: { id: 1, username: 'sam', isAdmin: true } }))
|
||||
return Promise.resolve(json(404, { error: 'not_found' }))
|
||||
})
|
||||
}
|
||||
|
||||
describe('routing', () => {
|
||||
it('shows the setup form when the server needs setup', async () => {
|
||||
fetchMock.mockImplementation((url: string) =>
|
||||
url === '/api/setup' ? Promise.resolve(json(200, { needed: true })) : Promise.resolve(json(404, {}))
|
||||
)
|
||||
render(<App />)
|
||||
await waitFor(() => expect(screen.getByRole('heading', { name: /welcome/i })).toBeTruthy())
|
||||
})
|
||||
|
||||
it('shows the login form when unauthenticated', async () => {
|
||||
fetchMock.mockImplementation((url: string) => {
|
||||
if (url === '/api/setup') return Promise.resolve(json(200, { needed: false }))
|
||||
return Promise.resolve(json(401, { error: 'unauthorized' }))
|
||||
})
|
||||
render(<App />)
|
||||
await waitFor(() => expect(screen.getByRole('heading', { name: /sign in/i })).toBeTruthy())
|
||||
})
|
||||
|
||||
it('lands on Library with the tab bar when authenticated', async () => {
|
||||
loggedInServer()
|
||||
render(<App />)
|
||||
await waitFor(() => expect(screen.getByRole('tab', { name: /library/i })).toBeTruthy())
|
||||
for (const tab of ['Library', 'Scan', 'Add', 'Settings']) {
|
||||
expect(screen.getByRole('tab', { name: new RegExp(tab, 'i') })).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('login form authenticates and enters the app', async () => {
|
||||
fetchMock.mockImplementation((url: string) => {
|
||||
if (url === '/api/setup') return Promise.resolve(json(200, { needed: false }))
|
||||
if (url === '/api/me') return Promise.resolve(json(401, { error: 'unauthorized' }))
|
||||
if (url === '/api/login') {
|
||||
return Promise.resolve(json(200, { user: { id: 1, username: 'sam', isAdmin: true } }))
|
||||
}
|
||||
return Promise.resolve(json(404, {}))
|
||||
})
|
||||
render(<App />)
|
||||
await waitFor(() => expect(screen.getByLabelText(/username/i)).toBeTruthy())
|
||||
await userEvent.type(screen.getByLabelText(/username/i), 'sam')
|
||||
await userEvent.type(screen.getByLabelText(/password/i), 'password123')
|
||||
await userEvent.click(screen.getByRole('button', { name: /sign in/i }))
|
||||
await waitFor(() => expect(screen.getByRole('tab', { name: /library/i })).toBeTruthy())
|
||||
})
|
||||
|
||||
it('setup form creates the admin account and enters the app', async () => {
|
||||
fetchMock.mockImplementation((url: string, init?: RequestInit) => {
|
||||
if (url === '/api/setup' && (!init || !init.method || init.method === 'GET')) {
|
||||
return Promise.resolve(json(200, { needed: true }))
|
||||
}
|
||||
if (url === '/api/setup' && init?.method === 'POST') {
|
||||
return Promise.resolve(json(200, { user: { id: 1, username: 'boss', isAdmin: true } }))
|
||||
}
|
||||
return Promise.resolve(json(404, {}))
|
||||
})
|
||||
render(<App />)
|
||||
await waitFor(() => expect(screen.getByLabelText(/username/i)).toBeTruthy())
|
||||
await userEvent.type(screen.getByLabelText(/username/i), 'boss')
|
||||
await userEvent.type(screen.getByLabelText(/password/i), 'password123')
|
||||
await userEvent.click(screen.getByRole('button', { name: /create/i }))
|
||||
await waitFor(() => expect(screen.getByRole('tab', { name: /library/i })).toBeTruthy())
|
||||
})
|
||||
|
||||
it('tab navigation switches pages', async () => {
|
||||
loggedInServer()
|
||||
render(<App />)
|
||||
await waitFor(() => expect(screen.getByRole('tab', { name: /settings/i })).toBeTruthy())
|
||||
await userEvent.click(screen.getByRole('tab', { name: /settings/i }))
|
||||
await waitFor(() => expect(screen.getByRole('heading', { name: /settings/i })).toBeTruthy())
|
||||
expect(screen.getByText(/account/i)).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -1,10 +1,31 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import App from '../src/App'
|
||||
|
||||
const fetchMock = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.mockReset()
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
window.history.replaceState(null, '', '/')
|
||||
})
|
||||
|
||||
function json(status: number, body: unknown) {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
})
|
||||
}
|
||||
|
||||
describe('App', () => {
|
||||
it('renders the app title', () => {
|
||||
it('renders the app title', async () => {
|
||||
fetchMock.mockImplementation((url: string) => {
|
||||
if (url === '/api/setup') return Promise.resolve(json(200, { needed: false }))
|
||||
if (url === '/api/me')
|
||||
return Promise.resolve(json(200, { user: { id: 1, username: 'sam', isAdmin: true } }))
|
||||
return Promise.resolve(json(404, {}))
|
||||
})
|
||||
render(<App />)
|
||||
expect(screen.getByRole('heading', { name: 'record-shop' })).toBeTruthy()
|
||||
await waitFor(() => expect(screen.getByRole('heading', { name: 'record-shop' })).toBeTruthy())
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user