forked from OurHike/OurHike
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathApp.test.tsx
More file actions
157 lines (124 loc) · 5.51 KB
/
Copy pathApp.test.tsx
File metadata and controls
157 lines (124 loc) · 5.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { render, screen, cleanup, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { get, set } from 'idb-keyval'
import App from './App'
import { PREFERENCES_KEY } from './lib/preferences'
import { DEFAULT_PREFERENCES } from './lib/userPreferences'
// The shell decides what a hiker sees, so what is worth testing here is the
// routing between screens and the honesty of what it shows before any data
// exists - not the screens themselves, which have their own tests.
vi.mock('maplibre-gl', () => import('./test/mocks/maplibre-gl'))
vi.mock('idb-keyval', () => ({ get: vi.fn(), set: vi.fn(), del: vi.fn() }))
const store = new Map<string, unknown>()
beforeEach(() => {
store.clear()
vi.mocked(get).mockImplementation((key) => Promise.resolve(store.get(key as string)))
vi.mocked(set).mockImplementation((key, value) => {
store.set(key as string, value)
return Promise.resolve()
})
vi.stubGlobal('fetch', vi.fn())
})
afterEach(() => {
cleanup()
vi.clearAllMocks()
vi.unstubAllGlobals()
})
/** A phone that has been through onboarding already. */
function returningHiker() {
store.set(PREFERENCES_KEY, {
...DEFAULT_PREFERENCES,
onboarding_completed: true,
download_choice_made: true,
})
}
async function completeOnboarding(user: ReturnType<typeof userEvent.setup>) {
await screen.findByText('What OurHike is')
await user.click(screen.getByRole('button', { name: 'Continue' }))
await user.click(screen.getByRole('button', { name: 'Continue' }))
await user.click(screen.getByRole('button', { name: /not now/i }))
}
describe('App shell', () => {
it('opens on onboarding the very first time', async () => {
render(<App />)
expect(await screen.findByText('What OurHike is')).toBeInTheDocument()
})
it('does not show onboarding again once it has been completed', async () => {
returningHiker()
render(<App />)
expect(await screen.findByRole('region', { name: /trail map/i })).toBeInTheDocument()
expect(screen.queryByText('What OurHike is')).not.toBeInTheDocument()
})
it('sends someone to Downloads when onboarding finishes, since there is no map yet', async () => {
const user = userEvent.setup()
render(<App />)
await completeOnboarding(user)
expect(await screen.findByText('Offline map')).toBeInTheDocument()
})
it('remembers that onboarding was completed, so a reload does not repeat it', async () => {
const user = userEvent.setup()
render(<App />)
await completeOnboarding(user)
await waitFor(() => {
const saved = store.get(PREFERENCES_KEY) as { onboarding_completed: boolean }
expect(saved.onboarding_completed).toBe(true)
})
})
it('says the position is unknown rather than claiming mile zero before a fix', async () => {
returningHiker()
render(<App />)
// Mile 0.0 is Springer Mountain - a confident claim about somewhere the
// hiker is almost certainly not standing.
expect(await screen.findByText(/looking for gps/i)).toBeInTheDocument()
expect(screen.queryByText(/mi 0\.0/)).not.toBeInTheDocument()
})
it('moves between the three tabs', async () => {
const user = userEvent.setup()
returningHiker()
render(<App />)
await screen.findByRole('region', { name: /trail map/i })
await user.click(screen.getByRole('tab', { name: 'Downloads' }))
expect(await screen.findByText('Offline map')).toBeInTheDocument()
await user.click(screen.getByRole('tab', { name: 'More' }))
expect(await screen.findByRole('heading', { name: 'You' })).toBeInTheDocument()
await user.click(screen.getByRole('tab', { name: 'Trail' }))
expect(await screen.findByRole('region', { name: /trail map/i })).toBeInTheDocument()
})
it('reaches the report flow from More', async () => {
const user = userEvent.setup()
returningHiker()
render(<App />)
await screen.findByRole('region', { name: /trail map/i })
await user.click(screen.getByRole('tab', { name: 'More' }))
await user.click(await screen.findByRole('button', { name: /report a problem/i }))
expect(
await screen.findByRole('heading', { name: 'Report a problem' }),
).toBeInTheDocument()
})
it('saves a report to the outbox rather than asking to sign in first', async () => {
const user = userEvent.setup()
returningHiker()
render(<App />)
await screen.findByRole('region', { name: /trail map/i })
await user.click(screen.getByRole('tab', { name: 'More' }))
await user.click(await screen.findByRole('button', { name: /report a problem/i }))
await user.click(await screen.findByRole('button', { name: /blow down/i }))
await user.click(await screen.findByRole('button', { name: /send|save to outbox/i }))
await waitFor(() => {
const queued = store.get('ourhike:outbox') as Array<{ payload: { type: string } }>
expect(queued).toHaveLength(1)
expect(queued[0].payload.type).toBe('blowdown')
})
})
it('warns on the Downloads screen when no data source was configured at build time', async () => {
const user = userEvent.setup()
returningHiker()
render(<App />)
await screen.findByRole('region', { name: /trail map/i })
await user.click(screen.getByRole('tab', { name: 'Downloads' }))
// VITE_DATA_BASE_URL is unset under test, so this is the real state - and a
// download that would silently 404 is worth saying out loud.
expect(await screen.findByRole('alert')).toHaveTextContent(/VITE_DATA_BASE_URL/)
})
})