-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathvrt-components.spec.ts
More file actions
178 lines (156 loc) · 6.47 KB
/
Copy pathvrt-components.spec.ts
File metadata and controls
178 lines (156 loc) · 6.47 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import { expect } from '@playwright/test'
import { test } from './fixtures'
import {
setupMinimalVisualRegressionTest,
mockSpotifyPlaybackState,
getSpotifyMasks,
} from './lib'
import { checkAccessibility } from './lib/accessibility'
import { takeScreenshot } from './lib/visual'
import { waitForPageReady, waitForWebSocketConnection } from './lib/waits'
import { VRT_TIMEOUTS } from './lib/timeouts'
test.describe('Component-Specific VRT', () => {
test('BottomNavBar highlights correct icon', async ({ dashboardPage }) => {
await setupMinimalVisualRegressionTest(dashboardPage, '/')
const bottomNav = dashboardPage.getByTestId('bottom-nav-bar')
await takeScreenshot(bottomNav, 'bottom-nav-bar.png')
})
test('Footer rendering', async ({ dashboardPage }) => {
await setupMinimalVisualRegressionTest(dashboardPage, '/')
const footer = dashboardPage.getByTestId('footer')
await takeScreenshot(footer, 'footer.png')
})
test('LoadingIndicator visibility', async ({ dashboardPage }) => {
await setupMinimalVisualRegressionTest(dashboardPage, '/')
// Force visibility and pause animation for VRT
await dashboardPage.evaluate(() => {
const el = document.querySelector(
'[data-testid="loading-indicator"]'
) as HTMLElement
if (el) {
el.style.opacity = '1'
el.style.visibility = 'visible'
// Force an opaque background to prevent pixel leakage from underlying content
el.style.backgroundColor = 'rgb(0, 0, 0)'
// Pause any CSS animations/transitions specifically on this element
el.style.animationPlayState = 'paused'
el.style.transition = 'none'
}
})
const loadingIndicator = dashboardPage.getByTestId('loading-indicator')
await expect(loadingIndicator).toBeVisible()
// Mask the animated progress circle as it's highly flaky in VRT
const progress = loadingIndicator.getByTestId('loading-indicator-progress')
await takeScreenshot(loadingIndicator, 'loading-indicator.png', {
mask: [progress],
})
})
test('GoogleDocViewer shrunk state', async ({ dashboardPage }) => {
// Ensure we are in non-native mode for this test, and enable testing mode
await dashboardPage.goto('/?native=false&testing=true')
await waitForPageReady(dashboardPage)
const toggleButton = dashboardPage.getByLabel('Collapse document')
await toggleButton.click()
const viewer = dashboardPage
.getByTestId('google-doc-viewer-iframe')
.locator('..')
await takeScreenshot(viewer, 'google-doc-viewer-shrunk.png')
})
test('WorkoutTableHeader rendering', async ({ dashboardPage }) => {
// Setup network interception first
await dashboardPage.route('/api/workout*', async (route) => {
await route.fulfill({
json: {
headers: ['Exercise', 'Sets', 'Reps'],
},
})
})
// Ensure we are in native mode for this test, and enable testing mode
await dashboardPage.goto('/?native=true&testing=true')
await waitForPageReady(dashboardPage)
const tableHeader = dashboardPage.getByTestId('workout-table-header')
await expect(tableHeader).toBeVisible()
await takeScreenshot(tableHeader, 'workout-table-header.png')
})
test('SpotifyDeviceSelector menu', async ({ dashboardPage }) => {
await setupMinimalVisualRegressionTest(dashboardPage, '/')
// Wait for the WebSocket to fully reconnect after the reload
// before applying mocks, otherwise the server's initial STATE_SYNC
// will immediately overwrite the mock.
await waitForWebSocketConnection(dashboardPage)
// Mock Spotify state with devices to show the component naturally
await mockSpotifyPlaybackState(dashboardPage, {
playback: {
is_playing: true,
volume_percent: 50,
isMuted: false,
progress_ms: 0,
track: {
id: 'vrt-track',
name: 'VRT Test Track',
artist: 'VRT Artist',
albumName: 'VRT Album',
albumArtUrl: 'https://via.placeholder.com/150',
},
},
devices: [
{
id: 'dev-1',
name: 'Speaker 1',
is_active: true,
type: 'Speaker',
is_private_session: false,
is_restricted: false,
volume_percent: 50,
},
{
id: 'dev-2',
name: 'Phone',
is_active: false,
type: 'Smartphone',
is_private_session: false,
is_restricted: false,
volume_percent: 50,
},
],
})
const selectorButton = dashboardPage.getByTestId('spotify-device-select')
await expect(selectorButton).toBeVisible({
timeout: VRT_TIMEOUTS.STANDARD,
})
await selectorButton.click()
const menu = dashboardPage
.locator('[data-testid="spotify-device-selector-menu-paper"]')
.last()
// Give the menu time to mount in the portal and stabilize before checking visibility
await expect(menu).toBeVisible()
// Wait for the opacity transition to finish rendering
await expect(menu).toHaveCSS('opacity', '1')
// Perform manual accessibility check on the specific menu element to ensure context validity
await checkAccessibility(menu)
await takeScreenshot(menu, 'spotify-device-selector-menu.png', {
threshold: 0.2, // Tighter threshold for the Paper element
skipA11y: true, // Accessibility checked manually above
mask: getSpotifyMasks(dashboardPage),
})
})
test('RefreshIconButton states', async ({ dashboardPage }) => {
await setupMinimalVisualRegressionTest(dashboardPage, '/')
const refreshButton = dashboardPage.getByTestId('refresh-icon-button')
await takeScreenshot(refreshButton, 'refresh-icon-button.png')
await refreshButton.hover()
await takeScreenshot(refreshButton, 'refresh-icon-button-hover.png')
})
test('ErrorFallback UI', async ({ dashboardPage }) => {
// Navigate to dashboard with test-error=true to trigger the real ErrorBoundary and ErrorFallback component.
// NOTE: This error is now triggered client-side to avoid noisy server logs and 500 responses.
await dashboardPage.goto('/?test-error=true&testing=true')
const errorFallback = dashboardPage.getByTestId('error-fallback')
// Explicit extended timeout for ErrorFallback as triggering the error boundary and
// rendering the fallback UI can be slower on CI environments.
await expect(errorFallback).toBeVisible({
timeout: VRT_TIMEOUTS.EXTENDED,
})
await takeScreenshot(errorFallback, 'error-fallback.png')
})
})