Previously, the [processCliCommands][1] dgeni processor, which is used
to generate the docs pages for the CLI commands, expected the CLI
commands overview page (with a URL of `cli`) to exist as a child of a
top-level navigation section (`CLI Commands`). If one tried to move the
`CLI Commands` section inside another section, `processCliCommnads`
would fail to find it and thus fail to generate the CLI commands docs.
This problem came up in #38353.
This commit updates the `processCliCommands` processor to be able to
find it regardless of the position of the `CLI Commands` section inside
the navigation doc.
[1]:
dca4443a8e/aio/tools/transforms/cli-docs-package/processors/processCliCommands.js (L7-L9)
PR Close #38365
		
	
			
		
			
				
	
	
		
			386 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			386 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const testPackage = require('../../helpers/test-package');
 | |
| const Dgeni = require('dgeni');
 | |
| 
 | |
| describe('processCliCommands processor', () => {
 | |
| 
 | |
|   let dgeni, injector, processor, createDocMessage;
 | |
| 
 | |
|   const navigationStub = {
 | |
|     docType: 'navigation-json',
 | |
|     data: {SideNav: [{children: [{'url': 'cli'}]}]}
 | |
|   };
 | |
| 
 | |
|   beforeEach(() => {
 | |
|     dgeni = new Dgeni([testPackage('cli-docs-package')]);
 | |
|     injector = dgeni.configureInjector();
 | |
|     processor = injector.get('processCliCommands');
 | |
|     createDocMessage = injector.get('createDocMessage');
 | |
|   });
 | |
| 
 | |
|   it('should be available on the injector', () => { expect(processor.$process).toBeDefined(); });
 | |
| 
 | |
|   it('should run after the correct processor',
 | |
|      () => { expect(processor.$runAfter).toEqual(['extra-docs-added']); });
 | |
| 
 | |
|   it('should run before the correct processor',
 | |
|      () => { expect(processor.$runBefore).toEqual(['rendering-docs']); });
 | |
| 
 | |
|   it('should collect the names (name + aliases)', () => {
 | |
|     const doc = {
 | |
|       docType: 'cli-command',
 | |
|       name: 'name',
 | |
|       commandAliases: ['alias1', 'alias2'],
 | |
|       options: [],
 | |
|     };
 | |
|     processor.$process([doc, navigationStub]);
 | |
|     expect(doc.names).toEqual(['name', 'alias1', 'alias2']);
 | |
|   });
 | |
| 
 | |
|   describe('options', () => {
 | |
|     it('should remove the hidden options', () => {
 | |
|       const doc = {
 | |
|         docType: 'cli-command',
 | |
|         name: 'name',
 | |
|         commandAliases: [],
 | |
|         options: [
 | |
|           {name: 'option1'},
 | |
|           {name: 'option2', hidden: true},
 | |
|           {name: 'option3'},
 | |
|           {name: 'option4', hidden: true},
 | |
|         ],
 | |
|       };
 | |
|       processor.$process([doc, navigationStub]);
 | |
|       expect(doc.namedOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'option1'}),
 | |
|         jasmine.objectContaining({name: 'option3'}),
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should collect the non-hidden positional and named options', () => {
 | |
|       const doc = {
 | |
|         docType: 'cli-command',
 | |
|         name: 'name',
 | |
|         commandAliases: [],
 | |
|         options: [
 | |
|           {name: 'named1'},
 | |
|           {name: 'positional1', positional: 0},
 | |
|           {name: 'named2', hidden: true},
 | |
|           {name: 'positional2', hidden: true, positional: 1},
 | |
|         ],
 | |
|       };
 | |
|       processor.$process([doc, navigationStub]);
 | |
|       expect(doc.positionalOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'positional1', positional: 0}),
 | |
|       ]);
 | |
|       expect(doc.namedOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'named1'}),
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should sort the named options into order by name', () => {
 | |
|       const doc = {
 | |
|         docType: 'cli-command',
 | |
|         name: 'name',
 | |
|         commandAliases: [],
 | |
|         options: [
 | |
|           {name: 'c'},
 | |
|           {name: 'a'},
 | |
|           {name: 'b'},
 | |
|         ],
 | |
|       };
 | |
|       processor.$process([doc, navigationStub]);
 | |
|       expect(doc.namedOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'a'}),
 | |
