sunpeak 0.19.12 → 0.20.2

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.
Files changed (85) hide show
  1. package/README.md +2 -2
  2. package/bin/commands/inspect.mjs +361 -12
  3. package/bin/commands/test-init.mjs +190 -118
  4. package/bin/commands/test.mjs +12 -1
  5. package/bin/lib/eval/eval-runner.mjs +7 -1
  6. package/bin/lib/inspect/inspect-config.mjs +17 -2
  7. package/bin/lib/inspect/inspect-server.d.mts +32 -0
  8. package/bin/lib/inspect/inspect-server.mjs +11 -0
  9. package/bin/lib/live/live-config.d.mts +10 -0
  10. package/bin/lib/live/live-config.mjs +34 -2
  11. package/bin/lib/resolve-bin.mjs +39 -0
  12. package/bin/lib/test/base-config.mjs +6 -3
  13. package/bin/lib/test/matchers.mjs +2 -2
  14. package/bin/lib/test/test-config.mjs +19 -8
  15. package/bin/lib/test/test-fixtures.d.mts +52 -92
  16. package/bin/lib/test/test-fixtures.mjs +174 -147
  17. package/dist/chatgpt/index.cjs +1 -1
  18. package/dist/chatgpt/index.js +1 -1
  19. package/dist/claude/index.cjs +1 -1
  20. package/dist/claude/index.js +1 -1
  21. package/dist/host/chatgpt/index.cjs +1 -1
  22. package/dist/host/chatgpt/index.js +1 -1
  23. package/dist/index.cjs +4 -4
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.js +3 -3
  26. package/dist/index.js.map +1 -1
  27. package/dist/inspector/index.cjs +1 -1
  28. package/dist/inspector/index.js +1 -1
  29. package/dist/{inspector-D5DckQuU.js → inspector-BBDa5yCm.js} +57 -23
  30. package/dist/inspector-BBDa5yCm.js.map +1 -0
  31. package/dist/{inspector-jY9O18z9.cjs → inspector-DAA1Wiyh.cjs} +58 -24
  32. package/dist/inspector-DAA1Wiyh.cjs.map +1 -0
  33. package/dist/lib/discovery-cli.cjs +1 -1
  34. package/dist/mcp/index.cjs +22 -25
  35. package/dist/mcp/index.cjs.map +1 -1
  36. package/dist/mcp/index.js +19 -22
  37. package/dist/mcp/index.js.map +1 -1
  38. package/dist/{use-app-Bfargfa3.js → use-app-Cr0auUa1.js} +2 -2
  39. package/dist/{use-app-Bfargfa3.js.map → use-app-Cr0auUa1.js.map} +1 -1
  40. package/dist/{use-app-CbsBEmwv.cjs → use-app-DPkj5Jp_.cjs} +2 -2
  41. package/dist/{use-app-CbsBEmwv.cjs.map → use-app-DPkj5Jp_.cjs.map} +1 -1
  42. package/package.json +17 -11
  43. package/template/dist/albums/albums.html +4 -4
  44. package/template/dist/albums/albums.json +1 -1
  45. package/template/dist/carousel/carousel.html +4 -4
  46. package/template/dist/carousel/carousel.json +1 -1
  47. package/template/dist/map/map.html +6 -6
  48. package/template/dist/map/map.json +1 -1
  49. package/template/dist/review/review.html +4 -4
  50. package/template/dist/review/review.json +1 -1
  51. package/template/node_modules/.bin/vite +2 -2
  52. package/template/node_modules/.bin/vitest +2 -2
  53. package/template/node_modules/.vite/deps/_metadata.json +4 -4
  54. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js +1 -1
  55. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js.map +1 -1
  56. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js +1 -1
  57. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js.map +1 -1
  58. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js +1 -1
  59. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js.map +1 -1
  60. package/template/node_modules/.vite-mcp/deps/@testing-library_react.js +4 -4
  61. package/template/node_modules/.vite-mcp/deps/@testing-library_react.js.map +1 -1
  62. package/template/node_modules/.vite-mcp/deps/_metadata.json +33 -33
  63. package/template/node_modules/.vite-mcp/deps/{client-CU1wWud4.js → client-B_5CX--u.js} +7 -7
  64. package/template/node_modules/.vite-mcp/deps/{client-CU1wWud4.js.map → client-B_5CX--u.js.map} +1 -1
  65. package/template/node_modules/.vite-mcp/deps/embla-carousel-react.js +1 -1
  66. package/template/node_modules/.vite-mcp/deps/embla-carousel-react.js.map +1 -1
  67. package/template/node_modules/.vite-mcp/deps/react-dom.js +3 -3
  68. package/template/node_modules/.vite-mcp/deps/react-dom.js.map +1 -1
  69. package/template/node_modules/.vite-mcp/deps/react-dom_client.js +1 -1
  70. package/template/node_modules/.vite-mcp/deps/react.js +3 -3
  71. package/template/node_modules/.vite-mcp/deps/react.js.map +1 -1
  72. package/template/node_modules/.vite-mcp/deps/react_jsx-dev-runtime.js +2 -2
  73. package/template/node_modules/.vite-mcp/deps/react_jsx-dev-runtime.js.map +1 -1
  74. package/template/node_modules/.vite-mcp/deps/react_jsx-runtime.js +2 -2
  75. package/template/node_modules/.vite-mcp/deps/react_jsx-runtime.js.map +1 -1
  76. package/template/node_modules/.vite-mcp/deps/vitest.js +1024 -622
  77. package/template/node_modules/.vite-mcp/deps/vitest.js.map +1 -1
  78. package/template/package.json +6 -6
  79. package/template/tests/e2e/albums.spec.ts +24 -52
  80. package/template/tests/e2e/carousel.spec.ts +36 -58
  81. package/template/tests/e2e/map.spec.ts +35 -56
  82. package/template/tests/e2e/review.spec.ts +56 -85
  83. package/template/tests/e2e/visual.spec.ts +14 -12
  84. package/dist/inspector-D5DckQuU.js.map +0 -1
  85. package/dist/inspector-jY9O18z9.cjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { test, expect } from 'sunpeak/test';
