sunpeak 0.18.13 → 0.19.1
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.
- package/README.md +34 -134
- package/bin/commands/test-init.mjs +305 -0
- package/bin/commands/test.mjs +83 -0
- package/bin/lib/inspect/inspect-config.mjs +16 -24
- package/bin/lib/test/base-config.mjs +60 -0
- package/bin/lib/test/matchers.mjs +99 -0
- package/bin/lib/test/test-config.d.mts +44 -0
- package/bin/lib/test/test-config.mjs +123 -0
- package/bin/lib/test/test-fixtures.d.mts +96 -0
- package/bin/lib/test/test-fixtures.mjs +189 -0
- package/bin/sunpeak.js +18 -5
- package/dist/mcp/index.cjs +58 -16
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +58 -16
- package/dist/mcp/index.js.map +1 -1
- package/package.json +22 -10
- package/template/README.md +15 -8
- package/template/dist/albums/albums.json +1 -1
- package/template/dist/carousel/carousel.json +1 -1
- package/template/dist/map/map.html +468 -280
- package/template/dist/map/map.json +1 -1
- package/template/dist/review/review.json +1 -1
- package/template/node_modules/.bin/playwright +2 -2
- package/template/node_modules/.bin/vite +2 -2
- package/template/node_modules/.bin/vitest +2 -2
- package/template/node_modules/.vite/deps/_metadata.json +4 -4
- package/template/node_modules/.vite-mcp/deps/_metadata.json +22 -22
- package/template/node_modules/.vite-mcp/deps/mapbox-gl.js +15924 -14588
- package/template/node_modules/.vite-mcp/deps/mapbox-gl.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/vitest.js +8 -8
- package/template/node_modules/.vite-mcp/deps/vitest.js.map +1 -1
- package/template/package.json +4 -4
- package/template/playwright.config.ts +2 -40
- package/template/tests/e2e/albums.spec.ts +114 -245
- package/template/tests/e2e/carousel.spec.ts +189 -313
- package/template/tests/e2e/map.spec.ts +177 -300
- package/template/tests/e2e/review.spec.ts +232 -423
- package/template/tests/live/albums.spec.ts +1 -1
- package/template/tests/live/carousel.spec.ts +1 -1
- package/template/tests/live/map.spec.ts +1 -1
- package/template/tests/live/playwright.config.ts +1 -1
- package/template/tests/live/review.spec.ts +1 -1
- package/template/vitest.config.ts +1 -1
- package/template/tests/e2e/global-setup.ts +0 -10
- package/template/tests/e2e/helpers.ts +0 -13
|
@@ -1,315 +1,191 @@
|
|
|
1
|
-
import { test, expect } from '
|
|
2
|
-
import { createInspectorUrl } from './helpers';
|
|
1
|
+
import { test, expect } from 'sunpeak/test';
|
|
3
2
|
|
|
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
|
-
await page.goto(createInspectorUrl({ simulation: 'show-carousel', theme: 'light', host }));
|
|
30
|
-
|
|
31
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
32
|
-
const card = iframe.locator('.rounded-2xl.border').first();
|
|
33
|
-
await expect(card).toBeVisible();
|
|
34
|
-
|
|
35
|
-
const styles = await card.evaluate((el) => {
|
|
36
|
-
const computed = window.getComputedStyle(el);
|
|
37
|
-
return {
|
|
38
|
-
borderWidth: computed.borderWidth,
|
|
39
|
-
borderStyle: computed.borderStyle,
|
|
40
|
-
};
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
expect(styles.borderWidth).toBe('1px');
|
|
44
|
-
expect(styles.borderStyle).toBe('solid');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('should have interactive buttons', async ({ page }) => {
|
|
48
|
-
await page.goto(createInspectorUrl({ simulation: 'show-carousel', theme: 'light', host }));
|
|
49
|
-
|
|
50
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
51
|
-
const visitButton = iframe.locator('button:has-text("Visit")').first();
|
|
52
|
-
await expect(visitButton).toBeAttached();
|
|
53
|
-
|
|
54
|
-
const styles = await visitButton.evaluate((el) => {
|
|
55
|
-
const computed = window.getComputedStyle(el);
|
|
56
|
-
return {
|
|
57
|
-
cursor: computed.cursor,
|
|
58
|
-
};
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
expect(styles.cursor).toBe('pointer');
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test.describe('Prod Tools Mode', () => {
|
|
66
|
-
test('should show empty state with Run button', async ({ page }) => {
|
|
67
|
-
await page.goto(createInspectorUrl({ tool: 'show-carousel', theme: 'dark', host }));
|
|
68
|
-
|
|
69
|
-
const emptyState = page.locator('text=Press Run to call the tool');
|
|
70
|
-
await expect(emptyState).toBeVisible();
|
|
71
|
-
|
|
72
|
-
const runButton = page.locator('button:has-text("Run")');
|
|
73
|
-
await expect(runButton).toBeVisible();
|
|
74
|
-
|
|
75
|
-
const iframe = page.locator('iframe');
|
|
76
|
-
await expect(iframe).not.toBeAttached();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('should have themed empty state colors in light mode', async ({ page }) => {
|
|
80
|
-
await page.goto(createInspectorUrl({ tool: 'show-carousel', theme: 'light', host }));
|
|
81
|
-
|
|
82
|
-
const emptyState = page.locator('text=Press Run to call the tool');
|
|
83
|
-
await expect(emptyState).toBeVisible();
|
|
84
|
-
|
|
85
|
-
const color = await emptyState.evaluate((el) => {
|
|
86
|
-
return window.getComputedStyle(el).color;
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const [r, g, b] = color.match(/\d+/g)!.map(Number);
|
|
90
|
-
expect(r + g + b).toBeLessThan(600);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test('should have themed empty state colors in dark mode', async ({ page }) => {
|
|
94
|
-
await page.goto(createInspectorUrl({ tool: 'show-carousel', theme: 'dark', host }));
|
|
95
|
-
|
|
96
|
-
const emptyState = page.locator('text=Press Run to call the tool');
|
|
97
|
-
await expect(emptyState).toBeVisible();
|
|
98
|
-
|
|
99
|
-
const color = await emptyState.evaluate((el) => {
|
|
100
|
-
return window.getComputedStyle(el).color;
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const [r, g, b] = color.match(/\d+/g)!.map(Number);
|
|
104
|
-
expect(r + g + b).toBeGreaterThan(200);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test.describe('Prod Resources Mode', () => {
|
|
109
|
-
test('should activate without errors', async ({ page }) => {
|
|
110
|
-
await page.goto(
|
|
111
|
-
createInspectorUrl({
|
|
112
|
-
simulation: 'show-carousel',
|
|
113
|
-
theme: 'dark',
|
|
114
|
-
host,
|
|
115
|
-
prodResources: true,
|
|
116
|
-
})
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const root = page.locator('#root');
|
|
120
|
-
await expect(root).not.toBeEmpty();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test.describe('Fullscreen Mode', () => {
|
|
125
|
-
test('should render correctly in fullscreen displayMode', async ({ page }) => {
|
|
126
|
-
await page.goto(
|
|
127
|
-
createInspectorUrl({
|
|
128
|
-
simulation: 'show-carousel',
|
|
129
|
-
theme: 'light',
|
|
130
|
-
displayMode: 'fullscreen',
|
|
131
|
-
host,
|
|
132
|
-
})
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
await page.waitForLoadState('networkidle');
|
|
136
|
-
const root = page.locator('#root');
|
|
137
|
-
await expect(root).not.toBeEmpty();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('should show detail view with place info in fullscreen', async ({ page }) => {
|
|
141
|
-
await page.goto(
|
|
142
|
-
createInspectorUrl({
|
|
143
|
-
simulation: 'show-carousel',
|
|
144
|
-
theme: 'light',
|
|
145
|
-
displayMode: 'fullscreen',
|
|
146
|
-
host,
|
|
147
|
-
})
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
151
|
-
|
|
152
|
-
// Click the first card to open fullscreen detail
|
|
153
|
-
const card = iframe.locator('.rounded-2xl').first();
|
|
154
|
-
await expect(card).toBeVisible();
|
|
155
|
-
await card.click();
|
|
156
|
-
|
|
157
|
-
// Should show detail view with place name, description, and detail fields
|
|
158
|
-
await expect(iframe.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({
|
|
159
|
-
timeout: 5000,
|
|
160
|
-
});
|
|
161
|
-
await expect(iframe.locator('text=Highlights')).toBeVisible();
|
|
162
|
-
await expect(iframe.locator('text=Tips')).toBeVisible();
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test('should show detail view when Learn More is clicked', async ({ page }) => {
|
|
166
|
-
await page.goto(
|
|
167
|
-
createInspectorUrl({
|
|
168
|
-
simulation: 'show-carousel',
|
|
169
|
-
theme: 'light',
|
|
170
|
-
displayMode: 'fullscreen',
|
|
171
|
-
host,
|
|
172
|
-
})
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
176
|
-
|
|
177
|
-
// Click "Learn More" on the first card
|
|
178
|
-
const learnMore = iframe.locator('button:has-text("Learn More")').first();
|
|
179
|
-
await expect(learnMore).toBeVisible();
|
|
180
|
-
await learnMore.click();
|
|
181
|
-
|
|
182
|
-
// Should show detail view
|
|
183
|
-
await expect(iframe.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({
|
|
184
|
-
timeout: 5000,
|
|
185
|
-
});
|
|
186
|
-
await expect(iframe.locator('text=Address')).toBeVisible();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test('should not have a back button in detail view', async ({ page }) => {
|
|
190
|
-
await page.goto(
|
|
191
|
-
createInspectorUrl({
|
|
192
|
-
simulation: 'show-carousel',
|
|
193
|
-
theme: 'light',
|
|
194
|
-
displayMode: 'fullscreen',
|
|
195
|
-
host,
|
|
196
|
-
})
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
200
|
-
|
|
201
|
-
// Click the first card to open detail
|
|
202
|
-
const card = iframe.locator('.rounded-2xl').first();
|
|
203
|
-
await expect(card).toBeVisible();
|
|
204
|
-
await card.click();
|
|
205
|
-
|
|
206
|
-
await expect(iframe.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({
|
|
207
|
-
timeout: 5000,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Back button should not exist
|
|
211
|
-
const backButton = iframe.locator('button[aria-label="Back to carousel"]');
|
|
212
|
-
await expect(backButton).not.toBeAttached();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test('should center the hero image without stretching', async ({ page }) => {
|
|
216
|
-
await page.goto(
|
|
217
|
-
createInspectorUrl({
|
|
218
|
-
simulation: 'show-carousel',
|
|
219
|
-
theme: 'light',
|
|
220
|
-
displayMode: 'fullscreen',
|
|
221
|
-
host,
|
|
222
|
-
})
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
226
|
-
|
|
227
|
-
// Click the first card to open detail
|
|
228
|
-
const card = iframe.locator('.rounded-2xl').first();
|
|
229
|
-
await expect(card).toBeVisible();
|
|
230
|
-
await card.click();
|
|
231
|
-
|
|
232
|
-
await expect(iframe.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({
|
|
233
|
-
timeout: 5000,
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
// Check the image container is centered
|
|
237
|
-
const imageContainer = iframe.locator('img').first().locator('..');
|
|
238
|
-
const styles = await imageContainer.evaluate((el) => {
|
|
239
|
-
const computed = window.getComputedStyle(el);
|
|
240
|
-
return {
|
|
241
|
-
justifyContent: computed.justifyContent,
|
|
242
|
-
};
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
expect(styles.justifyContent).toBe('center');
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test.describe('Dark Mode', () => {
|
|
250
|
-
test('should render carousel cards with correct styles', async ({ page }) => {
|
|
251
|
-
await page.goto(createInspectorUrl({ simulation: 'show-carousel', theme: 'dark', host }));
|
|
252
|
-
|
|
253
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
254
|
-
const card = iframe.locator('.rounded-2xl').first();
|
|
255
|
-
await expect(card).toBeVisible();
|
|
256
|
-
|
|
257
|
-
const styles = await card.evaluate((el) => {
|
|
258
|
-
const computed = window.getComputedStyle(el);
|
|
259
|
-
return {
|
|
260
|
-
borderRadius: computed.borderRadius,
|
|
261
|
-
cursor: computed.cursor,
|
|
262
|
-
};
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
expect(styles.borderRadius).toBe('16px'); // rounded-2xl
|
|
266
|
-
expect(styles.cursor).toBe('pointer');
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test('should have appropriate styling for dark mode', async ({ page }) => {
|
|
270
|
-
await page.goto(createInspectorUrl({ simulation: 'show-carousel', theme: 'dark', host }));
|
|
271
|
-
|
|
272
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
273
|
-
// Select card by its border + rounded combo
|
|
274
|
-
const card = iframe.locator('.rounded-2xl.border').first();
|
|
275
|
-
await expect(card).toBeVisible();
|
|
276
|
-
|
|
277
|
-
const styles = await card.evaluate((el) => {
|
|
278
|
-
const computed = window.getComputedStyle(el);
|
|
279
|
-
return {
|
|
280
|
-
borderWidth: computed.borderWidth,
|
|
281
|
-
borderStyle: computed.borderStyle,
|
|
282
|
-
};
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
expect(styles.borderWidth).toBe('1px');
|
|
286
|
-
expect(styles.borderStyle).toBe('solid');
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
test('should load without console errors', async ({ page }) => {
|
|
290
|
-
const errors: string[] = [];
|
|
291
|
-
page.on('console', (msg) => {
|
|
292
|
-
if (msg.type() === 'error') {
|
|
293
|
-
errors.push(msg.text());
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
await page.goto(createInspectorUrl({ simulation: 'show-carousel', theme: 'dark', host }));
|
|
298
|
-
|
|
299
|
-
// Wait for iframe content to render
|
|
300
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
301
|
-
await expect(iframe.locator('.rounded-2xl').first()).toBeVisible();
|
|
302
|
-
|
|
303
|
-
// Filter out expected iframe/MCP handshake errors
|
|
304
|
-
const unexpectedErrors = errors.filter(
|
|
305
|
-
(e) =>
|
|
306
|
-
!e.includes('[IframeResource]') &&
|
|
307
|
-
!e.includes('mcp') &&
|
|
308
|
-
!e.includes('PostMessage') &&
|
|
309
|
-
!e.includes('connect')
|
|
310
|
-
);
|
|
311
|
-
expect(unexpectedErrors).toHaveLength(0);
|
|
312
|
-
});
|
|
313
|
-
});
|
|
3
|
+
test('should render carousel cards with correct styles', async ({ mcp }) => {
|
|
4
|
+
const result = await mcp.callTool('show-carousel');
|
|
5
|
+
const app = result.app();
|
|
6
|
+
|
|
7
|
+
const card = app.locator('.rounded-2xl').first();
|
|
8
|
+
await expect(card).toBeVisible();
|
|
9
|
+
|
|
10
|
+
const styles = await card.evaluate((el) => {
|
|
11
|
+
const computed = window.getComputedStyle(el);
|
|
12
|
+
return { borderRadius: computed.borderRadius, cursor: computed.cursor };
|
|
13
|
+
});
|
|
14
|
+
expect(styles.borderRadius).toBe('16px');
|
|
15
|
+
expect(styles.cursor).toBe('pointer');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should have card with border styling', async ({ mcp }) => {
|
|
19
|
+
const result = await mcp.callTool('show-carousel');
|
|
20
|
+
const app = result.app();
|
|
21
|
+
|
|
22
|
+
const card = app.locator('.rounded-2xl.border').first();
|
|
23
|
+
await expect(card).toBeVisible();
|
|
24
|
+
|
|
25
|
+
const styles = await card.evaluate((el) => {
|
|
26
|
+
const computed = window.getComputedStyle(el);
|
|
27
|
+
return { borderWidth: computed.borderWidth, borderStyle: computed.borderStyle };
|
|
314
28
|
});
|
|
315
|
-
|
|
29
|
+
expect(styles.borderWidth).toBe('1px');
|
|
30
|
+
expect(styles.borderStyle).toBe('solid');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('should have interactive buttons', async ({ mcp }) => {
|
|
34
|
+
const result = await mcp.callTool('show-carousel');
|
|
35
|
+
const app = result.app();
|
|
36
|
+
|
|
37
|
+
const visitButton = app.locator('button:has-text("Visit")').first();
|
|
38
|
+
await expect(visitButton).toBeAttached();
|
|
39
|
+
|
|
40
|
+
const styles = await visitButton.evaluate((el) => ({
|
|
41
|
+
cursor: window.getComputedStyle(el).cursor,
|
|
42
|
+
}));
|
|
43
|
+
expect(styles.cursor).toBe('pointer');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('should show empty state with Run button in prod tools mode', async ({ mcp }) => {
|
|
47
|
+
await mcp.openTool('show-carousel', { theme: 'dark' });
|
|
48
|
+
|
|
49
|
+
await expect(mcp.page.locator('text=Press Run to call the tool')).toBeVisible();
|
|
50
|
+
await expect(mcp.page.locator('button:has-text("Run")')).toBeVisible();
|
|
51
|
+
await expect(mcp.page.locator('iframe')).not.toBeAttached();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('should have themed empty state colors in light mode', async ({ mcp }) => {
|
|
55
|
+
await mcp.openTool('show-carousel', { theme: 'light' });
|
|
56
|
+
|
|
57
|
+
const emptyState = mcp.page.locator('text=Press Run to call the tool');
|
|
58
|
+
await expect(emptyState).toBeVisible();
|
|
59
|
+
|
|
60
|
+
const color = await emptyState.evaluate((el) => window.getComputedStyle(el).color);
|
|
61
|
+
const [r, g, b] = color.match(/\d+/g)!.map(Number);
|
|
62
|
+
expect(r + g + b).toBeLessThan(600);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('should have themed empty state colors in dark mode', async ({ mcp }) => {
|
|
66
|
+
await mcp.openTool('show-carousel', { theme: 'dark' });
|
|
67
|
+
|
|
68
|
+
const emptyState = mcp.page.locator('text=Press Run to call the tool');
|
|
69
|
+
await expect(emptyState).toBeVisible();
|
|
70
|
+
|
|
71
|
+
const color = await emptyState.evaluate((el) => window.getComputedStyle(el).color);
|
|
72
|
+
const [r, g, b] = color.match(/\d+/g)!.map(Number);
|
|
73
|
+
expect(r + g + b).toBeGreaterThan(200);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should activate prod resources mode without errors', async ({ mcp }) => {
|
|
77
|
+
await mcp.callTool('show-carousel', {}, { theme: 'dark', prodResources: true });
|
|
78
|
+
const root = mcp.page.locator('#root');
|
|
79
|
+
await expect(root).not.toBeEmpty();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('should render correctly in fullscreen', async ({ mcp }) => {
|
|
83
|
+
await mcp.callTool('show-carousel', {}, { displayMode: 'fullscreen' });
|
|
84
|
+
await mcp.page.waitForLoadState('networkidle');
|
|
85
|
+
const root = mcp.page.locator('#root');
|
|
86
|
+
await expect(root).not.toBeEmpty();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('should show detail view with place info in fullscreen', async ({ mcp }) => {
|
|
90
|
+
const result = await mcp.callTool('show-carousel', {}, { displayMode: 'fullscreen' });
|
|
91
|
+
const app = result.app();
|
|
92
|
+
|
|
93
|
+
const card = app.locator('.rounded-2xl').first();
|
|
94
|
+
await expect(card).toBeVisible();
|
|
95
|
+
await card.click();
|
|
96
|
+
|
|
97
|
+
await expect(app.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({ timeout: 5000 });
|
|
98
|
+
await expect(app.locator('text=Highlights')).toBeVisible();
|
|
99
|
+
await expect(app.locator('text=Tips')).toBeVisible();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('should show detail view when Learn More is clicked', async ({ mcp }) => {
|
|
103
|
+
const result = await mcp.callTool('show-carousel', {}, { displayMode: 'fullscreen' });
|
|
104
|
+
const app = result.app();
|
|
105
|
+
|
|
106
|
+
const learnMore = app.locator('button:has-text("Learn More")').first();
|
|
107
|
+
await expect(learnMore).toBeVisible();
|
|
108
|
+
await learnMore.click();
|
|
109
|
+
|
|
110
|
+
await expect(app.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({ timeout: 5000 });
|
|
111
|
+
await expect(app.locator('text=Address')).toBeVisible();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('should not have a back button in detail view', async ({ mcp }) => {
|
|
115
|
+
const result = await mcp.callTool('show-carousel', {}, { displayMode: 'fullscreen' });
|
|
116
|
+
const app = result.app();
|
|
117
|
+
|
|
118
|
+
const card = app.locator('.rounded-2xl').first();
|
|
119
|
+
await expect(card).toBeVisible();
|
|
120
|
+
await card.click();
|
|
121
|
+
|
|
122
|
+
await expect(app.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({ timeout: 5000 });
|
|
123
|
+
const backButton = app.locator('button[aria-label="Back to carousel"]');
|
|
124
|
+
await expect(backButton).not.toBeAttached();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('should center the hero image without stretching', async ({ mcp }) => {
|
|
128
|
+
const result = await mcp.callTool('show-carousel', {}, { displayMode: 'fullscreen' });
|
|
129
|
+
const app = result.app();
|
|
130
|
+
|
|
131
|
+
const card = app.locator('.rounded-2xl').first();
|
|
132
|
+
await expect(card).toBeVisible();
|
|
133
|
+
await card.click();
|
|
134
|
+
|
|
135
|
+
await expect(app.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({ timeout: 5000 });
|
|
136
|
+
const imageContainer = app.locator('img').first().locator('..');
|
|
137
|
+
const styles = await imageContainer.evaluate((el) => ({
|
|
138
|
+
justifyContent: window.getComputedStyle(el).justifyContent,
|
|
139
|
+
}));
|
|
140
|
+
expect(styles.justifyContent).toBe('center');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('should render carousel in dark mode with correct styles', async ({ mcp }) => {
|
|
144
|
+
const result = await mcp.callTool('show-carousel', {}, { theme: 'dark' });
|
|
145
|
+
const app = result.app();
|
|
146
|
+
|
|
147
|
+
const card = app.locator('.rounded-2xl').first();
|
|
148
|
+
await expect(card).toBeVisible();
|
|
149
|
+
|
|
150
|
+
const styles = await card.evaluate((el) => ({
|
|
151
|
+
borderRadius: window.getComputedStyle(el).borderRadius,
|
|
152
|
+
cursor: window.getComputedStyle(el).cursor,
|
|
153
|
+
}));
|
|
154
|
+
expect(styles.borderRadius).toBe('16px');
|
|
155
|
+
expect(styles.cursor).toBe('pointer');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('should have appropriate dark mode styling', async ({ mcp }) => {
|
|
159
|
+
const result = await mcp.callTool('show-carousel', {}, { theme: 'dark' });
|
|
160
|
+
const app = result.app();
|
|
161
|
+
|
|
162
|
+
const card = app.locator('.rounded-2xl.border').first();
|
|
163
|
+
await expect(card).toBeVisible();
|
|
164
|
+
|
|
165
|
+
const styles = await card.evaluate((el) => ({
|
|
166
|
+
borderWidth: window.getComputedStyle(el).borderWidth,
|
|
167
|
+
borderStyle: window.getComputedStyle(el).borderStyle,
|
|
168
|
+
}));
|
|
169
|
+
expect(styles.borderWidth).toBe('1px');
|
|
170
|
+
expect(styles.borderStyle).toBe('solid');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('should load without console errors in dark mode', async ({ mcp }) => {
|
|
174
|
+
const errors: string[] = [];
|
|
175
|
+
mcp.page.on('console', (msg) => {
|
|
176
|
+
if (msg.type() === 'error') errors.push(msg.text());
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const result = await mcp.callTool('show-carousel', {}, { theme: 'dark' });
|
|
180
|
+
const app = result.app();
|
|
181
|
+
await expect(app.locator('.rounded-2xl').first()).toBeVisible();
|
|
182
|
+
|
|
183
|
+
const unexpectedErrors = errors.filter(
|
|
184
|
+
(e) =>
|
|
185
|
+
!e.includes('[IframeResource]') &&
|
|
186
|
+
!e.includes('mcp') &&
|
|
187
|
+
!e.includes('PostMessage') &&
|
|
188
|
+
!e.includes('connect')
|
|
189
|
+
);
|
|
190
|
+
expect(unexpectedErrors).toHaveLength(0);
|
|
191
|
+
});
|