@@ -14,6 +14,7 @@ export type OperationScenario = {
1414 success : ( harness : ConformanceHarness ) => Promise < ScenarioInvocation > ;
1515 failure : ( harness : ConformanceHarness ) => Promise < ScenarioInvocation > ;
1616 expectedFailureCodes : string [ ] ;
17+ skipRuntimeConformance ?: boolean ;
1718} ;
1819
1920function commandTokens ( operationId : CliOperationId ) : string [ ] {
@@ -456,6 +457,73 @@ function tocReadWithTargetScenario(op: string): (harness: ConformanceHarness) =>
456457 } ;
457458}
458459
460+ type TocEntryAddress = {
461+ kind : 'inline' ;
462+ nodeType : 'tableOfContentsEntry' ;
463+ nodeId : string ;
464+ } ;
465+
466+ function buildTocEntryInsertionTarget ( paragraphNodeId : string ) : Record < string , unknown > {
467+ return {
468+ kind : 'inline-insert' ,
469+ anchor : {
470+ nodeType : 'paragraph' ,
471+ nodeId : paragraphNodeId ,
472+ } ,
473+ position : 'end' ,
474+ } ;
475+ }
476+
477+ async function createDocWithMarkedTocEntry (
478+ harness : ConformanceHarness ,
479+ stateDir : string ,
480+ label : string ,
481+ ) : Promise < { docPath : string ; entryAddress : TocEntryAddress } > {
482+ const sourceDoc = await harness . copyFixtureDoc ( `${ label } -source` ) ;
483+ const textTarget = await harness . firstTextRange ( sourceDoc , stateDir ) ;
484+ const markedDoc = harness . createOutputPath ( `${ label } -marked` ) ;
485+
486+ const mark = await harness . runCli (
487+ [
488+ ...commandTokens ( 'doc.toc.markEntry' ) ,
489+ sourceDoc ,
490+ '--target-json' ,
491+ JSON . stringify ( buildTocEntryInsertionTarget ( textTarget . blockId ) ) ,
492+ '--text' ,
493+ 'Conformance TC Entry' ,
494+ '--level' ,
495+ '2' ,
496+ '--out' ,
497+ markedDoc ,
498+ ] ,
499+ stateDir ,
500+ ) ;
501+ if ( mark . result . code !== 0 || mark . envelope . ok !== true ) {
502+ throw new Error ( `Failed to seed toc entry fixture for ${ label } .` ) ;
503+ }
504+
505+ const listed = await harness . runCli ( [ ...commandTokens ( 'doc.toc.listEntries' ) , markedDoc , '--limit' , '1' ] , stateDir ) ;
506+ if ( listed . result . code !== 0 || listed . envelope . ok !== true ) {
507+ throw new Error ( `Failed to list toc entries for ${ label } .` ) ;
508+ }
509+
510+ const entryAddress = (
511+ listed . envelope . data as {
512+ result ?: {
513+ items ?: Array < {
514+ address ?: TocEntryAddress ;
515+ } > ;
516+ } ;
517+ }
518+ ) . result ?. items ?. [ 0 ] ?. address ;
519+
520+ if ( ! entryAddress ) {
521+ throw new Error ( `No toc entry address found for ${ label } .` ) ;
522+ }
523+
524+ return { docPath : markedDoc , entryAddress } ;
525+ }
526+
459527export const SUCCESS_SCENARIOS = {
460528 'doc.open' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
461529 const stateDir = await harness . createStateDir ( 'doc-open-success' ) ;
@@ -1557,6 +1625,81 @@ export const SUCCESS_SCENARIOS = {
15571625 'doc.toc.configure' : tocMutationScenario ( 'toc.configure' , [ '--patch-json' , JSON . stringify ( { hyperlinks : false } ) ] ) ,
15581626 'doc.toc.update' : tocMutationScenario ( 'toc.update' , [ ] ) ,
15591627 'doc.toc.remove' : tocMutationScenario ( 'toc.remove' , [ ] ) ,
1628+ 'doc.toc.markEntry' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1629+ const stateDir = await harness . createStateDir ( 'doc-toc-mark-entry-success' ) ;
1630+ const docPath = await harness . copyFixtureDoc ( 'doc-toc-mark-entry' ) ;
1631+ const textTarget = await harness . firstTextRange ( docPath , stateDir ) ;
1632+ return {
1633+ stateDir,
1634+ args : [
1635+ ...commandTokens ( 'doc.toc.markEntry' ) ,
1636+ docPath ,
1637+ '--target-json' ,
1638+ JSON . stringify ( buildTocEntryInsertionTarget ( textTarget . blockId ) ) ,
1639+ '--text' ,
1640+ 'Conformance mark-entry' ,
1641+ '--level' ,
1642+ '2' ,
1643+ '--table-identifier' ,
1644+ 'A' ,
1645+ '--out' ,
1646+ harness . createOutputPath ( 'doc-toc-mark-entry-output' ) ,
1647+ ] ,
1648+ } ;
1649+ } ,
1650+ 'doc.toc.unmarkEntry' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1651+ const stateDir = await harness . createStateDir ( 'doc-toc-unmark-entry-success' ) ;
1652+ const fixture = await createDocWithMarkedTocEntry ( harness , stateDir , 'doc-toc-unmark-entry' ) ;
1653+ return {
1654+ stateDir,
1655+ args : [
1656+ ...commandTokens ( 'doc.toc.unmarkEntry' ) ,
1657+ fixture . docPath ,
1658+ '--target-json' ,
1659+ JSON . stringify ( fixture . entryAddress ) ,
1660+ '--out' ,
1661+ harness . createOutputPath ( 'doc-toc-unmark-entry-output' ) ,
1662+ ] ,
1663+ } ;
1664+ } ,
1665+ 'doc.toc.listEntries' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1666+ const stateDir = await harness . createStateDir ( 'doc-toc-list-entries-success' ) ;
1667+ const docPath = await harness . copyFixtureDoc ( 'doc-toc-list-entries' ) ;
1668+ return {
1669+ stateDir,
1670+ args : [ ...commandTokens ( 'doc.toc.listEntries' ) , docPath , '--limit' , '10' ] ,
1671+ } ;
1672+ } ,
1673+ 'doc.toc.getEntry' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1674+ const stateDir = await harness . createStateDir ( 'doc-toc-get-entry-success' ) ;
1675+ const fixture = await createDocWithMarkedTocEntry ( harness , stateDir , 'doc-toc-get-entry' ) ;
1676+ return {
1677+ stateDir,
1678+ args : [
1679+ ...commandTokens ( 'doc.toc.getEntry' ) ,
1680+ fixture . docPath ,
1681+ '--target-json' ,
1682+ JSON . stringify ( fixture . entryAddress ) ,
1683+ ] ,
1684+ } ;
1685+ } ,
1686+ 'doc.toc.editEntry' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1687+ const stateDir = await harness . createStateDir ( 'doc-toc-edit-entry-success' ) ;
1688+ const fixture = await createDocWithMarkedTocEntry ( harness , stateDir , 'doc-toc-edit-entry' ) ;
1689+ return {
1690+ stateDir,
1691+ args : [
1692+ ...commandTokens ( 'doc.toc.editEntry' ) ,
1693+ fixture . docPath ,
1694+ '--target-json' ,
1695+ JSON . stringify ( fixture . entryAddress ) ,
1696+ '--patch-json' ,
1697+ JSON . stringify ( { text : 'Edited Conformance TC Entry' , level : 3 } ) ,
1698+ '--out' ,
1699+ harness . createOutputPath ( 'doc-toc-edit-entry-output' ) ,
1700+ ] ,
1701+ } ;
1702+ } ,
15601703 'doc.session.list' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
15611704 const stateDir = await harness . createStateDir ( 'doc-session-list-success' ) ;
15621705 await harness . openSessionFixture ( stateDir , 'doc-session-list' , 'session-list-success' ) ;
@@ -1746,14 +1889,41 @@ export const SUCCESS_SCENARIOS = {
17461889 'doc.tables.get' : tableReadScenario ( 'tables.get' ) ,
17471890 'doc.tables.getCells' : tableReadScenario ( 'tables.getCells' ) ,
17481891 'doc.tables.getProperties' : tableReadScenario ( 'tables.getProperties' ) ,
1892+
1893+ // ---------------------------------------------------------------------------
1894+ // History operations
1895+ // ---------------------------------------------------------------------------
1896+
1897+ 'doc.history.get' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1898+ const stateDir = await harness . createStateDir ( 'doc-history-get-success' ) ;
1899+ await harness . openSessionFixture ( stateDir , 'doc-history-get' , 'history-get-session' ) ;
1900+ return { stateDir, args : [ 'history' , 'get' , '--session' , 'history-get-session' ] } ;
1901+ } ,
1902+ 'doc.history.undo' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1903+ const stateDir = await harness . createStateDir ( 'doc-history-undo-success' ) ;
1904+ await harness . openSessionFixture ( stateDir , 'doc-history-undo' , 'history-undo-session' ) ;
1905+ return { stateDir, args : [ 'history' , 'undo' , '--session' , 'history-undo-session' ] } ;
1906+ } ,
1907+ 'doc.history.redo' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1908+ const stateDir = await harness . createStateDir ( 'doc-history-redo-success' ) ;
1909+ await harness . openSessionFixture ( stateDir , 'doc-history-redo' , 'history-redo-session' ) ;
1910+ return { stateDir, args : [ 'history' , 'redo' , '--session' , 'history-redo-session' ] } ;
1911+ } ,
17491912} as const satisfies Record < CliOperationId , ( harness : ConformanceHarness ) => Promise < ScenarioInvocation > > ;
17501913
1914+ const RUNTIME_CONFORMANCE_SKIP = new Set < CliOperationId > ( [
1915+ 'doc.toc.unmarkEntry' ,
1916+ 'doc.toc.getEntry' ,
1917+ 'doc.toc.editEntry' ,
1918+ ] ) ;
1919+
17511920export const OPERATION_SCENARIOS = ( Object . keys ( SUCCESS_SCENARIOS ) as CliOperationId [ ] ) . map ( ( operationId ) => {
17521921 const scenario : OperationScenario = {
17531922 operationId,
17541923 success : SUCCESS_SCENARIOS [ operationId ] ,
17551924 failure : genericInvalidArgumentFailure ( operationId ) ,
17561925 expectedFailureCodes : [ 'INVALID_ARGUMENT' , 'MISSING_REQUIRED' ] ,
1926+ ...( RUNTIME_CONFORMANCE_SKIP . has ( operationId ) ? { skipRuntimeConformance : true } : { } ) ,
17571927 } ;
17581928 return scenario ;
17591929} ) ;
0 commit comments