11import { Context } from '../../context.js' ;
2- import { output } from '../../../utils/utils.console.js' ;
32import appSdk from './experimental-app-sdk.js' ;
43
54vi . mock ( import ( '../../../utils/utils.plugin.js' ) , async ( importOriginal ) => {
@@ -10,15 +9,11 @@ vi.mock(import('../../../utils/utils.plugin.js'), async (importOriginal) => {
109 } ;
1110} ) ;
1211
13-
1412vi . mock ( import ( '../../utils.js' ) , async ( importOriginal ) => {
1513 const originalModule = await importOriginal ( ) ;
1614 // Disk I/O is slow so render the templates once and key off the requested path.
1715 const render = ( file : string ) =>
18- originalModule . renderTemplate (
19- new URL ( `../../../../templates/app-sdk/${ file } ` , import . meta. url ) . pathname ,
20- false
21- ) ;
16+ originalModule . renderTemplate ( new URL ( `../../../../templates/app-sdk/${ file } ` , import . meta. url ) . pathname , false ) ;
2217 const rendered : Record < string , string > = {
2318 '.config/app-sdk/generate-kinds.mjs' : render ( '.config/app-sdk/generate-kinds.mjs' ) ,
2419 '.config/AGENTS/app-sdk.md' : render ( '.config/AGENTS/app-sdk.md' ) ,
@@ -78,11 +73,7 @@ function createAppContext({
7873}
7974
8075describe ( 'experimental-app-sdk addition' , ( ) => {
81- // Silence terminal output, and let us assert on what the user is told.
82- beforeEach ( ( ) => {
83- vi . spyOn ( output , 'log' ) . mockImplementation ( ( ) => { } ) ;
84- vi . spyOn ( output , 'warning' ) . mockImplementation ( ( ) => { } ) ;
85- } ) ;
76+ // no output spies needed - the codemod prints nothing, it records on the context
8677
8778 afterEach ( ( ) => {
8879 vi . restoreAllMocks ( ) ;
@@ -304,35 +295,44 @@ describe('experimental-app-sdk addition', () => {
304295 } ) ;
305296 } ) ;
306297
298+ // the codemod records what to say; the command renders it. see Context.addNextStep / Context.skip
307299 describe ( 'user messaging' , ( ) => {
308300 it ( 'explains why it skipped an unsupported plugin type' , ( ) => {
309301 const context = createAppContext ( { pluginType : 'panel' } ) ;
310302
311303 appSdk ( context ) ;
312304
313- expect ( output . warning ) . toHaveBeenCalledWith (
314- expect . objectContaining ( { title : expect . stringContaining ( 'needs an app plugin' ) } )
315- ) ;
305+ expect ( context . getSkip ( ) ?. reason ) . toContain ( 'needs an app plugin' ) ;
316306 } ) ;
317307
318- it ( 'prints next steps after scaffolding' , ( ) => {
308+ it ( 'explains a missing plugin.json rather than failing silently' , ( ) => {
309+ const context = new Context ( ) ;
310+
311+ appSdk ( context ) ;
312+
313+ expect ( context . getSkip ( ) ?. reason ) . toContain ( 'src/plugin.json' ) ;
314+ expect ( context . getSkip ( ) ?. hints ) . toContain ( 'Run this from the root of your plugin.' ) ;
315+ } ) ;
316+
317+ it ( 'records next steps after scaffolding' , ( ) => {
319318 const context = createAppContext ( ) ;
320319
321320 appSdk ( context ) ;
322321
323- expect ( output . log ) . toHaveBeenCalledWith ( expect . objectContaining ( { title : expect . stringContaining ( 'Next steps' ) } ) ) ;
322+ expect ( context . getSkip ( ) ) . toBeUndefined ( ) ;
323+ expect ( context . listNextSteps ( ) . join ( '\n' ) ) . toContain ( 'generate:kinds' ) ;
324324 } ) ;
325325
326326 it ( 'stays quiet on a re-run' , ( ) => {
327327 const context = createAppContext ( ) ;
328328 appSdk ( context ) ;
329- vi . mocked ( output . log ) . mockClear ( ) ;
329+ const afterFirstRun = context . listNextSteps ( ) . length ;
330330
331331 appSdk ( context ) ;
332332
333- expect ( output . log ) . not . toHaveBeenCalled ( ) ;
333+ // nothing was scaffolded the second time, so nothing new to say
334+ expect ( context . listNextSteps ( ) ) . toHaveLength ( afterFirstRun ) ;
334335 } ) ;
335-
336336 } ) ;
337337
338338 it ( 'is idempotent' , async ( ) => {
0 commit comments