Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion website/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,20 @@ Sidebar order is defined in `astro.config.mjs`, not by directory order.
| :--------------------- | :--------------------------------------------------------- |
| `pnpm install` | Install dependencies |
| `pnpm dev` | Start local dev server at `localhost:4321` |
| `pnpm build` | Build the production site to `./dist/` |
| `pnpm build` | Build the production site to `./dist/` and test its output |
| `pnpm preview` | Preview the production build locally |
| `pnpm test` | Test site plugins with Node's built-in test runner |
| `pnpm test:build` | Test rendered Markdown/MDX tables in `dist/` after a build |
| `pnpm refresh:openapi` | Generate the OpenAPI snapshot from `scope-core` |

## Authoring docs

- Use `.md` for plain Markdown, `.mdx` whenever the page contains JSX
(e.g. Starlight `<Tabs>`).
- Use standard pipe-delimited Markdown tables in both `.md` and `.mdx`.
Keep `markdown.gfm: true` explicit in [astro.config.mjs](astro.config.mjs):
the installed MDX integration does not inherit Astro's Markdown processor
defaults, so otherwise MDX tables render as plain text.
- Write internal Markdown links and literal MDX `href`/`src` attributes
relative to the site root, such as `/getting-started/access/`.
[src/plugins/remark-base-path.mjs](src/plugins/remark-base-path.mjs)
Expand Down Expand Up @@ -109,6 +114,8 @@ SITE=https://microsoft.github.io BASE_PATH=/scope pnpm preview

Open `/scope/` on the preview server. Keep `BASE_PATH` the same for the
build and preview so assets, navigation, and search use the same URLs.
`pnpm build` runs `pnpm test:build` after generating the site, so local and
CI builds catch table-rendering regressions in both Markdown and MDX pages.

## Learn more

Expand Down
2 changes: 2 additions & 0 deletions website/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export default defineConfig({
base,
server: { port: docPort },
markdown: {
// The installed MDX integration does not inherit the processor's GFM default.
gfm: true,
remarkPlugins: [[remarkBasePath, { base }], remarkHttpSnippets],
},
integrations: [
Expand Down
3 changes: 2 additions & 1 deletion website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
"scripts": {
"dev": "worktree-env && astro dev",
"start": "worktree-env && astro dev",
"build": "astro build",
"build": "astro build && pnpm test:build",
"preview": "worktree-env && astro preview",
"test": "node --test src/plugins/*.test.mjs",
"test:build": "node --test tests/*.test.mjs",
"astro": "astro",
"refresh:openapi": "cd .. && pnpm --filter api generate:openapi"
},
Expand Down
59 changes: 59 additions & 0 deletions website/tests/tables.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { test } from 'node:test';

async function readTables(page) {
const html = await readFile(new URL(`../dist/${page}/index.html`, import.meta.url), 'utf8');
return html.match(/<table\b[^>]*>[\s\S]*?<\/table>/g) ?? [];
}

function assertTable(table, headers, fields) {
assert.deepEqual(
Array.from(table.matchAll(/<th\b[^>]*>([\s\S]*?)<\/th>/g), ([, text]) => text),
headers,
);
assert.deepEqual(
Array.from(
table.matchAll(/<tr\b[^>]*>\s*<td\b[^>]*><code\b[^>]*>([^<]+)<\/code>/g),
([, field]) => field,
),
fields,
);
assert.equal(Array.from(table.matchAll(/<td\b/g)).length, headers.length * fields.length);
}

test('renders the MDX profile anatomy as a table with all fields and inline formatting', async () => {
const tables = await readTables('guides/defining-profiles');
assert.equal(tables.length, 1, 'The profile anatomy must render as an HTML table, not pipe-delimited text');
assertTable(tables[0], ['Field', 'Layer', 'Description'], [
'name',
'description',
'workerType',
'model',
'agentVersion',
'mcpServers',
'skillRevisions',
'extensions',
]);
assert.match(tables[0], /<a href="[^"]*\/guides\/choosing-a-coding-agent\/">Choosing a coding agent<\/a>/);
assert.match(tables[0], /<strong>VS Code Copilot only\.<\/strong>/);
});

test('preserves tables in the plain Markdown profile schema reference', async () => {
const tables = await readTables('reference/profile-schema');
assert.equal(tables.length, 2);
assertTable(tables[0], ['Field', 'Type', 'Required', 'Description'], ['id', 'name', 'description']);
assertTable(tables[1], ['Field', 'Type', 'Required', 'Description'], [
'id',
'profileId',
'workerType',
'model',
'agentVersion',
'mcpServers',
'skillRevisions',
'createdAt',
]);
});
Loading