2
2
 
3
- test('should render review title with correct styles', async ({ mcp }) => {
4
- const result = await mcp.callTool('review-diff');
3
+ test('should render review title with correct styles', async ({ inspector }) => {
4
+ const result = await inspector.renderTool('review-diff');
5
5
  const app = result.app();
6
6
 
7
7
  const title = app.locator('h1:has-text("Refactor Authentication Module")');
@@ -13,8 +13,8 @@ test('should render review title with correct styles', async ({ mcp }) => {
13
13
  expect(parseInt(styles.fontWeight)).toBeGreaterThanOrEqual(600);
14
14
  });
15
15
 
16
- test('should render change items with type-specific styling', async ({ mcp }) => {
17
- const result = await mcp.callTool('review-diff');
16
+ test('should render change items with type-specific styling', async ({ inspector }) => {
17
+ const result = await inspector.renderTool('review-diff');
18
18
  const app = result.app();
19
19
 
20
20
  const changeItem = app.locator('li').first();
@@ -27,8 +27,8 @@ test('should render change items with type-specific styling', async ({ mcp }) =>
27
27
  expect(styles.backgroundColor).not.toBe('rgba(0, 0, 0, 0)');
28
28
  });
29
29
 
30
- test('should have interactive apply and cancel buttons', async ({ mcp }) => {
31
- const result = await mcp.callTool('review-diff');
30
+ test('should have interactive apply and cancel buttons', async ({ inspector }) => {
31
+ const result = await inspector.renderTool('review-diff');
32
32
  const app = result.app();
33
33
 
34
34
  const applyButton = app.locator('button:has-text("Apply Changes")');
@@ -40,8 +40,8 @@ test('should have interactive apply and cancel buttons', async ({ mcp }) => {
40
40
  expect(await cancelButton.evaluate((el) => window.getComputedStyle(el).cursor)).toBe('pointer');
41
41
  });
42
42
 
43
- test('should have expand fullscreen button in inline mode', async ({ mcp }) => {
44
- const result = await mcp.callTool('review-diff', {}, { displayMode: 'inline' });
43
+ test('should have expand fullscreen button in inline mode', async ({ inspector }) => {
44
+ const result = await inspector.renderTool('review-diff', undefined, { displayMode: 'inline' });
45
45
  const app = result.app();
46
46
 
47
47
  const expandButton = app.locator('button[aria-label="Enter fullscreen"]');
@@ -49,50 +49,20 @@ test('should have expand fullscreen button in inline mode', async ({ mcp }) => {
49
49
  expect(await expandButton.evaluate((el) => window.getComputedStyle(el).cursor)).toBe('pointer');
50
50
  });
51
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');
52
+ test('should activate prod resources mode without errors', async ({ inspector }) => {
53
+ await inspector.renderTool('review-diff', undefined, { theme: 'dark', prodResources: true });
54
+ const root = inspector.page.locator('#root');
85
55
  await expect(root).not.toBeEmpty();
86
56
  });
87
57
 
88
- test('should render review title in dark mode', async ({ mcp }) => {
89
- const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
58
+ test('should render review title in dark mode', async ({ inspector }) => {
59
+ const result = await inspector.renderTool('review-diff', undefined, { theme: 'dark' });
90
60
  const app = result.app();
91
61
  await expect(app.locator('h1:has-text("Refactor Authentication Module")')).toBeVisible();
92
62
  });
93
63
 
94
- test('should have appropriate text colors in dark mode', async ({ mcp }) => {
95
- const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
64
+ test('should have appropriate text colors in dark mode', async ({ inspector }) => {
65
+ const result = await inspector.renderTool('review-diff', undefined, { theme: 'dark' });
96
66
  const app = result.app();
97
67
 
98
68
  const title = app.locator('h1').first();
@@ -104,19 +74,19 @@ test('should have appropriate text colors in dark mode', async ({ mcp }) => {
104
74
  expect(styles.color).toBeTruthy();
105
75
  });
106
76
 
107
- test('should render change items in dark mode', async ({ mcp }) => {
108
- const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
77
+ test('should render change items in dark mode', async ({ inspector }) => {
78
+ const result = await inspector.renderTool('review-diff', undefined, { theme: 'dark' });
109
79
  const app = result.app();
110
80
  await expect(app.locator('li').first()).toBeVisible();
111
81
  });
112
82
 
113
- test('should load without console errors in dark mode', async ({ mcp }) => {
83
+ test('should load without console errors in dark mode', async ({ inspector }) => {
114
84
  const errors: string[] = [];
115
- mcp.page.on('console', (msg) => {
85
+ inspector.page.on('console', (msg) => {
116
86
  if (msg.type() === 'error') errors.push(msg.text());
117
87
  });
118
88
 
119
- const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
89
+ const result = await inspector.renderTool('review-diff', undefined, { theme: 'dark' });
120
90
  const app = result.app();
121
91
  await expect(app.locator('h1').first()).toBeVisible();
122
92
 
@@ -130,40 +100,41 @@ test('should load without console errors in dark mode', async ({ mcp }) => {
130
100
  expect(unexpectedErrors).toHaveLength(0);
131
101
  });
132
102
 
133
- test('should not show fullscreen button in fullscreen mode', async ({ mcp }) => {
134
- const result = await mcp.callTool('review-diff', {}, { displayMode: 'fullscreen' });
103
+ test('should not show fullscreen button in fullscreen mode', async ({ inspector }) => {
104
+ const result = await inspector.renderTool('review-diff', undefined, {
105
+ displayMode: 'fullscreen',
106
+ });
135
107
  const app = result.app();
136
108
 
137
109
  await expect(app.locator('h1').first()).toBeVisible();
138
110
  await expect(app.locator('button[aria-label="Enter fullscreen"]')).not.toBeVisible();
139
111
  });
140
112
 
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
- );
113
+ test('should render content in fullscreen mode', async ({ inspector }) => {
114
+ const result = await inspector.renderTool('review-diff', undefined, {
115
+ theme: 'dark',
116
+ displayMode: 'fullscreen',
117
+ });
147
118
  const app = result.app();
148
119
 
149
- await expect(mcp.page.locator('#root')).not.toBeEmpty();
120
+ await expect(inspector.page.locator('#root')).not.toBeEmpty();
150
121
  await expect(app.locator('h1')).toBeVisible();
151
122
  });
152
123
 
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();
124
+ test('should render post review in light mode', async ({ inspector }) => {
125
+ await inspector.renderTool('review-post');
126
+ await inspector.page.waitForLoadState('networkidle');
127
+ await expect(inspector.page.locator('#root')).not.toBeEmpty();
157
128
  });
158
129
 
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();
130
+ test('should render post review in dark mode', async ({ inspector }) => {
131
+ await inspector.renderTool('review-post', undefined, { theme: 'dark' });
132
+ await inspector.page.waitForLoadState('networkidle');
133
+ await expect(inspector.page.locator('#root')).not.toBeEmpty();
163
134
  });
164
135
 
165
- test('should show server success message when confirming post', async ({ mcp }) => {
166
- const result = await mcp.callTool('review-post', {}, { theme: 'dark' });
136
+ test('should show server success message when confirming post', async ({ inspector }) => {
137
+ const result = await inspector.renderTool('review-post', undefined, { theme: 'dark' });
167
138
  const app = result.app();
168
139
 
169
140
  const publishButton = app.locator('button:has-text("Publish")');
@@ -174,8 +145,8 @@ test('should show server success message when confirming post', async ({ mcp })
174
145
  await expect(app.locator('text=Publishing post...')).toBeVisible({ timeout: 10000 });
175
146
  });
176
147
 
177
- test('should show server cancel message when rejecting post', async ({ mcp }) => {
178
- const result = await mcp.callTool('review-post', {}, { theme: 'dark' });
148
+ test('should show server cancel message when rejecting post', async ({ inspector }) => {
149
+ const result = await inspector.renderTool('review-post', undefined, { theme: 'dark' });
179
150
  const app = result.app();
180
151
 
181
152
  const cancelButton = app.locator('button:has-text("Cancel")');
@@ -185,20 +156,20 @@ test('should show server cancel message when rejecting post', async ({ mcp }) =>
185
156
  await expect(app.locator('text=Cancelled.')).toBeVisible({ timeout: 10000 });
186
157
  });
187
158
 
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();
159
+ test('should render purchase review in light mode', async ({ inspector }) => {
160
+ await inspector.renderTool('review-purchase');
161
+ await inspector.page.waitForLoadState('networkidle');
162
+ await expect(inspector.page.locator('#root')).not.toBeEmpty();
192
163
  });
193
164
 
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();
165
+ test('should render purchase review in dark mode', async ({ inspector }) => {
166
+ await inspector.renderTool('review-purchase', undefined, { theme: 'dark' });
167
+ await inspector.page.waitForLoadState('networkidle');
168
+ await expect(inspector.page.locator('#root')).not.toBeEmpty();
198
169
  });
199
170
 
200
- test('should show loading then result when placing order', async ({ mcp }) => {
201
- const result = await mcp.callTool('review-purchase');
171
+ test('should show loading then result when placing order', async ({ inspector }) => {
172
+ const result = await inspector.renderTool('review-purchase');
202
173
  const app = result.app();
203
174
 
204
175
  const placeOrderButton = app.locator('button:has-text("Place Order")');
@@ -209,8 +180,8 @@ test('should show loading then result when placing order', async ({ mcp }) => {
209
180
  await expect(app.locator('text=Completed.')).toBeVisible({ timeout: 10000 });
210
181
  });
211
182
 
212
- test('should confirm review-diff and show server success', async ({ mcp }) => {
213
- const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
183
+ test('should confirm review-diff and show server success', async ({ inspector }) => {
184
+ const result = await inspector.renderTool('review-diff', undefined, { theme: 'dark' });
214
185
  const app = result.app();
215
186
 
216
187
  const applyButton = app.locator('button:has-text("Apply Changes")');
@@ -221,8 +192,8 @@ test('should confirm review-diff and show server success', async ({ mcp }) => {
221
192
  await expect(app.locator('text=Completed.')).toBeVisible({ timeout: 10000 });
222
193
  });
223
194
 
224
- test('should cancel review-diff and show server cancelled', async ({ mcp }) => {
225
- const result = await mcp.callTool('review-diff', {}, { theme: 'dark' });
195
+ test('should cancel review-diff and show server cancelled', async ({ inspector }) => {
196
+ const result = await inspector.renderTool('review-diff', undefined, { theme: 'dark' });
226
197
  const app = result.app();
227
198
 
228
199
  const cancelButton = app.locator('button:has-text("Cancel")');
@@ -3,34 +3,36 @@ import { test, expect } from 'sunpeak/test';
3
3
  // Visual regression tests. Screenshot comparisons only run with `sunpeak test --visual`.
4
4
  // Update baselines with `sunpeak test --visual --update`.
5
5
 
6
- test('albums renders correctly in light mode', async ({ mcp }) => {
7
- const result = await mcp.callTool('show-albums', {}, { theme: 'light' });
6
+ test('albums renders correctly in light mode', async ({ inspector }) => {
7
+ const result = await inspector.renderTool('show-albums', undefined, { theme: 'light' });
8
8
  const app = result.app();
9
9
  await expect(app.locator('button:has-text("Summer Slice")')).toBeVisible();
10
10
 
11
- await mcp.screenshot('albums-light');
11
+ await result.screenshot('albums-light');
12
12
  });
13
13
 
14
- test('albums renders correctly in dark mode', async ({ mcp }) => {
15
- const result = await mcp.callTool('show-albums', {}, { theme: 'dark' });
14
+ test('albums renders correctly in dark mode', async ({ inspector }) => {
15
+ const result = await inspector.renderTool('show-albums', undefined, { theme: 'dark' });
16
16
  const app = result.app();
17
17
  await expect(app.locator('button:has-text("Summer Slice")')).toBeVisible();
18
18
 
19
- await mcp.screenshot('albums-dark');
19
+ await result.screenshot('albums-dark');
20
20
  });
21
21
 
22
- test('albums renders correctly in fullscreen', async ({ mcp }) => {
23
- const result = await mcp.callTool('show-albums', {}, { displayMode: 'fullscreen' });
22
+ test('albums renders correctly in fullscreen', async ({ inspector }) => {
23
+ const result = await inspector.renderTool('show-albums', undefined, {
24
+ displayMode: 'fullscreen',
25
+ });
24
26
  const app = result.app();
25
27
  await expect(app.locator('button:has-text("Summer Slice")')).toBeVisible();
26
28
 
27
- await mcp.screenshot('albums-fullscreen');
29
+ await result.screenshot('albums-fullscreen');
28
30
  });
29
31
 
30
- test('full page renders correctly', async ({ mcp }) => {
31
- const result = await mcp.callTool('show-albums', {}, { theme: 'light' });
32
+ test('full page renders correctly', async ({ inspector }) => {
33
+ const result = await inspector.renderTool('show-albums', undefined, { theme: 'light' });
32
34
  const app = result.app();
33
35
  await expect(app.locator('button:has-text("Summer Slice")')).toBeVisible();
34
36
 
35
- await mcp.screenshot('albums-page-light', { target: 'page', maxDiffPixelRatio: 0.02 });
37
+ await result.screenshot('albums-page-light', { target: 'page', maxDiffPixelRatio: 0.02 });
36
38
  });