een-api-toolkit 0.1.13 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,373 +1,293 @@
1
1
  /**
2
- * Authenticated E2E tests for vue-media example
2
+ * E2E tests for the Vue Media Example - OAuth Login Flow
3
3
  *
4
- * These tests require:
5
- * - TEST_USER and TEST_PASSWORD environment variables
6
- * - VITE_EEN_CLIENT_ID environment variable
7
- * - Running OAuth proxy server (see ../../../scripts/restart-proxy.sh)
4
+ * Tests the OAuth login flow through the UI:
5
+ * 1. Click login button in the example app
6
+ * 2. Enter credentials on EEN OAuth page
7
+ * 3. Complete the OAuth callback
8
+ * 4. Verify authenticated state and media functionality
9
+ *
10
+ * Required environment variables:
11
+ * - VITE_PROXY_URL: OAuth proxy URL (e.g., http://127.0.0.1:8787)
12
+ * - VITE_EEN_CLIENT_ID: EEN OAuth client ID
13
+ * - TEST_USER: Test user email
14
+ * - TEST_PASSWORD: Test user password
15
+ *
16
+ * Note: Helper functions (isProxyAccessible, performLogin, clearAuthState) are
17
+ * intentionally duplicated in each example's auth.spec.ts to avoid Playwright's
18
+ * "Requiring @playwright/test second time" error that occurs when importing
19
+ * from a shared file outside the example directory.
8
20
  */
9
21
 
10
- import { test, expect } from '@playwright/test'
11
- import { getAuthToken, injectAuthState, AuthState } from '../../../e2e/auth-helper'
22
+ import { test, expect, Page } from '@playwright/test'
23
+ import { baseURL } from '../playwright.config'
12
24
 
