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,424 +1,233 @@
|
|
|
1
|
-
import { test, expect } from '
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
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
|
-
await page.goto(createInspectorUrl({ tool: 'review-diff', theme: 'light', host }));
|
|
118
|
-
|
|
119
|
-
const emptyState = page.locator('text=Press Run to call the tool');
|
|
120
|
-
await expect(emptyState).toBeVisible();
|
|
121
|
-
|
|
122
|
-
const color = await emptyState.evaluate((el) => {
|
|
123
|
-
return window.getComputedStyle(el).color;
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const [r, g, b] = color.match(/\d+/g)!.map(Number);
|
|
127
|
-
expect(r + g + b).toBeLessThan(600);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('should have themed empty state colors in dark mode', async ({ page }) => {
|
|
131
|
-
await page.goto(createInspectorUrl({ tool: 'review-diff', theme: 'dark', host }));
|
|
132
|
-
|
|
133
|
-
const emptyState = page.locator('text=Press Run to call the tool');
|
|
134
|
-
await expect(emptyState).toBeVisible();
|
|
135
|
-
|
|
136
|
-
const color = await emptyState.evaluate((el) => {
|
|
137
|
-
return window.getComputedStyle(el).color;
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const [r, g, b] = color.match(/\d+/g)!.map(Number);
|
|
141
|
-
expect(r + g + b).toBeGreaterThan(200);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test.describe('Prod Resources Mode', () => {
|
|
146
|
-
test('should activate without errors', async ({ page }) => {
|
|
147
|
-
await page.goto(
|
|
148
|
-
createInspectorUrl({
|
|
149
|
-
simulation: 'review-diff',
|
|
150
|
-
theme: 'dark',
|
|
151
|
-
host,
|
|
152
|
-
prodResources: true,
|
|
153
|
-
})
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
const root = page.locator('#root');
|
|
157
|
-
await expect(root).not.toBeEmpty();
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test.describe('Dark Mode', () => {
|
|
162
|
-
test('should render review title with correct styles', async ({ page }) => {
|
|
163
|
-
await page.goto(createInspectorUrl({ simulation: 'review-diff', theme: 'dark', host }));
|
|
164
|
-
|
|
165
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
166
|
-
const title = iframe.locator('h1:has-text("Refactor Authentication Module")');
|
|
167
|
-
await expect(title).toBeVisible();
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
test('should have appropriate text colors for dark mode', async ({ page }) => {
|
|
171
|
-
await page.goto(createInspectorUrl({ simulation: 'review-diff', theme: 'dark', host }));
|
|
172
|
-
|
|
173
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
174
|
-
const title = iframe.locator('h1').first();
|
|
175
|
-
await expect(title).toBeVisible();
|
|
176
|
-
|
|
177
|
-
const styles = await title.evaluate((el) => {
|
|
178
|
-
const computed = window.getComputedStyle(el);
|
|
179
|
-
return {
|
|
180
|
-
color: computed.color,
|
|
181
|
-
};
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// In dark mode, text color should be light
|
|
185
|
-
expect(styles.color).toBeTruthy();
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test('should render change items in dark mode', async ({ page }) => {
|
|
189
|
-
await page.goto(createInspectorUrl({ simulation: 'review-diff', theme: 'dark', host }));
|
|
190
|
-
|
|
191
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
192
|
-
const changeItem = iframe.locator('li').first();
|
|
193
|
-
await expect(changeItem).toBeVisible();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test('should load without console errors', async ({ page }) => {
|
|
197
|
-
const errors: string[] = [];
|
|
198
|
-
page.on('console', (msg) => {
|
|
199
|
-
if (msg.type() === 'error') {
|
|
200
|
-
errors.push(msg.text());
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
await page.goto(createInspectorUrl({ simulation: 'review-diff', theme: 'dark', host }));
|
|
205
|
-
|
|
206
|
-
// Wait for iframe content to render
|
|
207
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
208
|
-
await expect(iframe.locator('h1').first()).toBeVisible();
|
|
209
|
-
|
|
210
|
-
// Filter out expected iframe/MCP handshake errors
|
|
211
|
-
const unexpectedErrors = errors.filter(
|
|
212
|
-
(e) =>
|
|
213
|
-
!e.includes('[IframeResource]') &&
|
|
214
|
-
!e.includes('mcp') &&
|
|
215
|
-
!e.includes('PostMessage') &&
|
|
216
|
-
!e.includes('connect')
|
|
217
|
-
);
|
|
218
|
-
expect(unexpectedErrors).toHaveLength(0);
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
test.describe('Fullscreen Mode', () => {
|
|
223
|
-
test('should not show fullscreen button when already in fullscreen', async ({ page }) => {
|
|
224
|
-
await page.goto(
|
|
225
|
-
createInspectorUrl({
|
|
226
|
-
simulation: 'review-diff',
|
|
227
|
-
theme: 'light',
|
|
228
|
-
displayMode: 'fullscreen',
|
|
229
|
-
host,
|
|
230
|
-
})
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
234
|
-
// Wait for content to render first
|
|
235
|
-
await expect(iframe.locator('h1').first()).toBeVisible();
|
|
236
|
-
|
|
237
|
-
// The expand button should not be visible in fullscreen mode
|
|
238
|
-
const expandButton = iframe.locator('button[aria-label="Enter fullscreen"]');
|
|
239
|
-
await expect(expandButton).not.toBeVisible();
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test('should render content correctly in fullscreen', async ({ page }) => {
|
|
243
|
-
await page.goto(
|
|
244
|
-
createInspectorUrl({
|
|
245
|
-
simulation: 'review-diff',
|
|
246
|
-
theme: 'dark',
|
|
247
|
-
displayMode: 'fullscreen',
|
|
248
|
-
host,
|
|
249
|
-
})
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
// The root container should be present
|
|
253
|
-
const root = page.locator('#root');
|
|
254
|
-
await expect(root).not.toBeEmpty();
|
|
255
|
-
|
|
256
|
-
// Title should be visible inside the iframe
|
|
257
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
258
|
-
const title = iframe.locator('h1');
|
|
259
|
-
await expect(title).toBeVisible();
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
test('should render content in fullscreen mode', async ({ page }) => {
|
|
263
|
-
await page.goto(
|
|
264
|
-
createInspectorUrl({
|
|
265
|
-
simulation: 'review-diff',
|
|
266
|
-
theme: 'light',
|
|
267
|
-
displayMode: 'fullscreen',
|
|
268
|
-
host,
|
|
269
|
-
})
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
273
|
-
// Content sections should be visible in fullscreen
|
|
274
|
-
const title = iframe.locator('h1');
|
|
275
|
-
await expect(title).toBeVisible();
|
|
276
|
-
|
|
277
|
-
// Fullscreen expand button should NOT be visible (already in fullscreen)
|
|
278
|
-
const expandButton = iframe.locator('button[aria-label="Enter fullscreen"]');
|
|
279
|
-
await expect(expandButton).toHaveCount(0);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
test.describe('Review Post Simulation', () => {
|
|
284
|
-
test('should render post review in light mode', async ({ page }) => {
|
|
285
|
-
await page.goto(createInspectorUrl({ simulation: 'review-post', theme: 'light', host }));
|
|
286
|
-
|
|
287
|
-
await page.waitForLoadState('networkidle');
|
|
288
|
-
|
|
289
|
-
// Should render the review content
|
|
290
|
-
const root = page.locator('#root');
|
|
291
|
-
await expect(root).not.toBeEmpty();
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
test('should render post review in dark mode', async ({ page }) => {
|
|
295
|
-
await page.goto(createInspectorUrl({ simulation: 'review-post', theme: 'dark', host }));
|
|
296
|
-
|
|
297
|
-
await page.waitForLoadState('networkidle');
|
|
298
|
-
|
|
299
|
-
const root = page.locator('#root');
|
|
300
|
-
await expect(root).not.toBeEmpty();
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
test('should show server success message when confirming', async ({ page }) => {
|
|
304
|
-
await page.goto(
|
|
305
|
-
createInspectorUrl({
|
|
306
|
-
simulation: 'review-post',
|
|
307
|
-
theme: 'dark',
|
|
308
|
-
host,
|
|
309
|
-
})
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
313
|
-
const publishButton = iframe.locator('button:has-text("Publish")');
|
|
314
|
-
await expect(publishButton).toBeVisible();
|
|
315
|
-
// Use evaluate to dispatch click directly — Playwright's coordinate-based
|
|
316
|
-
// click can miss the target inside the double cross-origin iframe.
|
|
317
|
-
await publishButton.evaluate((el) => (el as HTMLElement).click());
|
|
318
|
-
|
|
319
|
-
// Should show the server's success message from serverTools mock
|
|
320
|
-
await expect(iframe.locator('text=Completed.')).toBeVisible({ timeout: 10000 });
|
|
321
|
-
// Should also show what button was pressed
|
|
322
|
-
await expect(iframe.locator('text=Publishing post...')).toBeVisible({ timeout: 10000 });
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
test('should show server cancel message when rejecting', async ({ page }) => {
|
|
326
|
-
await page.goto(
|
|
327
|
-
createInspectorUrl({
|
|
328
|
-
simulation: 'review-post',
|
|
329
|
-
theme: 'dark',
|
|
330
|
-
host,
|
|
331
|
-
})
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
335
|
-
const cancelButton = iframe.locator('button:has-text("Cancel")');
|
|
336
|
-
await expect(cancelButton).toBeVisible();
|
|
337
|
-
await cancelButton.evaluate((el) => (el as HTMLElement).click());
|
|
338
|
-
|
|
339
|
-
// Server returned cancelled status via serverTools when condition
|
|
340
|
-
await expect(iframe.locator('text=Cancelled.')).toBeVisible({ timeout: 10000 });
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
test.describe('Review Purchase Simulation', () => {
|
|
345
|
-
test('should render purchase review in light mode', async ({ page }) => {
|
|
346
|
-
await page.goto(
|
|
347
|
-
createInspectorUrl({ simulation: 'review-purchase', theme: 'light', host })
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
await page.waitForLoadState('networkidle');
|
|
351
|
-
|
|
352
|
-
const root = page.locator('#root');
|
|
353
|
-
await expect(root).not.toBeEmpty();
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
test('should render purchase review in dark mode', async ({ page }) => {
|
|
357
|
-
await page.goto(createInspectorUrl({ simulation: 'review-purchase', theme: 'dark', host }));
|
|
358
|
-
|
|
359
|
-
await page.waitForLoadState('networkidle');
|
|
360
|
-
|
|
361
|
-
const root = page.locator('#root');
|
|
362
|
-
await expect(root).not.toBeEmpty();
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
test('should show loading then result when placing order', async ({ page }) => {
|
|
366
|
-
await page.goto(
|
|
367
|
-
createInspectorUrl({
|
|
368
|
-
simulation: 'review-purchase',
|
|
369
|
-
theme: 'light',
|
|
370
|
-
host,
|
|
371
|
-
})
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
375
|
-
const placeOrderButton = iframe.locator('button:has-text("Place Order")');
|
|
376
|
-
await expect(placeOrderButton).toBeVisible();
|
|
377
|
-
await placeOrderButton.evaluate((el) => (el as HTMLElement).click());
|
|
378
|
-
|
|
379
|
-
// After server responds, should show what the user clicked and the server result
|
|
380
|
-
await expect(iframe.locator('text=Placing order...')).toBeVisible({ timeout: 10000 });
|
|
381
|
-
await expect(iframe.locator('text=Completed.')).toBeVisible({ timeout: 10000 });
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
test.describe('Server Tool Simulation via serverTools field', () => {
|
|
386
|
-
test('should confirm review-diff and show server success', async ({ page }) => {
|
|
387
|
-
await page.goto(
|
|
388
|
-
createInspectorUrl({
|
|
389
|
-
simulation: 'review-diff',
|
|
390
|
-
theme: 'dark',
|
|
391
|
-
host,
|
|
392
|
-
})
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
396
|
-
const applyButton = iframe.locator('button:has-text("Apply Changes")');
|
|
397
|
-
await expect(applyButton).toBeVisible();
|
|
398
|
-
await applyButton.evaluate((el) => (el as HTMLElement).click());
|
|
399
|
-
|
|
400
|
-
// Should show the decision label and server response
|
|
401
|
-
await expect(iframe.locator('text=Applying changes...')).toBeVisible({ timeout: 10000 });
|
|
402
|
-
await expect(iframe.locator('text=Completed.')).toBeVisible({ timeout: 10000 });
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
test('should cancel review-diff and show server cancelled', async ({ page }) => {
|
|
406
|
-
await page.goto(
|
|
407
|
-
createInspectorUrl({
|
|
408
|
-
simulation: 'review-diff',
|
|
409
|
-
theme: 'dark',
|
|
410
|
-
host,
|
|
411
|
-
})
|
|
412
|
-
);
|
|
413
|
-
|
|
414
|
-
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
415
|
-
const cancelButton = iframe.locator('button:has-text("Cancel")');
|
|
416
|
-
await expect(cancelButton).toBeVisible();
|
|
417
|
-
await cancelButton.evaluate((el) => (el as HTMLElement).click());
|
|
418
|
-
|
|
419
|
-
// Server returned cancelled status via when condition matching
|
|
420
|
-
await expect(iframe.locator('text=Cancelled.')).toBeVisible({ timeout: 10000 });
|
|
421
|
-
});
|
|
422
|
-
});
|
|
1
|
+
import { test, expect } from 'sunpeak/test';
|
|
2
|
+
|
|
3
|
+
test('should render review title with correct styles', async ({ mcp }) => {
|
|
4
|
+
const result = await mcp.callTool('review-diff');
|
|
5
|
+
const app = result.app();
|
|
6
|
+
|
|
7
|
+
const title = app.locator('h1:has-text("Refactor Authentication Module")');
|
|
8
|
+
await expect(title).toBeVisible();
|
|
9
|
+
|
|
10
|
+
const styles = await title.evaluate((el) => ({
|
|
11
|
+
fontWeight: window.getComputedStyle(el).fontWeight,
|
|
12
|
+
}));
|
|
13
|
+
expect(parseInt(styles.fontWeight)).toBeGreaterThanOrEqual(600);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('should render change items with type-specific styling', async ({ mcp }) => {
|
|
17
|
+
const result = await mcp.callTool('review-diff');
|
|
18
|
+
const app = result.app();
|
|
19
|
+
|
|
20
|
+
const changeItem = app.locator('li').first();
|
|
21
|
+
await expect(changeItem).toBeVisible();
|
|
22
|
+
|
|
23
|
+
const styles = await changeItem.evaluate((el) => ({
|
|
24
|
+
backgroundColor: window.getComputedStyle(el).backgroundColor,
|
|
25
|
+
}));
|
|
26
|
+
expect(styles.backgroundColor).toBeTruthy();
|
|
27
|
+
expect(styles.backgroundColor).not.toBe('rgba(0, 0, 0, 0)');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('should have interactive apply and cancel buttons', async ({ mcp }) => {
|
|
31
|
+
const result = await mcp.callTool('review-diff');
|
|
32
|
+
const app = result.app();
|
|
33
|
+
|
|
34
|
+
const applyButton = app.locator('button:has-text("Apply Changes")');
|
|
35
|
+
await expect(applyButton).toBeVisible();
|
|
36
|
+
expect(await applyButton.evaluate((el) => window.getComputedStyle(el).cursor)).toBe('pointer');
|
|
37
|
+
|
|
38
|
+
const cancelButton = app.locator('button:has-text("Cancel")');
|
|
39
|
+
await expect(cancelButton).toBeVisible();
|
|
40
|
+
expect(await cancelButton.evaluate((el) => window.getComputedStyle(el).cursor)).toBe('pointer');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should have expand fullscreen button in inline mode', async ({ mcp }) => {
|
|
44
|
+
const result = await mcp.callTool('review-diff', {}, { displayMode: 'inline' });
|
|
45
|
+
const app = result.app();
|
|
46
|
+
|
|
47
|
+
const expandButton = app.locator('button[aria-label="Enter fullscreen"]');
|
|
48
|
+
await expect(expandButton).toBeVisible();
|
|
49
|
+
expect(await expandButton.evaluate((el) => window.getComputedStyle(el).cursor)).toBe('pointer');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('should show empty state with Run button in prod tools mode', async ({ mcp }) => {
|
|
53
|
+
await mcp.openTool('review-diff', { theme: 'dark' });
|
|
54
|
+
|
|
55
|
+
await expect(mcp.page.locator('text=Press Run to call the tool')).toBeVisible();
|
|
56
|
+
await expect(mcp.page.locator('button:has-text("Run")')).toBeVisible();
|
|
57
|
+
await expect(mcp.page.locator('iframe')).not.toBeAttached();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('should have themed empty state colors in light mode', async ({ mcp }) => {
|
|
61
|
+
await mcp.openTool('review-diff', { theme: 'light' });
|
|
62
|
+
|
|
63
|
+
const emptyState = mcp.page.locator('text=Press Run to call the tool');
|
|
64
|
+
await expect(emptyState).toBeVisible();
|
|
65
|
+
|
|
66
|
+
const color = await emptyState.evaluate((el) => window.getComputedStyle(el).color);
|
|
67
|
+
const [r, g, b] = color.match(/\d+/g)!.map(Number);
|
|
68
|
+
expect(r + g + b).toBeLessThan(600);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('should have themed empty state colors in dark mode', async ({ mcp }) => {
|
|
72
|
+
await mcp.openTool('review-diff', { theme: 'dark' });
|
|
73
|
+
|
|
74
|
+
const emptyState = mcp.page.locator('text=Press Run to call the tool');
|
|
75
|
+
await expect(emptyState).toBeVisible();
|
|
76
|
+
|
|
77
|
+
const color = await emptyState.evaluate((el) => window.getComputedStyle(el).color);
|
|
78
|
+
const [r, g, b] = color.match(/\d+/g)!.map(Number);
|
|
79
|
+
expect(r + g + b).toBeGreaterThan(200);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('should activate prod resources mode without errors', async ({ mcp }) => {
|
|
83
|
+
await mcp.callTool('review-diff', {}, { theme: 'dark', prodResources: true });
|
|
84
|
+
const root = mcp.page.locator('#root');
|
|
85
|
+
await expect(root).not.toBeEmpty();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('should render review title in dark mode', async ({ mcp }) => {
|
|
89
|
+
const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
|
|
90
|
+
const app = result.app();
|
|
91
|
+
await expect(app.locator('h1:has-text("Refactor Authentication Module")')).toBeVisible();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('should have appropriate text colors in dark mode', async ({ mcp }) => {
|
|
95
|
+
const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
|
|
96
|
+
const app = result.app();
|
|
97
|
+
|
|
98
|
+
const title = app.locator('h1').first();
|
|
99
|
+
await expect(title).toBeVisible();
|
|
100
|
+
|
|
101
|
+
const styles = await title.evaluate((el) => ({
|
|
102
|
+
color: window.getComputedStyle(el).color,
|
|
103
|
+
}));
|
|
104
|
+
expect(styles.color).toBeTruthy();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('should render change items in dark mode', async ({ mcp }) => {
|
|
108
|
+
const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
|
|
109
|
+
const app = result.app();
|
|
110
|
+
await expect(app.locator('li').first()).toBeVisible();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('should load without console errors in dark mode', async ({ mcp }) => {
|
|
114
|
+
const errors: string[] = [];
|
|
115
|
+
mcp.page.on('console', (msg) => {
|
|
116
|
+
if (msg.type() === 'error') errors.push(msg.text());
|
|
423
117
|
});
|
|
424
|
-
|
|
118
|
+
|
|
119
|
+
const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
|
|
120
|
+
const app = result.app();
|
|
121
|
+
await expect(app.locator('h1').first()).toBeVisible();
|
|
122
|
+
|
|
123
|
+
const unexpectedErrors = errors.filter(
|
|
124
|
+
(e) =>
|
|
125
|
+
!e.includes('[IframeResource]') &&
|
|
126
|
+
!e.includes('mcp') &&
|
|
127
|
+
!e.includes('PostMessage') &&
|
|
128
|
+
!e.includes('connect')
|
|
129
|
+
);
|
|
130
|
+
expect(unexpectedErrors).toHaveLength(0);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('should not show fullscreen button in fullscreen mode', async ({ mcp }) => {
|
|
134
|
+
const result = await mcp.callTool('review-diff', {}, { displayMode: 'fullscreen' });
|
|
135
|
+
const app = result.app();
|
|
136
|
+
|
|
137
|
+
await expect(app.locator('h1').first()).toBeVisible();
|
|
138
|
+
await expect(app.locator('button[aria-label="Enter fullscreen"]')).not.toBeVisible();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('should render content in fullscreen mode', async ({ mcp }) => {
|
|
142
|
+
const result = await mcp.callTool(
|
|
143
|
+
'review-diff',
|
|
144
|
+
{},
|
|
145
|
+
{ theme: 'dark', displayMode: 'fullscreen' }
|
|
146
|
+
);
|
|
147
|
+
const app = result.app();
|
|
148
|
+
|
|
149
|
+
await expect(mcp.page.locator('#root')).not.toBeEmpty();
|
|
150
|
+
await expect(app.locator('h1')).toBeVisible();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('should render post review in light mode', async ({ mcp }) => {
|
|
154
|
+
await mcp.callTool('review-post');
|
|
155
|
+
await mcp.page.waitForLoadState('networkidle');
|
|
156
|
+
await expect(mcp.page.locator('#root')).not.toBeEmpty();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('should render post review in dark mode', async ({ mcp }) => {
|
|
160
|
+
await mcp.callTool('review-post', {}, { theme: 'dark' });
|
|
161
|
+
await mcp.page.waitForLoadState('networkidle');
|
|
162
|
+
await expect(mcp.page.locator('#root')).not.toBeEmpty();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('should show server success message when confirming post', async ({ mcp }) => {
|
|
166
|
+
const result = await mcp.callTool('review-post', {}, { theme: 'dark' });
|
|
167
|
+
const app = result.app();
|
|
168
|
+
|
|
169
|
+
const publishButton = app.locator('button:has-text("Publish")');
|
|
170
|
+
await expect(publishButton).toBeVisible();
|
|
171
|
+
await publishButton.evaluate((el) => (el as HTMLElement).click());
|
|
172
|
+
|
|
173
|
+
await expect(app.locator('text=Completed.')).toBeVisible({ timeout: 10000 });
|
|
174
|
+
await expect(app.locator('text=Publishing post...')).toBeVisible({ timeout: 10000 });
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('should show server cancel message when rejecting post', async ({ mcp }) => {
|
|
178
|
+
const result = await mcp.callTool('review-post', {}, { theme: 'dark' });
|
|
179
|
+
const app = result.app();
|
|
180
|
+
|
|
181
|
+
const cancelButton = app.locator('button:has-text("Cancel")');
|
|
182
|
+
await expect(cancelButton).toBeVisible();
|
|
183
|
+
await cancelButton.evaluate((el) => (el as HTMLElement).click());
|
|
184
|
+
|
|
185
|
+
await expect(app.locator('text=Cancelled.')).toBeVisible({ timeout: 10000 });
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('should render purchase review in light mode', async ({ mcp }) => {
|
|
189
|
+
await mcp.callTool('review-purchase');
|
|
190
|
+
await mcp.page.waitForLoadState('networkidle');
|
|
191
|
+
await expect(mcp.page.locator('#root')).not.toBeEmpty();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('should render purchase review in dark mode', async ({ mcp }) => {
|
|
195
|
+
await mcp.callTool('review-purchase', {}, { theme: 'dark' });
|
|
196
|
+
await mcp.page.waitForLoadState('networkidle');
|
|
197
|
+
await expect(mcp.page.locator('#root')).not.toBeEmpty();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('should show loading then result when placing order', async ({ mcp }) => {
|
|
201
|
+
const result = await mcp.callTool('review-purchase');
|
|
202
|
+
const app = result.app();
|
|
203
|
+
|
|
204
|
+
const placeOrderButton = app.locator('button:has-text("Place Order")');
|
|
205
|
+
await expect(placeOrderButton).toBeVisible();
|
|
206
|
+
await placeOrderButton.evaluate((el) => (el as HTMLElement).click());
|
|
207
|
+
|
|
208
|
+
await expect(app.locator('text=Placing order...')).toBeVisible({ timeout: 10000 });
|
|
209
|
+
await expect(app.locator('text=Completed.')).toBeVisible({ timeout: 10000 });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test('should confirm review-diff and show server success', async ({ mcp }) => {
|
|
213
|
+
const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
|
|
214
|
+
const app = result.app();
|
|
215
|
+
|
|
216
|
+
const applyButton = app.locator('button:has-text("Apply Changes")');
|
|
217
|
+
await expect(applyButton).toBeVisible();
|
|
218
|
+
await applyButton.evaluate((el) => (el as HTMLElement).click());
|
|
219
|
+
|
|
220
|
+
await expect(app.locator('text=Applying changes...')).toBeVisible({ timeout: 10000 });
|
|
221
|
+
await expect(app.locator('text=Completed.')).toBeVisible({ timeout: 10000 });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('should cancel review-diff and show server cancelled', async ({ mcp }) => {
|
|
225
|
+
const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
|
|
226
|
+
const app = result.app();
|
|
227
|
+
|
|
228
|
+
const cancelButton = app.locator('button:has-text("Cancel")');
|
|
229
|
+
await expect(cancelButton).toBeVisible();
|
|
230
|
+
await cancelButton.evaluate((el) => (el as HTMLElement).click());
|
|
231
|
+
|
|
232
|
+
await expect(app.locator('text=Cancelled.')).toBeVisible({ timeout: 10000 });
|
|
233
|
+
});
|