sunpeak 0.20.6 → 0.20.7

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.
@@ -61,12 +61,14 @@ export const defaultDeps = {
61
61
  * - JS/TS projects: root-level config + test files
62
62
  * - sunpeak projects: migrate to defineConfig()
63
63
  *
64
- * Scaffolds all 5 test types:
64
+ * Scaffolds 4 test types:
65
65
  * 1. E2E tests — Playwright-based inspector tests (mcp fixture)
66
66
  * 2. Visual regression — Screenshot comparison via result.screenshot()
67
67
  * 3. Live tests — Test against real ChatGPT/Claude hosts
68
68
  * 4. Evals — Multi-model tool calling reliability tests
69
- * 5. Unit tests — Direct tool handler tests (JS/TS projects only)
69
+ *
70
+ * Unit tests are not scaffolded here — they're part of the sunpeak app
71
+ * framework (`sunpeak new`) where tool handlers can be imported directly.
70
72
  */
71
73
  export async function testInit(args = [], deps = defaultDeps) {
72
74
  const d = { ...defaultDeps, ...deps };
@@ -532,56 +534,6 @@ export default defineLiveConfig({${serverOption}
532
534
  d.log.success(`Created ${liveDir}/ with live test config and example.`);
533
535
  }
534
536
 
535
- /**
536
- * Scaffold a unit test example for JS/TS projects.
537
- * @param {string} filePath - Full path to the unit test file
538
- * @param {object} d - Dependencies
539
- */
540
- function scaffoldUnitTest(filePath, d) {
541
- if (d.existsSync(filePath)) {
542
- d.log.info('Unit test already exists. Skipping.');
543
- return;
544
- }
545
-
546
- d.mkdirSync(dirname(filePath), { recursive: true });
547
-
548
- d.writeFileSync(
549
- filePath,
550
- `import { describe, it, expect } from 'vitest';
551
-
552
- /**
553
- * Unit tests for your MCP tool handlers.
554
- *
555
- * Import your tool handler directly and test its input/output
556
- * without starting the MCP server or inspector.
557
- *
558
- * Run with: npx sunpeak test --unit
559
- *
560
- * To set up vitest, add it to your devDependencies:
561
- * npm install -D vitest
562
- *
563
- * Uncomment and customize the tests below for your tools.
564
- */
565
-
566
- // import handler, { tool, schema } from '../../src/tools/your-tool';
567
- // const extra = {} as Parameters<typeof handler>[1];
568
-
569
- // describe('your tool', () => {
570
- // it('returns expected output', async () => {
571
- // const result = await handler({ key: 'value' }, extra);
572
- // expect(result.structuredContent).toBeDefined();
573
- // });
574
- //
575
- // it('exports correct tool config', () => {
576
- // expect(tool.title).toBe('Your Tool');
577
- // expect(tool.annotations?.readOnlyHint).toBe(true);
578
- // });
579
- // });
580
- `
581
- );
582
- d.log.success(`Created ${filePath}`);
583
- }
584
-
585
537
  async function initExternalProject(cliServer, d) {
586
538
  d.log.info('Detected non-JS project. Creating self-contained test directory.');
587
539
 
@@ -715,6 +667,22 @@ async function initJsProject(cliServer, d) {
715
667
  const server = await getServerConfig(cliServer, d);
716
668
  const cwd = d.cwd();
717
669
 
670
+ // Ensure "type": "module" — sunpeak exports are ESM-only and Playwright's
671
+ // CJS resolver won't find them without it.
672
+ const pkgPath = join(cwd, 'package.json');
673
+ if (d.existsSync(pkgPath)) {
674
+ try {
675
+ const pkg = JSON.parse(d.readFileSync(pkgPath, 'utf-8'));
676
+ if (pkg.type !== 'module') {
677
+ pkg.type = 'module';
678
+ d.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
679
+ d.log.success('Set "type": "module" in package.json (required for sunpeak imports)');
680
+ }
681
+ } catch {
682
+ d.log.warn('Could not read package.json. Make sure "type": "module" is set.');
683
+ }
684
+ }
685
+
718
686
  // Create playwright.config.ts
719
687
  const configPath = join(cwd, 'playwright.config.ts');
720
688
  if (d.existsSync(configPath)) {
@@ -775,19 +743,15 @@ test('server exposes tools', async ({ mcp }) => {
775
743
  // 4. Eval boilerplate
776
744
  scaffoldEvals(join(cwd, 'tests', 'evals'), { server, d });
777
745
 
778
- // 5. Unit test
779
- scaffoldUnitTest(join(cwd, 'tests', 'unit', 'example.test.ts'), d);
780
-
781
746
  if (server.type === 'later') {
782
747
  d.log.warn('Server not configured. Edit playwright.config.ts before running tests.');
783
748
  }
784
749
  const pkgMgr = d.detectPackageManager();
785
750
  d.log.step('Next steps:');
786
- d.log.message(` ${pkgMgr} add -D sunpeak @playwright/test vitest`);
751
+ d.log.message(` ${pkgMgr} add -D sunpeak @playwright/test`);
787
752
  d.log.message(` ${pkgMgr} exec playwright install chromium`);
788
753
  d.log.message('');
789
754
  d.log.message(' npx sunpeak test # E2E tests');
790
- d.log.message(' npx sunpeak test --unit # Unit tests (vitest)');
791
755
  d.log.message(' npx sunpeak test --visual # Visual regression');
792
756
  d.log.message(' npx sunpeak test --live # Live tests against real hosts');
793
757
  d.log.message(' npx sunpeak test --eval # Multi-model evals');
@@ -833,14 +797,10 @@ export default defineConfig();
833
797
  // 3. Eval boilerplate
834
798
  scaffoldEvals(join(cwd, 'tests', 'evals'), { isSunpeak: true, d });
835
799
 
836
- // 4. Unit test
837
- scaffoldUnitTest(join(cwd, 'tests', 'unit', 'example.test.ts'), d);
838
-
839
800
  d.log.step('Scaffolded test types:');
840
801
  d.log.message(' tests/e2e/visual.test.ts — Visual regression (npx sunpeak test --visual)');
841
802
  d.log.message(' tests/live/ — Live host tests (npx sunpeak test --live)');
842
803
  d.log.message(' tests/evals/ — Multi-model evals (npx sunpeak test --eval)');
843
- d.log.message(' tests/unit/example.test.ts — Unit tests (npx sunpeak test --unit)');
844
804
  d.log.message('');
845
805
  d.log.message(' Migrate existing e2e tests:');
846
806
  d.log.message(' Replace: import { test, expect } from "@playwright/test"');
@@ -59,8 +59,17 @@ export async function runTest(args) {
59
59
  const results = [];
60
60
 
61
61
  if (runUnit) {
62
- const code = await runChild('pnpm', ['exec', 'vitest', 'run', ...filteredArgs]);
63
- results.push({ suite: 'unit', code });
62
+ // Only run unit tests if vitest is available (app framework projects have it,
63
+ // standalone testing framework projects don't).
64
+ const hasVitest = existsSync(join(process.cwd(), 'node_modules', '.bin', 'vitest'));
65
+ if (hasVitest) {
66
+ const code = await runChild('pnpm', ['exec', 'vitest', 'run', ...filteredArgs]);
67
+ results.push({ suite: 'unit', code });
68
+ } else if (isUnit) {
69
+ // Only warn if the user explicitly asked for --unit
70
+ console.error('vitest is not installed. Install it with: npm add -D vitest');
71
+ results.push({ suite: 'unit', code: 1 });
72
+ }
64
73
  }
65
74
 
66
75
  if (runE2e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sunpeak",
3
- "version": "0.20.6",
3
+ "version": "0.20.7",
4
4
  "description": "App framework, testing framework, and inspector for MCP Apps.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -12,5 +12,5 @@
12
12
  }
13
13
  },
14
14
  "name": "albums",
15
- "uri": "ui://albums-mo0qm3p4"
15
+ "uri": "ui://albums-mo0td9su"
16
16
  }
@@ -12,5 +12,5 @@
12
12
  }
13
13
  },
14
14
  "name": "carousel",
15
- "uri": "ui://carousel-mo0qm3p4"
15
+ "uri": "ui://carousel-mo0td9su"
16
16
  }
@@ -18,5 +18,5 @@
18
18
  }
19
19
  },
20
20
  "name": "map",
21
- "uri": "ui://map-mo0qm3p4"
21
+ "uri": "ui://map-mo0td9su"
22
22
  }
@@ -12,5 +12,5 @@
12
12
  }
13
13
  },
14
14
  "name": "review",
15
- "uri": "ui://review-mo0qm3p4"
15
+ "uri": "ui://review-mo0td9su"
16
16
  }