13
- test.describe('vue-media authenticated tests', () => {
14
- let authState: AuthState
25
+ const TIMEOUTS = {
26
+ OAUTH_REDIRECT: 30000,
27
+ ELEMENT_VISIBLE: 15000,
28
+ PASSWORD_VISIBLE: 10000,
29
+ AUTH_COMPLETE: 30000,
30
+ UI_UPDATE: 10000,
31
+ PROXY_CHECK: 5000,
32
+ MEDIA_LOAD: 30000
33
+ } as const
15
34
 
16
- test.beforeAll(async () => {
17
- // Get auth token (from cache or fresh login)
18
- authState = await getAuthToken()
19
- })
35
+ const TEST_USER = process.env.TEST_USER
36
+ const TEST_PASSWORD = process.env.TEST_PASSWORD
37
+ const PROXY_URL = process.env.VITE_PROXY_URL
20
38
 
21
- test.beforeEach(async ({ page }) => {
22
- // Navigate to home and inject auth state before each test
23
- await page.goto('/')
24
- await injectAuthState(page, authState)
25
- })
39
+ /**
40
+ * Checks if the OAuth proxy server is accessible.
41
+ * Returns false if proxy is unavailable, allowing tests to be skipped gracefully.
42
+ */
43
+ async function isProxyAccessible(): Promise<boolean> {
44
+ if (!PROXY_URL) return false
45
+ const controller = new AbortController()
46
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUTS.PROXY_CHECK)
47
+
48
+ try {
49
+ const response = await fetch(PROXY_URL, {
50
+ method: 'HEAD',
51
+ signal: controller.signal
52
+ })
53
+ return response.ok || response.status === 404
54
+ } catch (error) {
55
+ if (!process.env.CI) {
56
+ console.log('Proxy check failed:', error instanceof Error ? error.message : error)
57
+ }
58
+ return false
59
+ } finally {
60
+ clearTimeout(timeoutId)
61
+ }
62
+ }
26
63
 
27
- test('authenticated user sees cameras on live page', async ({ page }) => {
28
- // Navigate to live page (auth already injected by beforeEach)
29
- await page.goto('/live')
64
+ /**
65
+ * Performs OAuth login flow through the EEN authentication page.
66
+ * Handles two-step navigation: app login page -> EEN OAuth -> callback
67
+ */
68
+ async function performLogin(page: Page, username: string, password: string): Promise<void> {
69
+ await page.goto('/')
30
70
 
31
- // Should show the live camera view
32
- await expect(page.getByRole('heading', { name: 'Live Camera View' })).toBeVisible()
71
+ // Click login button on home page to go to login page
72
+ await page.click('[data-testid="login-button"]')
73
+ await page.waitForURL('/login')
33
74
 
34
- // Wait for cameras to load (or show no cameras message)
35
- await page.waitForSelector('[data-testid="camera-select"], .no-cameras', {
36
- timeout: 30000
37
- })
75
+ // Click login button on login page to trigger OAuth
76
+ await Promise.all([
77
+ page.waitForURL(/.*eagleeyenetworks.com.*/, { timeout: TIMEOUTS.OAUTH_REDIRECT }),
78
+ page.getByRole('button', { name: 'Login with Eagle Eye Networks' }).click()
79
+ ])
38
80
 
39
- // Either cameras are loaded or we see "no cameras" message
40
- const cameraSelect = page.getByTestId('camera-select')
41
- const noCameras = page.locator('.no-cameras')
81
+ // EEN OAuth page selectors - these depend on EEN's login UI and may need
82
+ // updates if EEN changes their authentication page structure
83
+ const emailInput = page.locator('#authentication--input__email')
84
+ await emailInput.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE })
85
+ await emailInput.fill(username)
42
86
 
43
- const hasCameras = await cameraSelect.isVisible().catch(() => false)
44
- const hasNoCameras = await noCameras.isVisible().catch(() => false)
87
+ await page.getByRole('button', { name: 'Next' }).click()
45
88
 
46
- expect(hasCameras || hasNoCameras).toBe(true)
89
+ const passwordInput = page.locator('#authentication--input__password')
90
+ await passwordInput.waitFor({ state: 'visible', timeout: TIMEOUTS.PASSWORD_VISIBLE })
91
+ await passwordInput.fill(password)
47
92
 
48
- if (hasCameras) {
49
- console.log('Cameras found - checking controls')
93
+ // EEN uses either #next or "Sign in" button depending on login flow
94
+ await page.locator('#next, button:has-text("Sign in")').first().click()
50
95
 
51
- // Verify controls are present
52
- await expect(page.getByTestId('refresh-button')).toBeVisible()
53
- await expect(page.getByTestId('auto-refresh-button')).toBeVisible()
96
+ // Wait for redirect back to the app using configured baseURL
97
+ const baseURLPattern = new RegExp(baseURL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
98
+ await page.waitForURL(baseURLPattern, { timeout: TIMEOUTS.AUTH_COMPLETE })
99
+ }
54
100
 
55
- // Wait for image to load
56
- await page.waitForSelector('[data-testid="live-image"], .image-loading, .no-image', {
57
- timeout: 30000
101
+ /**
102
+ * Clears authentication state from browser storage.
103
+ * Used in afterEach to ensure test isolation.
104
+ */
105
+ async function clearAuthState(page: Page): Promise<void> {
106
+ try {
107
+ const url = page.url()
108
+ if (url && url.startsWith('http')) {
109
+ await page.evaluate(() => {
110
+ try {
111
+ localStorage.clear()
112
+ sessionStorage.clear()
113
+ } catch {
114
+ // Storage access may fail in certain contexts
115
+ }
58
116
  })
117
+ }
118
+ } catch (error) {
119
+ if (!process.env.CI) {
120
+ console.log('Clear auth state failed:', error instanceof Error ? error.message : error)
121
+ }
122
+ }
123
+ }
59
124
 
60
- // Check if we got an image or an error
61
- const hasImage = await page.getByTestId('live-image').isVisible().catch(() => false)
62
- if (hasImage) {
63
- console.log('Live image loaded successfully')
64
-
65
- // Check timestamp is shown
66
- const timestamp = page.getByTestId('timestamp')
67
- await expect(timestamp).toBeVisible()
68
- } else {
69
- console.log('No live image available (camera may be offline)')
70
- }
71
- } else {
72
- console.log('No cameras in account')
125
+ test.describe('Vue Media Example - Auth', () => {
126
+ let proxyAccessible = false
127
+
128
+ function skipIfNoProxy() {
129
+ test.skip(!proxyAccessible, 'OAuth proxy not accessible')
130
+ }
131
+
132
+ function skipIfNoCredentials() {
133
+ test.skip(!TEST_USER || !TEST_PASSWORD, 'Test credentials not available')
134
+ }
135
+
136
+ test.beforeAll(async () => {
137
+ proxyAccessible = await isProxyAccessible()
138
+ if (!proxyAccessible) {
139
+ console.log('OAuth proxy not accessible - OAuth tests will be skipped')
73
140
  }
74
141
  })
75
142
 
76
- test('authenticated home page shows view live button', async ({ page }) => {
77
- // Reload to apply auth state (already injected by beforeEach)
78
- await page.reload()
143
+ test.afterEach(async ({ page }) => {
144
+ await clearAuthState(page)
145
+ })
79
146
 
80
- // Should show authenticated state
81
- await expect(page.getByTestId('authenticated')).toBeVisible()
82
- await expect(page.getByTestId('view-live-button')).toBeVisible()
83
- await expect(page.getByText('You are logged in!')).toBeVisible()
147
+ test('shows login button when not authenticated', async ({ page }) => {
148
+ await page.goto('/')
149
+ await expect(page.getByTestId('not-authenticated')).toBeVisible()
150
+ await expect(page.getByTestId('nav-login')).toBeVisible()
84
151
  })
85
152
 
86
- test('refresh button fetches new image', async ({ page }) => {
87
- // Navigate to live page (auth already injected by beforeEach)
153
+ test('live page redirects to login when not authenticated', async ({ page }) => {
88
154
  await page.goto('/live')
155
+ await expect(page).toHaveURL('/login')
156
+ })
89
157
 
90
- // Wait for initial load
91
- await page.waitForSelector('[data-testid="camera-select"], .no-cameras', {
92
- timeout: 30000
93
- })
94
-
95
- const hasCameras = await page.getByTestId('camera-select').isVisible().catch(() => false)
158
+ test('login button redirects to OAuth page', async ({ page }) => {
159
+ skipIfNoProxy()
160
+ skipIfNoCredentials()
96
161
 
97
- if (hasCameras) {
98
- // Wait for initial image
99
- await page.waitForSelector('[data-testid="live-image"], .no-image', {
100
- timeout: 30000
101
- })
162
+ await page.goto('/')
163
+ await expect(page.getByTestId('login-button')).toBeVisible()
102
164
 
103
- // Click refresh
104
- await page.getByTestId('refresh-button').click()
165
+ // Click login button on home page to go to login page
166
+ await page.click('[data-testid="login-button"]')
167
+ await page.waitForURL('/login')
105
168
 
106
- // Button should show loading state briefly
107
- // Just verify the click works without errors
108
- await expect(page.getByTestId('refresh-button')).toBeVisible()
169
+ // Click login button on login page to trigger OAuth
170
+ await Promise.all([
171
+ page.waitForURL(/.*eagleeyenetworks.com.*/, { timeout: TIMEOUTS.OAUTH_REDIRECT }),
172
+ page.getByRole('button', { name: 'Login with Eagle Eye Networks' }).click()
173
+ ])
109
174
 
110
- console.log('Refresh button clicked successfully')
111
- } else {
112
- console.log('No cameras to test refresh with')
113
- }
175
+ // EEN OAuth page selector
176
+ const emailInput = page.locator('#authentication--input__email')
177
+ await expect(emailInput).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE })
114
178
  })
115
179
 
116
- test('authenticated home page shows view recorded button', async ({ page }) => {
117
- // Reload to apply auth state (already injected by beforeEach)
118
- await page.reload()
119
-
120
- // Should show authenticated state with both buttons
121
- await expect(page.getByTestId('authenticated')).toBeVisible()
122
- await expect(page.getByTestId('view-live-button')).toBeVisible()
123
- await expect(page.getByTestId('view-recorded-button')).toBeVisible()
124
- })
180
+ test('complete OAuth login flow and view media', async ({ page }) => {
181
+ skipIfNoProxy()
182
+ skipIfNoCredentials()
125
183
 
126
- test('recorded image page shows camera selector and time picker', async ({ page }) => {
127
- // Navigate to recorded page (auth already injected by beforeEach)
128
- await page.goto('/recorded')
184
+ await page.goto('/')
185
+ await expect(page.getByTestId('not-authenticated')).toBeVisible()
129
186
 
130
- // Should show the recorded image page
131
- await expect(page.getByRole('heading', { name: 'Recorded Images' })).toBeVisible()
187
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
132
188
 
133
- // Wait for cameras to load
134
- await page.waitForSelector('[data-testid="camera-select"], .no-cameras', {
135
- timeout: 30000
136
- })
137
-
138
- const hasCameras = await page.getByTestId('camera-select').isVisible().catch(() => false)
189
+ await expect(page.getByTestId('not-authenticated')).not.toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
190
+ await expect(page.getByTestId('nav-live')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
191
+ await expect(page.getByTestId('nav-logout')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
192
+ })
139
193
 
140
- if (hasCameras) {
141
- console.log('Cameras found - checking recorded image controls')
194
+ test('can view live camera after login', async ({ page }) => {
195
+ skipIfNoProxy()
196
+ skipIfNoCredentials()
142
197
 
143
- // Verify controls are present
144
- await expect(page.getByTestId('datetime-input')).toBeVisible()
145
- await expect(page.getByTestId('go-button')).toBeVisible()
146
- await expect(page.getByTestId('prev-button')).toBeVisible()
147
- await expect(page.getByTestId('next-button')).toBeVisible()
198
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
199
+ await expect(page.getByTestId('nav-live')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
148
200
 
149
- // Initially prev/next should be disabled (no image loaded yet or no tokens)
150
- const prevDisabled = await page.getByTestId('prev-button').isDisabled()
151
- const nextDisabled = await page.getByTestId('next-button').isDisabled()
152
- console.log(`Navigation buttons: prev=${prevDisabled ? 'disabled' : 'enabled'}, next=${nextDisabled ? 'disabled' : 'enabled'}`)
153
- } else {
154
- console.log('No cameras in account')
155
- }
156
- })
201
+ await page.click('[data-testid="nav-live"]')
202
+ await page.waitForURL('/live')
157
203
 
158
- test('recorded image page auto-loads image on mount', async ({ page }) => {
159
- // Navigate to recorded page (auth already injected by beforeEach)
160
- await page.goto('/recorded')
204
+ await expect(page.getByRole('heading', { name: 'Live Camera View' })).toBeVisible()
161
205
 
162
206
  // Wait for cameras to load
163
207
  await page.waitForSelector('[data-testid="camera-select"], .no-cameras', {
164
- timeout: 30000
208
+ timeout: TIMEOUTS.MEDIA_LOAD
165
209
  })
210
+ })
166
211
 
167
- const hasCameras = await page.getByTestId('camera-select').isVisible().catch(() => false)
168
-
169
- if (hasCameras) {
170
- // Wait for image to load (auto-loads on page mount with default time of 1 hour ago)
171
- await page.waitForSelector('[data-testid="recorded-image"], .no-image, .error', {
172
- timeout: 45000
173
- })
174
-
175
- const hasImage = await page.getByTestId('recorded-image').isVisible().catch(() => false)
212
+ test('live page shows camera controls when cameras available', async ({ page }) => {
213
+ skipIfNoProxy()
214
+ skipIfNoCredentials()
176
215
 
177
- if (hasImage) {
178
- console.log('Recorded image loaded successfully')
216
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
217
+ await expect(page.getByTestId('nav-live')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
179
218
 
180
- // Check timestamp is shown
181
- const timestamp = page.getByTestId('timestamp')
182
- await expect(timestamp).toBeVisible()
219
+ await page.click('[data-testid="nav-live"]')
220
+ await page.waitForURL('/live')
183
221
 
184
- // Time picker should be updated with the image timestamp
185
- const datetimeInput = page.getByTestId('datetime-input')
186
- const inputValue = await datetimeInput.inputValue()
187
- expect(inputValue).toBeTruthy()
188
- console.log(`Time picker value: ${inputValue}`)
189
- } else {
190
- console.log('No recorded image available (no recordings in time range)')
191
- }
192
- } else {
193
- console.log('No cameras to test with')
194
- }
195
- })
196
-
197
- test('recorded image navigation buttons work', async ({ page }) => {
198
- // Navigate to recorded page (auth already injected by beforeEach)
199
- await page.goto('/recorded')
200
-
201
- // Wait for cameras to load
202
222
  await page.waitForSelector('[data-testid="camera-select"], .no-cameras', {
203
- timeout: 30000
223
+ timeout: TIMEOUTS.MEDIA_LOAD
204
224
  })
205
225
 
206
226
  const hasCameras = await page.getByTestId('camera-select').isVisible().catch(() => false)
207
-
208
227
  if (hasCameras) {
209
- // Wait for an image to load
210
- await page.waitForSelector('[data-testid="recorded-image"], .no-image', {
211
- timeout: 45000
212
- })
213
-
214
- const hasImage = await page.getByTestId('recorded-image').isVisible().catch(() => false)
228
+ await expect(page.getByTestId('refresh-button')).toBeVisible()
229
+ await expect(page.getByTestId('auto-refresh-button')).toBeVisible()
230
+ console.log('Camera controls visible')
231
+ } else {
232
+ console.log('No cameras in account - skipping camera-specific checks')
233
+ }
234
+ })
215
235
 
216
- if (hasImage) {
217
- // Check if next button is enabled
218
- const nextButton = page.getByTestId('next-button')
219
- const isNextEnabled = !(await nextButton.isDisabled())
236
+ test('can view recorded images after login', async ({ page }) => {
237
+ skipIfNoProxy()
238
+ skipIfNoCredentials()
220
239
 
221
- if (isNextEnabled) {
222
- // Get current timestamp
223
- const initialTimestamp = await page.getByTestId('timestamp').textContent()
240
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
241
+ await expect(page.getByTestId('nav-recorded')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
224
242
 
225
- // Click next
226
- await nextButton.click()
243
+ await page.click('[data-testid="nav-recorded"]')
244
+ await page.waitForURL('/recorded')
227
245
 
228
- // Wait for potential update
229
- await page.waitForTimeout(2000)
246
+ await expect(page.getByRole('heading', { name: 'Recorded Images' })).toBeVisible()
230
247
 
231
- // Check timestamp changed
232
- const newTimestamp = await page.getByTestId('timestamp').textContent()
233
- console.log(`Navigated: ${initialTimestamp} -> ${newTimestamp}`)
248
+ // Wait for cameras to load
249
+ await page.waitForSelector('[data-testid="camera-select"], .no-cameras', {
250
+ timeout: TIMEOUTS.MEDIA_LOAD
251
+ })
252
+ })
234
253
 
235
- // Check if prev button is now enabled
236
- const prevButton = page.getByTestId('prev-button')
237
- const isPrevEnabled = !(await prevButton.isDisabled())
254
+ test('recorded page shows navigation controls when cameras available', async ({ page }) => {
255
+ skipIfNoProxy()
256
+ skipIfNoCredentials()
238
257
 
239
- if (isPrevEnabled) {
240
- // Navigate back
241
- await prevButton.click()
242
- await page.waitForTimeout(2000)
243
- console.log('Previous navigation successful')
244
- }
245
- } else {
246
- console.log('Next button disabled - only one image available')
247
- }
248
- } else {
249
- console.log('No recorded image to navigate from')
250
- }
251
- } else {
252
- console.log('No cameras to test navigation with')
253
- }
254
- })
258
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
259
+ await expect(page.getByTestId('nav-recorded')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
255
260
 
256
- test('recorded image page loads with past date via Go button', async ({ page }) => {
257
- // Navigate to recorded page (auth already injected by beforeEach)
258
- await page.goto('/recorded')
261
+ await page.click('[data-testid="nav-recorded"]')
262
+ await page.waitForURL('/recorded')
259
263
 
260
- // Wait for cameras to load
261
264
  await page.waitForSelector('[data-testid="camera-select"], .no-cameras', {
262
- timeout: 30000
265
+ timeout: TIMEOUTS.MEDIA_LOAD
263
266
  })
264
267
 
265
268
  const hasCameras = await page.getByTestId('camera-select').isVisible().catch(() => false)
266
-
267
269
  if (hasCameras) {
268
- // Set the date/time to 24 hours ago
269
- const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000)
270
- const year = yesterday.getFullYear()
271
- const month = String(yesterday.getMonth() + 1).padStart(2, '0')
272
- const day = String(yesterday.getDate()).padStart(2, '0')
273
- const hours = String(yesterday.getHours()).padStart(2, '0')
274
- const minutes = String(yesterday.getMinutes()).padStart(2, '0')
275
- const dateTimeValue = `${year}-${month}-${day}T${hours}:${minutes}`
276
-
277
- // Fill in the datetime input with yesterday's date
278
- await page.getByTestId('datetime-input').fill(dateTimeValue)
279
-
280
- // Click Go button to load image from that time
281
- await page.getByTestId('go-button').click()
282
-
283
- // Wait for the image to load or error to appear
284
- await page.waitForSelector('[data-testid="recorded-image"], .error, .no-image', {
285
- timeout: 45000
286
- })
287
-
288
- // Check results
289
- const hasImage = await page.getByTestId('recorded-image').isVisible().catch(() => false)
290
- const hasError = await page.locator('.error').isVisible().catch(() => false)
291
-
292
- if (hasImage) {
293
- console.log('Image loaded successfully with past date')
294
- // Check timestamp is shown
295
- const timestamp = page.getByTestId('timestamp')
296
- await expect(timestamp).toBeVisible()
297
- } else if (hasError) {
298
- const errorText = await page.locator('.error').textContent()
299
- console.log('Error loading image with past date:', errorText)
300
- // A 404 "Not found" is acceptable if no recordings exist for that time
301
- expect(errorText).toMatch(/Not found|No recordings|Unknown error/)
302
- } else {
303
- console.log('No image or error visible - no recordings for past date')
304
- }
270
+ await expect(page.getByTestId('datetime-input')).toBeVisible()
271
+ await expect(page.getByTestId('go-button')).toBeVisible()
272
+ await expect(page.getByTestId('prev-button')).toBeVisible()
273
+ await expect(page.getByTestId('next-button')).toBeVisible()
274
+ console.log('Recorded image controls visible')
305
275
  } else {
306
- console.log('No cameras to test past date with')
276
+ console.log('No cameras in account - skipping camera-specific checks')
307
277
  }
308
278
  })
309
279
 
310
- test('camera selection persists between live and recorded pages', async ({ page }) => {
311
- // Navigate to live page (auth already injected by beforeEach)
312
- await page.goto('/live')
280
+ test('can logout after login', async ({ page }) => {
281
+ skipIfNoProxy()
282
+ skipIfNoCredentials()
313
283
 
314
- // Wait for cameras to load
315
- await page.waitForSelector('[data-testid="camera-select"], .no-cameras', {
316
- timeout: 30000
317
- })
284
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
285
+ await expect(page.getByTestId('nav-logout')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
318
286
 
319
- const cameraSelect = page.getByTestId('camera-select')
320
- const hasCameras = await cameraSelect.isVisible().catch(() => false)
287
+ await page.click('[data-testid="nav-logout"]')
321
288
 
322
- if (hasCameras) {
323
- // Get all camera options
324
- const options = await cameraSelect.locator('option').all()
325
-
326
- if (options.length >= 2) {
327
- // Get the second camera's value
328
- const secondCameraValue = await options[1].getAttribute('value')
329
- console.log(`Selecting camera: ${secondCameraValue}`)
330
-
331
- // Select the second camera
332
- await cameraSelect.selectOption(secondCameraValue!)
333
-
334
- // Wait for selection to be applied
335
- await page.waitForTimeout(1000)
336
-
337
- // Navigate to recorded page
338
- await page.goto('/recorded')
339
-
340
- // Wait for cameras to load on recorded page
341
- await page.waitForSelector('[data-testid="camera-select"]', {
342
- timeout: 30000
343
- })
344
-
345
- // Check that the same camera is selected
346
- const recordedCameraSelect = page.getByTestId('camera-select')
347
- const selectedValue = await recordedCameraSelect.inputValue()
348
-
349
- expect(selectedValue).toBe(secondCameraValue)
350
- console.log(`Camera selection persisted: ${selectedValue}`)
351
-
352
- // Navigate back to live page
353
- await page.goto('/live')
354
-
355
- // Wait for cameras to load
356
- await page.waitForSelector('[data-testid="camera-select"]', {
357
- timeout: 30000
358
- })
359
-
360
- // Verify selection still persists
361
- const liveCameraSelect = page.getByTestId('camera-select')
362
- const liveSelectedValue = await liveCameraSelect.inputValue()
363
-
364
- expect(liveSelectedValue).toBe(secondCameraValue)
365
- console.log('Camera selection persisted across page navigations')
366
- } else {
367
- console.log('Only one camera available - cannot test persistence')
368
- }
369
- } else {
370
- console.log('No cameras to test with')
371
- }
289
+ await page.waitForURL('**/')
290
+ await expect(page.getByTestId('not-authenticated')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
291
+ await expect(page.getByTestId('nav-login')).toBeVisible()
372
292
  })
373
293
  })