|         jasmine.objectContaining({name: 'b'}),
 | |
|         jasmine.objectContaining({name: 'c'}),
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should collect potential search terms from options for indexing', () => {
 | |
|       const doc = {
 | |
|         docType: 'cli-command',
 | |
|         name: 'name',
 | |
|         commandAliases: [],
 | |
|         options: [
 | |
|           {name: 'named1'},
 | |
|           {name: 'positional1', positional: 0},
 | |
|           {name: 'named2', hidden: true},
 | |
|           {name: 'positional2', hidden: true, positional: 1},
 | |
|         ],
 | |
|       };
 | |
|       processor.$process([doc, navigationStub]);
 | |
|       expect(doc.optionKeywords).toEqual('named1 positional1');
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('subcommands', () => {
 | |
|     it('should convert subcommands hash into a collection', () => {
 | |
|       const doc = {
 | |
|         docType: 'cli-command',
 | |
|         name: 'name',
 | |
|         commandAliases: [],
 | |
|         options: [{
 | |
|           name: 'supercommand',
 | |
|           subcommands: {
 | |
|             subcommand1: {
 | |
|               name: 'subcommand1',
 | |
|               options: [
 | |
|                 {name: 'subcommand1-option1'},
 | |
|                 {name: 'subcommand1-option2'},
 | |
|               ],
 | |
|             },
 | |
|             subcommand2: {
 | |
|               name: 'subcommand2',
 | |
|               options: [
 | |
|                 {name: 'subcommand2-option1'},
 | |
|                 {name: 'subcommand2-option2'},
 | |
|               ],
 | |
|             }
 | |
|           },
 | |
|         }],
 | |
|       };
 | |
|       processor.$process([doc, navigationStub]);
 | |
|       expect(doc.options[0].subcommands).toEqual([
 | |
|         jasmine.objectContaining({name: 'subcommand1'}),
 | |
|         jasmine.objectContaining({name: 'subcommand2'}),
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should remove the hidden subcommand options', () => {
 | |
|       const doc = {
 | |
|         docType: 'cli-command',
 | |
|         name: 'name',
 | |
|         commandAliases: [],
 | |
|         options: [{
 | |
|           name: 'supercommand',
 | |
|           subcommands: {
 | |
|             subcommand1: {
 | |
|               name: 'subcommand1',
 | |
|               options: [
 | |
|                 {name: 'subcommand1-option1'},
 | |
|                 {name: 'subcommand1-option2', hidden: true},
 | |
|               ],
 | |
|             },
 | |
|             subcommand2: {
 | |
|               name: 'subcommand2',
 | |
|               options: [
 | |
|                 {name: 'subcommand2-option1', hidden: true},
 | |
|                 {name: 'subcommand2-option2'},
 | |
|               ],
 | |
|             }
 | |
|           },
 | |
|         }],
 | |
|       };
 | |
|       processor.$process([doc, navigationStub]);
 | |
|       expect(doc.options[0].subcommands[0].namedOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'subcommand1-option1'}),
 | |
|       ]);
 | |
|       expect(doc.options[0].subcommands[1].namedOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'subcommand2-option2'}),
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should collect the non-hidden positional arguments and named options', () => {
 | |
|       const doc = {
 | |
|         docType: 'cli-command',
 | |
|         name: 'name',
 | |
|         commandAliases: [],
 | |
|         options: [{
 | |
|           name: 'supercommand',
 | |
|           subcommands: {
 | |
|             subcommand1: {
 | |
|               name: 'subcommand1',
 | |
|               options: [
 | |
|                 {name: 'subcommand1-option1'},
 | |
|                 {name: 'subcommand1-option2', positional: 0},
 | |
|               ],
 | |
|             },
 | |
|             subcommand2: {
 | |
|               name: 'subcommand2',
 | |
|               options: [
 | |
|                 {name: 'subcommand2-option1', hidden: true},
 | |
|                 {name: 'subcommand2-option2', hidden: true, positional: 1},
 | |
|               ],
 | |
|             }
 | |
|           },
 | |
|         }],
 | |
|       };
 | |
|       processor.$process([doc, navigationStub]);
 | |
|       expect(doc.options[0].subcommands[0].positionalOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'subcommand1-option2', positional: 0}),
 | |
|       ]);
 | |
