feat: add user selection flow

This commit is contained in:
Urban Modig
2026-07-24 20:21:33 +02:00
parent 9957383e88
commit 1ec7a72908
22 changed files with 778 additions and 40 deletions

View File

@ -1,21 +1,145 @@
import { render, screen } from '@testing-library/react'
import { afterEach, expect, test, vi } from 'vitest'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { afterEach, beforeEach, expect, test, vi } from 'vitest'
import App from './App'
const users = [
{
id: 'd56b54dd-31b0-4d71-8a10-82464be59a61',
name: 'Urban',
createdAt: '2026-07-24T10:15:30Z',
},
{
id: '2213a673-c859-462b-a031-7b68b6f01c73',
name: 'Anna',
createdAt: '2026-07-24T10:16:30Z',
},
]
beforeEach(() => {
window.localStorage.clear()
})
afterEach(() => {
cleanup()
vi.restoreAllMocks()
})
test('visar sidans rubrik', () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(JSON.stringify({ status: 'UP' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
}),
test('tom användarlista visar Skapa användare', async () => {
mockJsonResponse([])
render(<App />)
expect(await screen.findByRole('heading', { name: 'Skapa användare' })).toBeInTheDocument()
})
test('befintliga användare utan lokalt val visar Vem är du', async () => {
mockJsonResponse(users)
render(<App />)
expect(await screen.findByRole('heading', { name: 'Vem är du?' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Urban' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Anna' })).toBeInTheDocument()
})
test('val av användare lagrar id och visar startsidan', async () => {
mockJsonResponse(users)
render(<App />)
fireEvent.click(await screen.findByRole('button', { name: 'Urban' }))
expect(screen.getByRole('heading', { name: 'Hej Urban' })).toBeInTheDocument()
expect(window.localStorage.getItem('hemhub.activeUserId')).toBe(users[0].id)
})
test('skapad användare blir automatiskt aktiv', async () => {
const createdUser = users[0]
const fetchMock = vi.spyOn(globalThis, 'fetch')
fetchMock.mockResolvedValueOnce(jsonResponse([]))
fetchMock.mockResolvedValueOnce(jsonResponse(createdUser, 201))
render(<App />)
fireEvent.change(await screen.findByLabelText('Namn'), { target: { value: ' Urban ' } })
fireEvent.click(screen.getByRole('button', { name: 'Skapa användare' }))
expect(await screen.findByRole('heading', { name: 'Hej Urban' })).toBeInTheDocument()
expect(window.localStorage.getItem('hemhub.activeUserId')).toBe(createdUser.id)
expect(fetchMock).toHaveBeenLastCalledWith('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Urban' }),
})
})
test('skapandefel visas utan att namnet försvinner', async () => {
const fetchMock = vi.spyOn(globalThis, 'fetch')
fetchMock.mockResolvedValueOnce(jsonResponse([]))
fetchMock.mockResolvedValueOnce(
jsonResponse(
{
code: 'USER_NAME_ALREADY_EXISTS',
message: 'En användare med det namnet finns redan.',
},
409,
),
)
render(<App />)
expect(screen.getByRole('heading', { name: 'HemHub' })).toBeInTheDocument()
const input = await screen.findByLabelText('Namn')
fireEvent.change(input, { target: { value: 'Urban' } })
fireEvent.click(screen.getByRole('button', { name: 'Skapa användare' }))
expect(await screen.findByRole('alert')).toHaveTextContent(
'En användare med det namnet finns redan.',
)
expect(input).toHaveValue('Urban')
})
test('Byt användare rensar valt id och visar användarvalet', async () => {
window.localStorage.setItem('hemhub.activeUserId', users[0].id)
mockJsonResponse(users)
render(<App />)
fireEvent.click(await screen.findByRole('button', { name: 'Byt användare' }))
expect(screen.getByRole('heading', { name: 'Vem är du?' })).toBeInTheDocument()
expect(window.localStorage.getItem('hemhub.activeUserId')).toBeNull()
})
test('hämtningsfel visas som fel och kan återförsökas', async () => {
const fetchMock = vi.spyOn(globalThis, 'fetch')
fetchMock.mockRejectedValueOnce(new Error('Nätverksfel'))
fetchMock.mockResolvedValueOnce(jsonResponse(users))
render(<App />)
expect(await screen.findByText('Kunde inte ansluta till HemHub.')).toBeInTheDocument()
expect(screen.queryByRole('heading', { name: 'Skapa användare' })).not.toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'Försök igen' }))
expect(await screen.findByRole('heading', { name: 'Vem är du?' })).toBeInTheDocument()
expect(fetchMock).toHaveBeenCalledTimes(2)
})
test('ogiltigt lagrat användar-id rensas', async () => {
window.localStorage.setItem('hemhub.activeUserId', 'finns-inte')
mockJsonResponse(users)
render(<App />)
expect(await screen.findByRole('heading', { name: 'Vem är du?' })).toBeInTheDocument()
expect(window.localStorage.getItem('hemhub.activeUserId')).toBeNull()
})
function mockJsonResponse(body: unknown, status = 200) {
return vi.spyOn(globalThis, 'fetch').mockResolvedValue(jsonResponse(body, status))
}
function jsonResponse(body: unknown, status = 200) {
return new Response(JSON.stringify(body), {
status,
headers: { 'Content-Type': 'application/json' },
})
}