this repo has no description
1import { describe, it, expect, beforeEach } from 'vitest'
2import { render, screen, fireEvent, waitFor } from '@testing-library/svelte'
3import Dashboard from '../routes/Dashboard.svelte'
4import {
5 setupFetchMock,
6 mockEndpoint,
7 jsonResponse,
8 mockData,
9 clearMocks,
10 setupAuthenticatedUser,
11 setupUnauthenticatedUser,
12} from './mocks'
13
14const STORAGE_KEY = 'bspds_session'
15
16describe('Dashboard', () => {
17 beforeEach(() => {
18 clearMocks()
19 setupFetchMock()
20 })
21
22 describe('authentication guard', () => {
23 it('redirects to login when not authenticated', async () => {
24 setupUnauthenticatedUser()
25 render(Dashboard)
26
27 await waitFor(() => {
28 expect(window.location.hash).toBe('#/login')
29 })
30 })
31
32 it('shows loading state while checking auth', () => {
33 render(Dashboard)
34
35 expect(screen.getByText(/loading/i)).toBeInTheDocument()
36 })
37 })
38
39 describe('authenticated view', () => {
40 beforeEach(() => {
41 setupAuthenticatedUser()
42 })
43
44 it('displays user account info and page structure', async () => {
45 render(Dashboard)
46
47 await waitFor(() => {
48 expect(screen.getByRole('heading', { name: /dashboard/i })).toBeInTheDocument()
49 expect(screen.getByRole('heading', { name: /account overview/i })).toBeInTheDocument()
50 expect(screen.getByText(/@testuser\.test\.bspds\.dev/)).toBeInTheDocument()
51 expect(screen.getByText(/did:web:test\.bspds\.dev:u:testuser/)).toBeInTheDocument()
52 expect(screen.getByText('test@example.com')).toBeInTheDocument()
53 expect(screen.getByText('Verified')).toBeInTheDocument()
54 expect(screen.getByText('Verified')).toHaveClass('badge', 'success')
55 })
56 })
57
58 it('displays unverified badge when email not confirmed', async () => {
59 setupAuthenticatedUser({ emailConfirmed: false })
60 render(Dashboard)
61
62 await waitFor(() => {
63 expect(screen.getByText('Unverified')).toBeInTheDocument()
64 expect(screen.getByText('Unverified')).toHaveClass('badge', 'warning')
65 })
66 })
67
68 it('displays all navigation cards', async () => {
69 render(Dashboard)
70
71 await waitFor(() => {
72 const navCards = [
73 { name: /app passwords/i, href: '#/app-passwords' },
74 { name: /invite codes/i, href: '#/invite-codes' },
75 { name: /account settings/i, href: '#/settings' },
76 { name: /notification preferences/i, href: '#/notifications' },
77 { name: /repository explorer/i, href: '#/repo' },
78 ]
79
80 for (const { name, href } of navCards) {
81 const card = screen.getByRole('link', { name })
82 expect(card).toBeInTheDocument()
83 expect(card).toHaveAttribute('href', href)
84 }
85 })
86 })
87 })
88
89 describe('logout functionality', () => {
90 beforeEach(() => {
91 setupAuthenticatedUser()
92 localStorage.setItem(STORAGE_KEY, JSON.stringify(mockData.session()))
93
94 mockEndpoint('com.atproto.server.deleteSession', () =>
95 jsonResponse({})
96 )
97 })
98
99 it('calls deleteSession and navigates to login on logout', async () => {
100 let deleteSessionCalled = false
101
102 mockEndpoint('com.atproto.server.deleteSession', () => {
103 deleteSessionCalled = true
104 return jsonResponse({})
105 })
106
107 render(Dashboard)
108
109 await waitFor(() => {
110 expect(screen.getByRole('button', { name: /sign out/i })).toBeInTheDocument()
111 })
112
113 await fireEvent.click(screen.getByRole('button', { name: /sign out/i }))
114
115 await waitFor(() => {
116 expect(deleteSessionCalled).toBe(true)
117 expect(window.location.hash).toBe('#/login')
118 })
119 })
120
121 it('clears session from localStorage after logout', async () => {
122 const storedSession = localStorage.getItem(STORAGE_KEY)
123 expect(storedSession).not.toBeNull()
124
125 render(Dashboard)
126
127 await waitFor(() => {
128 expect(screen.getByRole('button', { name: /sign out/i })).toBeInTheDocument()
129 })
130
131 await fireEvent.click(screen.getByRole('button', { name: /sign out/i }))
132
133 await waitFor(() => {
134 expect(localStorage.getItem(STORAGE_KEY)).toBeNull()
135 })
136 })
137 })
138})