|       expect(doc.options[0].subcommands[0].namedOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'subcommand1-option1'}),
 | |
|       ]);
 | |
| 
 | |
|       expect(doc.options[0].subcommands[1].positionalOptions).toEqual([]);
 | |
|       expect(doc.options[0].subcommands[1].namedOptions).toEqual([]);
 | |
|     });
 | |
| 
 | |
|     it('should sort the named subcommand options into order by name', () => {
 | |
|       const doc = {
 | |
|         docType: 'cli-command',
 | |
|         name: 'name',
 | |
|         commandAliases: [],
 | |
|         options: [{
 | |
|           name: 'supercommand',
 | |
|           subcommands: {
 | |
|             subcommand1: {
 | |
|               name: 'subcommand1',
 | |
|               options: [
 | |
|                 {name: 'c'},
 | |
|                 {name: 'a'},
 | |
|                 {name: 'b'},
 | |
|               ]
 | |
|             }
 | |
|           }
 | |
|         }],
 | |
|       };
 | |
|       processor.$process([doc, navigationStub]);
 | |
|       expect(doc.options[0].subcommands[0].namedOptions).toEqual([
 | |
|         jasmine.objectContaining({name: 'a'}),
 | |
|         jasmine.objectContaining({name: 'b'}),
 | |
|         jasmine.objectContaining({name: 'c'}),
 | |
|       ]);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   it('should add the command to the CLI node in the navigation doc if there is a first child node with a `cli` url',
 | |
|      () => {
 | |
|        const command = {
 | |
|          docType: 'cli-command',
 | |
|          name: 'command1',
 | |
|          commandAliases: ['alias1', 'alias2'],
 | |
|          options: [],
 | |
|          path: 'cli/command1',
 | |
|        };
 | |
|        const navigation = {
 | |
|          docType: 'navigation-json',
 | |
|          data: {
 | |
|            SideNav: [
 | |
|              {url: 'some/page', title: 'Some Page'},
 | |
|              {
 | |
|                title: 'CLI Commands',
 | |
|                tooltip: 'Angular CLI command reference',
 | |
|                children: [{'title': 'Overview', 'url': 'cli'}],
 | |
|              },
 | |
|              {url: 'other/page', title: 'Other Page'},
 | |
|            ],
 | |
|          },
 | |
|        };
 | |
|        processor.$process([command, navigation]);
 | |
|        expect(navigation.data.SideNav[1].title).toEqual('CLI Commands');
 | |
|        expect(navigation.data.SideNav[1].children).toEqual([
 | |
|          {url: 'cli', title: 'Overview'},
 | |
|          {url: 'cli/command1', title: 'ng command1'},
 | |
|        ]);
 | |
|      });
 | |
| 
 | |
|   it('should detect the CLI node if it is nested in another node (as long as there is a first child node with a `cli` url',
 | |
|      () => {
 | |
|        const command = {
 | |
|          docType: 'cli-command',
 | |
|          name: 'command1',
 | |
|          commandAliases: ['alias1', 'alias2'],
 | |
|          options: [],
 | |
|          path: 'cli/command1',
 | |
|        };
 | |
|        const navigation = {
 | |
|          docType: 'navigation-json',
 | |
|          data: {
 | |
|            SideNav: [
 | |
|              {url: 'some/page', title: 'Some Page'},
 | |
|              {
 | |
|                title: 'CLI Commands Grandparent',
 | |
|                children: [
 | |
|                  {url: 'some/nested/page', title: 'Some Nested Page'},
 | |
|                  {
 | |
|                    title: 'CLI Commands Parent',
 | |
|                    children: [
 | |
|                      {url: 'some/more/nested/page', title: 'Some More Nested Page'},
 | |
|                      {
 | |
|                        title: 'CLI Commands',
 | |
|                        tooltip: 'Angular CLI command reference',
 | |
|                        children: [{'title': 'Overview', 'url': 'cli'}],
 | |
|                      },
 | |
|                      {url: 'other/more/nested/page', title: 'Other More Nested Page'},
 | |
|                    ],
 | |
|                  },
 | |
|                  {url: 'other/nested/page', title: 'Other Nested Page'},
 | |
|                ],
 | |
|              },
 | |
|              {url: 'other/page', title: 'Other Page'},
 | |
|            ],
 | |
|          },
 | |
|        };
 | |
| 
 | |
|        processor.$process([command, navigation]);
 | |
| 
 | |
|        const cliCommandsNode = navigation.data.SideNav[1].children[1].children[1];
 | |
|        expect(cliCommandsNode.title).toEqual('CLI Commands');
 | |
|        expect(cliCommandsNode.children).toEqual([
 | |
|          {url: 'cli', title: 'Overview'},
 | |
|          {url: 'cli/command1', title: 'ng command1'},
 | |
|        ]);
 | |
|      });
 | |
| 
 | |
|   it('should complain if there is no child with `cli` url', () => {
 | |
|     const command = {
 | |
|       docType: 'cli-command',
 | |
|       name: 'command1',
 | |
|       commandAliases: ['alias1', 'alias2'],
 | |
|       options: [],
 | |
|       path: 'cli/command1',
 | |
|     };
 | |
|     const navigation = {
 | |
|       docType: 'navigation-json',
 | |
|       data: {
 | |
|         SideNav: [
 | |
|           {url: 'some/page', title: 'Some Page'}, {
 | |
|             title: 'CLI Commands',
 | |
|             tooltip: 'Angular CLI command reference',
 | |
|             children: [{'title': 'Overview', 'url': 'client'}]
 | |
|           },
 | |
|           {url: 'other/page', title: 'Other Page'}
 | |
|         ]
 | |
|       }
 | |
|     };
 | |
|     expect(() => processor.$process([command, navigation]))
 | |
|         .toThrowError(createDocMessage(
 | |
|             'Missing `cli` url - CLI Commands must include a first child node with url set at `cli`',
 | |
|             navigation));
 | |
|   });
 | |
| 
 | |
|   it('should collect potential search terms from options and its subcommands for indexing', () => {
 | |
|     const doc = {
 | |
|       docType: 'cli-command',
 | |
|       name: 'name',
 | |
|       commandAliases: [],
 | |
|       options: [{
 | |
|         name: 'supercommand',
 | |
|         subcommands: {
 | |
|           subcommand1: {
 | |
|             name: 'subcommand1',
 | |
|             options: [
 | |
|               {name: 'subcommand1-option1'},
 | |
|               {name: 'subcommand1-option2'},
 | |
|             ],
 | |
|           },
 | |
|           subcommand2: {
 | |
|             name: 'subcommand2',
 | |
|             options: [
 | |
|               {name: 'subcommand2-option1'},
 | |
|               {name: 'subcommand2-option2'},
 | |
|             ],
 | |
|           }
 | |
|         },
 | |
|       }],
 | |
|     };
 | |
|     processor.$process([doc, navigationStub]);
 | |
|     expect(doc.optionKeywords)
 | |
|         .toEqual(
 | |
|             'supercommand subcommand1 subcommand1-option1 subcommand1-option2 subcommand2 subcommand2-option1 subcommand2-option2');
 | |
|   });
 | |
| 
 | |
| });
 |