angular/adev/shared-docs/pipeline/api-gen/rendering/test/cli.spec.mts
Kam 745ee71c25 fix(docs-infra): make absolute angular.dev hrefs relative in CLI option descriptions
CLI option descriptions are sourced from `@angular/cli` schema JSON
files, several of which contain absolute `https://angular.dev/...` URLs
in their `description` text. Those URLs render with the external-link
icon and push preview users out to production when viewed on
`next.angular.dev` or other dev previews. The path bypasses the existing
`link.mts` ban on absolute angular.dev links because option descriptions
go through `marked.parse` directly, without `AdevDocsRenderer`. Rewrite
the rendered hrefs whose values begin with `https://angular.dev/` (or
the `http:` variant) to root-relative paths so the resulting anchors
route through Angular's Router and resolve against the active
deployment. Subdomains such as `next.angular.dev/...` are intentionally
not rewritten because they refer to genuinely different deployments.

Closes #68795
2026-05-20 10:28:27 -07:00

59 lines
2.3 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {resolve} from 'path';
import {readFile} from 'fs/promises';
import {JSDOM} from 'jsdom';
import {getRenderable} from '../processing.mjs';
import {renderEntry} from '../rendering.mjs';
import {initHighlighter} from '../../../shared/shiki.mjs';
import {setHighlighterInstance} from '../shiki/shiki.mjs';
describe('CLI docs to html', () => {
let fragment: DocumentFragment;
let entryJson: any;
beforeAll(async () => {
setHighlighterInstance(await initHighlighter());
const entryContent = await readFile(resolve('./fake-cli-entries.json'), {
encoding: 'utf-8',
});
entryJson = JSON.parse(entryContent) as any;
const renderableJson = await getRenderable(entryJson, '', 'angular/cli');
fragment = JSDOM.fragment(await renderEntry(renderableJson));
});
it('should subcommands correctly', async () => {
const generateComponentSubcommand = entryJson.subcommands.find(
(subcommand: any) => subcommand.name === 'component',
);
const renderableJson = await getRenderable(generateComponentSubcommand, '', 'angular/cli');
fragment = JSDOM.fragment(await renderEntry(renderableJson));
const cliTocs = fragment.querySelectorAll('.docs-reference-cli-toc')!;
expect(cliTocs.length).toBe(2);
expect(cliTocs[0].textContent).toContain('ng component [name] [options]');
expect(cliTocs[1].textContent).toContain('ng c [name] [options]');
});
it('should rewrite absolute angular.dev hrefs in option descriptions to root-relative', async () => {
const renderableJson = await getRenderable(entryJson, '', 'angular/cli');
const localFragment = JSDOM.fragment(renderEntry(renderableJson));
const hrefs = Array.from(localFragment.querySelectorAll('a')).map((a) =>
a.getAttribute('href'),
);
// Absolute angular.dev URLs are rewritten to root-relative.
expect(hrefs).toContain('/cli-test-rewrite');
expect(hrefs).not.toContain('https://angular.dev/cli-test-rewrite');
// Subdomains are intentionally left as external.
expect(hrefs).toContain('https://next.angular.dev/cli-test-preview');
});
});