|
| 1 | +import {describe, it, expect} from 'vitest'; |
| 2 | +import {Command} from 'commander'; |
| 3 | +import {format_examples, add_examples} from '../../utils/help'; |
| 4 | +import type {Example} from '../../utils/help'; |
| 5 | +import {scraper_command} from '../../commands/scraper'; |
| 6 | +import {discover_command} from '../../commands/discover'; |
| 7 | +import {search_command} from '../../commands/search'; |
| 8 | +import {pipelines_command} from '../../commands/dataset'; |
| 9 | +import {scrape_command} from '../../commands/scrape'; |
| 10 | + |
| 11 | +describe('utils/help.format_examples', ()=>{ |
| 12 | + it('returns empty string for no examples', ()=>{ |
| 13 | + expect(format_examples([])).toBe(''); |
| 14 | + }); |
| 15 | + |
| 16 | + it('renders a single example with comment + dollar-prefixed command', ()=>{ |
| 17 | + const exs: Example[] = [{description: 'Do a thing', command: 'cmd x'}]; |
| 18 | + const out = format_examples(exs); |
| 19 | + expect(out).toContain('\nExamples:\n'); |
| 20 | + expect(out).toContain(' # Do a thing'); |
| 21 | + expect(out).toContain(' $ cmd x'); |
| 22 | + }); |
| 23 | + |
| 24 | + it('separates multiple examples with a blank line', ()=>{ |
| 25 | + const exs: Example[] = [ |
| 26 | + {description: 'A', command: 'cmd a'}, |
| 27 | + {description: 'B', command: 'cmd b'}, |
| 28 | + ]; |
| 29 | + const out = format_examples(exs); |
| 30 | + expect(out).toMatch(/cmd a\n\n # B/); |
| 31 | + }); |
| 32 | +}); |
| 33 | + |
| 34 | +describe('utils/help.add_examples attaches to a Commander command', ()=>{ |
| 35 | + it('appears in the rendered --help output', ()=>{ |
| 36 | + const cmd = new Command('demo') |
| 37 | + .description('A demo command') |
| 38 | + .argument('<thing>', 'The thing'); |
| 39 | + add_examples(cmd, [ |
| 40 | + {description: 'Demo this', command: 'demo widget'}, |
| 41 | + ]); |
| 42 | + let captured = ''; |
| 43 | + cmd.configureOutput({writeOut: (s)=>{ captured += s; }}); |
| 44 | + cmd.outputHelp(); |
| 45 | + expect(captured).toContain('Examples:'); |
| 46 | + expect(captured).toContain('# Demo this'); |
| 47 | + expect(captured).toContain('$ demo widget'); |
| 48 | + }); |
| 49 | +}); |
| 50 | + |
| 51 | +const render_help = (cmd: Command): string=>{ |
| 52 | + let captured = ''; |
| 53 | + cmd.configureOutput({ |
| 54 | + writeOut: (s)=>{ captured += s; }, |
| 55 | + writeErr: (s)=>{ captured += s; }, |
| 56 | + }); |
| 57 | + cmd.outputHelp(); |
| 58 | + return captured; |
| 59 | +}; |
| 60 | + |
| 61 | +describe('every customer-facing command has Examples in --help', ()=>{ |
| 62 | + const scraper_create = |
| 63 | + scraper_command.commands.find(c=>c.name() == 'create')!; |
| 64 | + const scraper_run = |
| 65 | + scraper_command.commands.find(c=>c.name() == 'run')!; |
| 66 | + |
| 67 | + const cases: [string, Command][] = [ |
| 68 | + ['scraper create', scraper_create], |
| 69 | + ['scraper run', scraper_run], |
| 70 | + ['discover', discover_command], |
| 71 | + ['search', search_command], |
| 72 | + ['pipelines', pipelines_command], |
| 73 | + ['scrape', scrape_command], |
| 74 | + ]; |
| 75 | + |
| 76 | + for (const [name, cmd] of cases) |
| 77 | + { |
| 78 | + it(`${name} --help includes an Examples section`, ()=>{ |
| 79 | + const help = render_help(cmd); |
| 80 | + const examples_idx = help.indexOf('\nExamples:\n'); |
| 81 | + expect(examples_idx, `${name} is missing the Examples section`) |
| 82 | + .toBeGreaterThan(-1); |
| 83 | + const examples_block = help.slice(examples_idx); |
| 84 | + expect(examples_block, |
| 85 | + `${name} Examples section has no $ brightdata command`) |
| 86 | + .toMatch(/\$\s+brightdata\s/); |
| 87 | + expect(examples_block, |
| 88 | + `${name} examples leak the fake example.com domain`) |
| 89 | + .not.toMatch(/https?:\/\/(www\.)?example\.com/); |
| 90 | + }); |
| 91 | + } |
| 92 | +}); |
0 commit comments