@@ -12,13 +12,19 @@ import {
1212
1313describe ( 'writer schema prototype page' , ( ) => {
1414 let dom ;
15+ let navigatorObj ;
1516
1617 beforeEach ( ( ) => {
1718 dom = new JSDOM (
1819 `<!doctype html><html><body>
1920 <div class="header" data-role="theme-toggle-host"><div class="pageheading">AnyWayData</div></div>
2021 <main id="writer-schema-page-root">
2122 <p id="writer-schema-support-status">Checking Writer API availability...</p>
23+ <button
24+ id="writer-schema-copy-flag"
25+ type="button"
26+ data-copy-text="chrome://flags/#writer-api-for-gemini-nano"
27+ ></button>
2228 <textarea id="writer-schema-prompt"></textarea>
2329 <button id="writer-schema-example-prompt" type="button">Load example prompt</button>
2430 <button id="writer-schema-generate" type="button">Generate schema from prompt</button>
@@ -28,11 +34,20 @@ describe('writer schema prototype page', () => {
2834 <pre id="writer-schema-raw-output">No raw Writer response yet.</pre>
2935 <pre id="writer-schema-error-output">No errors yet.</pre>
3036 <ol id="writer-schema-progress-output"><li>No generation activity yet.</li></ol>
37+ <button id="writer-schema-copy-request" type="button"></button>
38+ <button id="writer-schema-copy-prompt" type="button"></button>
39+ <textarea id="writer-schema-process-response"></textarea>
40+ <button id="writer-schema-create-schema" type="button">Create Schema</button>
3141 <div id="writer-schema-editor-root"></div>
3242 </main>
3343 </body></html>` ,
3444 { url : 'https://example.test/writer-schema.html' }
3545 ) ;
46+ navigatorObj = {
47+ clipboard : {
48+ writeText : jest . fn ( async ( ) => { } ) ,
49+ } ,
50+ } ;
3651 } ) ;
3752
3853 afterEach ( ( ) => {
@@ -170,6 +185,57 @@ describe('writer schema prototype page', () => {
170185 expect ( writer . destroy ) . toHaveBeenCalledTimes ( 1 ) ;
171186 } ) ;
172187
188+ test ( 'runWriterSchemaGeneration accumulates streaming Writer chunks into structured output' , async ( ) => {
189+ const writer = {
190+ destroy : jest . fn ( ) ,
191+ writeStreaming : jest . fn ( async function * ( ) {
192+ yield '{"schemaFields":[' ;
193+ yield '{"name":"Book Title","sourceType":"domain","command":"commerce.productName"},' ;
194+ yield '{"name":"Genre","sourceType":"enum","values":["Fiction","Non-fiction"]}' ;
195+ yield ']}' ;
196+ } ) ,
197+ } ;
198+ const WriterCtor = {
199+ create : jest . fn ( async ( ) => writer ) ,
200+ } ;
201+
202+ const result = await runWriterSchemaGeneration ( {
203+ WriterCtor,
204+ promptText : DEFAULT_PROMPT ,
205+ domainCommands : [ 'commerce.productName' , 'person.fullName' ] ,
206+ sampleSchemaText : 'Name\nperson.fullName' ,
207+ onStatus : jest . fn ( ) ,
208+ } ) ;
209+
210+ expect ( WriterCtor . create ) . toHaveBeenCalledTimes ( 1 ) ;
211+ expect ( writer . writeStreaming ) . toHaveBeenCalledWith (
212+ DEFAULT_PROMPT ,
213+ expect . objectContaining ( {
214+ context : expect . any ( String ) ,
215+ expectedInputLanguages : [ 'en' ] ,
216+ expectedContextLanguages : [ 'en' ] ,
217+ outputLanguage : 'en' ,
218+ } )
219+ ) ;
220+ expect ( result . parsedPayload . schemaFields ) . toHaveLength ( 2 ) ;
221+ expect ( result . requestDetails ) . toMatchObject ( {
222+ promptText : DEFAULT_PROMPT ,
223+ taskContext : expect . any ( String ) ,
224+ writeOptions : expect . objectContaining ( {
225+ outputLanguage : 'en' ,
226+ } ) ,
227+ createOptions : expect . objectContaining ( {
228+ sharedContext : expect . any ( String ) ,
229+ } ) ,
230+ } ) ;
231+ expect ( result . schemaRows ) . toMatchObject ( [
232+ { name : 'Book Title' , sourceType : 'domain' , command : 'commerce.productName' } ,
233+ { name : 'Genre' , sourceType : 'enum' , value : '"Fiction","Non-fiction"' } ,
234+ ] ) ;
235+ expect ( result . normalizationErrors ) . toEqual ( [ ] ) ;
236+ expect ( writer . destroy ) . toHaveBeenCalledTimes ( 1 ) ;
237+ } ) ;
238+
173239 test ( 'bootstrap warns when Writer API support is unavailable' , async ( ) => {
174240 const schemaComponent = {
175241 destroy : jest . fn ( ) ,
@@ -184,6 +250,7 @@ describe('writer schema prototype page', () => {
184250 await bootstrapWriterSchemaPage ( {
185251 documentObj : dom . window . document ,
186252 WriterCtor : null ,
253+ navigatorObj,
187254 createThemeToggleComponentFn : ( ) => ( { destroy : jest . fn ( ) } ) ,
188255 createSharedSchemaDefinitionComponentFn : ( ) => schemaComponent ,
189256 } ) ;
@@ -194,6 +261,34 @@ describe('writer schema prototype page', () => {
194261 expect ( dom . window . document . getElementById ( 'writer-schema-prompt' ) . value ) . toBe ( DEFAULT_PROMPT ) ;
195262 } ) ;
196263
264+ test ( 'bootstrap copies setup flag URLs to the clipboard' , async ( ) => {
265+ const schemaComponent = {
266+ destroy : jest . fn ( ) ,
267+ replaceRows : jest . fn ( ) ,
268+ setTextMode : jest . fn ( ) ,
269+ render : jest . fn ( ) ,
270+ syncTextFromRows : jest . fn ( ) ,
271+ validateRows : jest . fn ( ( ) => ( { errors : [ ] } ) ) ,
272+ getSchemaText : jest . fn ( ( ) => '' ) ,
273+ } ;
274+
275+ await bootstrapWriterSchemaPage ( {
276+ documentObj : dom . window . document ,
277+ WriterCtor : null ,
278+ navigatorObj,
279+ createThemeToggleComponentFn : ( ) => ( { destroy : jest . fn ( ) } ) ,
280+ createSharedSchemaDefinitionComponentFn : ( ) => schemaComponent ,
281+ } ) ;
282+
283+ dom . window . document . getElementById ( 'writer-schema-copy-flag' ) . click ( ) ;
284+ await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) ) ;
285+
286+ expect ( navigatorObj . clipboard . writeText ) . toHaveBeenCalledWith ( 'chrome://flags/#writer-api-for-gemini-nano' ) ;
287+ expect ( dom . window . document . getElementById ( 'writer-schema-generation-status' ) . textContent ) . toContain (
288+ 'Copied the Chrome flags URL to the clipboard.'
289+ ) ;
290+ } ) ;
291+
197292 test ( 'bootstrap generates rows and populates the shared schema component' , async ( ) => {
198293 const schemaComponent = {
199294 destroy : jest . fn ( ) ,
@@ -219,6 +314,7 @@ describe('writer schema prototype page', () => {
219314 const page = await bootstrapWriterSchemaPage ( {
220315 documentObj : dom . window . document ,
221316 WriterCtor,
317+ navigatorObj,
222318 createThemeToggleComponentFn : ( ) => ( { destroy : jest . fn ( ) } ) ,
223319 createSharedSchemaDefinitionComponentFn : ( ) => schemaComponent ,
224320 } ) ;
@@ -233,7 +329,7 @@ describe('writer schema prototype page', () => {
233329 ) ;
234330 expect ( schemaComponent . syncTextFromRows ) . toHaveBeenCalledTimes ( 1 ) ;
235331 expect ( dom . window . document . getElementById ( 'writer-schema-generation-status' ) . textContent ) . toContain (
236- 'Generated 1 schema fields'
332+ 'Processed Writer API output into 1 schema fields. '
237333 ) ;
238334 expect ( dom . window . document . getElementById ( 'writer-schema-json-output' ) . textContent ) . toContain ( 'Book Title' ) ;
239335 expect ( dom . window . document . getElementById ( 'writer-schema-request-output' ) . textContent ) . toContain (
@@ -244,7 +340,19 @@ describe('writer schema prototype page', () => {
244340 ) ;
245341 expect ( dom . window . document . getElementById ( 'writer-schema-error-output' ) . textContent ) . toBe ( 'No errors yet.' ) ;
246342 expect ( dom . window . document . getElementById ( 'writer-schema-progress-output' ) . textContent ) . toContain (
247- 'Schema generation completed successfully.'
343+ 'Processed Writer API output successfully.'
344+ ) ;
345+
346+ await page . copyLatestRequestJson ( ) ;
347+ await page . copyLatestRequestAsPrompt ( ) ;
348+
349+ expect ( navigatorObj . clipboard . writeText ) . toHaveBeenNthCalledWith (
350+ 1 ,
351+ expect . stringContaining ( '"promptText": "Create 10 fields that represent the inventory of a bookshop"' )
352+ ) ;
353+ expect ( navigatorObj . clipboard . writeText ) . toHaveBeenNthCalledWith (
354+ 2 ,
355+ expect . stringContaining ( 'Generate an AnyWayData schema response using the following request details.' )
248356 ) ;
249357 } ) ;
250358
@@ -272,6 +380,7 @@ describe('writer schema prototype page', () => {
272380 const page = await bootstrapWriterSchemaPage ( {
273381 documentObj : dom . window . document ,
274382 WriterCtor,
383+ navigatorObj,
275384 createThemeToggleComponentFn : ( ) => ( { destroy : jest . fn ( ) } ) ,
276385 createSharedSchemaDefinitionComponentFn : ( ) => schemaComponent ,
277386 } ) ;
@@ -325,6 +434,7 @@ describe('writer schema prototype page', () => {
325434 const page = await bootstrapWriterSchemaPage ( {
326435 documentObj : dom . window . document ,
327436 WriterCtor,
437+ navigatorObj,
328438 createThemeToggleComponentFn : ( ) => ( { destroy : jest . fn ( ) } ) ,
329439 createSharedSchemaDefinitionComponentFn : ( ) => schemaComponent ,
330440 } ) ;
@@ -341,7 +451,43 @@ describe('writer schema prototype page', () => {
341451 'unsupported command "commerce.publisher"'
342452 ) ;
343453 expect ( dom . window . document . getElementById ( 'writer-schema-progress-output' ) . textContent ) . toContain (
344- 'Completed with partial recovery.'
454+ 'Processed Writer API output with partial recovery.'
455+ ) ;
456+ } ) ;
457+
458+ test ( 'bootstrap processes a pasted AI response into schema rows' , async ( ) => {
459+ const schemaComponent = {
460+ destroy : jest . fn ( ) ,
461+ replaceRows : jest . fn ( ) ,
462+ setTextMode : jest . fn ( ) ,
463+ render : jest . fn ( ) ,
464+ syncTextFromRows : jest . fn ( ) ,
465+ validateRows : jest . fn ( ( ) => ( { errors : [ ] } ) ) ,
466+ getSchemaText : jest . fn ( ( ) => 'Book Title\ncommerce.productName()' ) ,
467+ } ;
468+
469+ const page = await bootstrapWriterSchemaPage ( {
470+ documentObj : dom . window . document ,
471+ WriterCtor : null ,
472+ navigatorObj,
473+ createThemeToggleComponentFn : ( ) => ( { destroy : jest . fn ( ) } ) ,
474+ createSharedSchemaDefinitionComponentFn : ( ) => schemaComponent ,
475+ } ) ;
476+
477+ dom . window . document . getElementById ( 'writer-schema-process-response' ) . value = JSON . stringify ( {
478+ schemaFields : [ { name : 'Book Title' , sourceType : 'domain' , command : 'commerce.productName' } ] ,
479+ } ) ;
480+
481+ await page . createSchemaFromResponse ( ) ;
482+
483+ expect ( schemaComponent . replaceRows ) . toHaveBeenCalledWith (
484+ expect . arrayContaining ( [ expect . objectContaining ( { name : 'Book Title' , command : 'commerce.productName' } ) ] )
485+ ) ;
486+ expect ( dom . window . document . getElementById ( 'writer-schema-generation-status' ) . textContent ) . toContain (
487+ 'Processed pasted AI response into 1 schema fields.'
488+ ) ;
489+ expect ( dom . window . document . getElementById ( 'writer-schema-raw-output' ) . textContent ) . toContain (
490+ 'commerce.productName'
345491 ) ;
346492 } ) ;
347493} ) ;
0 commit comments