@microsoft/fast-test-harness 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -253,7 +253,7 @@ CLI flags take precedence over environment variables.
253
253
 
254
254
  | Option | Type | Description |
255
255
  |--------|------|-------------|
256
- | `tagPrefix` | `string` | Tag name prefix for custom elements (e.g., `"fluent"`, `"mai"`) |
256
+ | `tagPrefix` | `string` | Tag name prefix for custom elements (e.g., `"fluent"`, `"contoso"`) |
257
257
  | `packageName` | `string?` | Monolithic package name — scans subdirectories for component artifacts. Mutually exclusive with `components`. |
258
258
  | `components` | `ComponentRegistration[]?` | Explicit list of per-component packages. Mutually exclusive with `packageName`. |
259
259
  | `distDir` | `string?` | Artifact directory relative to the package root (default: `"dist/esm"`). Only used with `packageName`. |
@@ -266,7 +266,7 @@ CLI flags take precedence over environment variables.
266
266
  | `@microsoft/fast-test-harness` | `test`, `expect`, `CSRFixture`, `SSRFixture`, `toHaveCustomState`, `installDomShim`, `createSSRRenderer` |
267
267
  | `@microsoft/fast-test-harness/build/*.js` | `installDomShim`, `generateStylesheets`, `generateFTemplates`, `generateWebuiTemplates`, `definitionAsyncResolver`, `shadowOptionsToAttributes`, `ShadowOptionsResolver` |
268
268
  | `@microsoft/fast-test-harness/fixtures/*.js` | `CSRFixture`, `SSRFixture`, `toHaveCustomState`, extended `test` and `expect` |
269
- | `@microsoft/fast-test-harness/ssr/render.js` | `createSSRRenderer`, `renderTemplate`, `buildEntryHtml`, `buildState`, `parseDefaultValue` |
269
+ | `@microsoft/fast-test-harness/ssr/render.js` | `createSSRRenderer`, `renderTemplate`, `buildEntryHtml`, `buildState` |
270
270
  | `@microsoft/fast-test-harness/server.mjs` | `startServer` |
271
271
  | `@microsoft/fast-test-harness/playwright.config.mjs` | Shared Playwright configuration |
272
272
  | `@microsoft/fast-test-harness/vite.config.mjs` | Shared Vite configuration |
@@ -14,7 +14,7 @@
14
14
  * ```ts
15
15
  * import { generateWebuiTemplates } from "@microsoft/fast-test-harness/build/generate-webui-templates.js";
16
16
  *
17
- * await generateWebuiTemplates({ cwd: process.cwd(), tagPrefix: "mai" });
17
+ * await generateWebuiTemplates({ cwd: process.cwd(), tagPrefix: "contoso" });
18
18
  * ```
19
19
  */
20
20
  import { type ShadowOptionsResolver } from "./generate-templates.js";
@@ -21,10 +21,10 @@
21
21
  * flat exports.
22
22
  * ```ts
23
23
  * const { render } = createSSRRenderer({
24
- * tagPrefix: "mai",
24
+ * tagPrefix: "contoso",
25
25
  * components: [
26
- * { name: "button", packageName: "@mai-ui/button" },
27
- * { name: "checkbox", packageName: "@mai-ui/checkbox" },
26
+ * { name: "button", packageName: "@contoso/button" },
27
+ * { name: "checkbox", packageName: "@contoso/checkbox" },
28
28
  * ],
29
29
  * });
30
30
  * ```
@@ -37,25 +37,25 @@ export interface ComponentRegistration {
37
37
  name: string;
38
38
  /**
39
39
  * The npm package name for this component
40
- * (e.g., "@mai-ui/button").
40
+ * (e.g., "@contoso/button").
41
41
  */
42
42
  packageName: string;
43
43
  }
44
44
  export interface SSRRendererOptions {
45
45
  /**
46
- * Tag name prefix for custom elements (e.g., "fluent", "mai").
46
+ * Tag name prefix for custom elements (e.g., "fluent", "contoso").
47
47
  */
48
48
  tagPrefix: string;
49
49
  /**
50
50
  * The npm package name used to resolve component build artifacts
51
51
  * when all components live in subdirectories of a single package
52
- * (Fluent-style). Mutually exclusive with `components`.
52
+ * (monolithic layout). Mutually exclusive with `components`.
53
53
  */
54
54
  packageName?: string;
55
55
  /**
56
- * Explicit list of per-component packages (MAI-style). Each entry
57
- * maps a component name to its npm package. Mutually exclusive
58
- * with `packageName`.
56
+ * Explicit list of per-component packages. Each entry maps a
57
+ * component name to its npm package. Mutually exclusive with
58
+ * `packageName`.
59
59
  */
60
60
  components?: ComponentRegistration[];
61
61
  /**
@@ -82,11 +82,6 @@ export interface RenderResult {
82
82
  /** Preload link tags (empty string when using fast-build). */
83
83
  preloadLinks: string;
84
84
  }
85
- /**
86
- * Parse a JavaScript default value string from CEM into a JSON-safe value.
87
- * @internal
88
- */
89
- export declare function parseDefaultValue(raw: string): unknown;
90
85
  /**
91
86
  * Replace the `{{styles}}` placeholder in an f-template with a
92
87
  * stylesheet `<link>` tag. Falls back to injecting after the opening
@@ -117,9 +112,9 @@ export declare function buildState(queryObj: Record<string, string>): Record<str
117
112
  *
118
113
  * Supports two modes:
119
114
  * - **`packageName`**: Scans a monolithic package's dist directory for
120
- * components in subdirectories (Fluent-style).
115
+ * components in subdirectories (monolithic layout).
121
116
  * - **`components`**: Uses an explicit list of per-component packages
122
- * with flat exports (MAI-style).
117
+ * with flat exports.
123
118
  */
124
119
  export declare function createSSRRenderer(options: SSRRendererOptions): {
125
120
  render: (queryObj: Record<string, string>) => RenderResult;
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/ssr/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAOH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,UAAU,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAErC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IACzB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;CACxB;AA4CD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA6BtD;AA0FD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAa1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAwCvE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG;IAC5D,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,YAAY,CAAC;CAC9D,CAkMA"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/ssr/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAOH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,UAAU,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAErC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IACzB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;CACxB;AAqFD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAa1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAwCvE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG;IAC5D,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,YAAY,CAAC;CAC9D,CA6KA"}
@@ -219,7 +219,7 @@ export function convertTemplate(viewTemplate, componentName) {
219
219
  // so the test harness can substitute it with a <link rel="stylesheet"> at
220
220
  // render time. Harness fallback auto-injects if the marker is missing, but
221
221
  // emitting it explicitly keeps generated output consistent with hand-authored
222
- // f-templates (see MAI core components).
222
+ // f-templates.
223
223
  fInner = fInner.replace(/(<template[^>]*>)/, `$1${stylesMarker}`);
224
224
  return `<f-template name="${componentName}" shadowrootmode="open">\n${fInner}\n</f-template>\n`;
225
225
  }
@@ -14,7 +14,7 @@
14
14
  * ```ts
15
15
  * import { generateWebuiTemplates } from "@microsoft/fast-test-harness/build/generate-webui-templates.js";
16
16
  *
17
- * await generateWebuiTemplates({ cwd: process.cwd(), tagPrefix: "mai" });
17
+ * await generateWebuiTemplates({ cwd: process.cwd(), tagPrefix: "contoso" });
18
18
  * ```
19
19
  */
20
20
  import { glob, mkdir, writeFile } from "node:fs/promises";
@@ -21,10 +21,10 @@
21
21
  * flat exports.
22
22
  * ```ts
23
23
  * const { render } = createSSRRenderer({
24
- * tagPrefix: "mai",
24
+ * tagPrefix: "contoso",
25
25
  * components: [
26
- * { name: "button", packageName: "@mai-ui/button" },
27
- * { name: "checkbox", packageName: "@mai-ui/checkbox" },
26
+ * { name: "button", packageName: "@contoso/button" },
27
+ * { name: "checkbox", packageName: "@contoso/checkbox" },
28
28
  * ],
29
29
  * });
30
30
  * ```
@@ -71,75 +71,6 @@ function resolvePackageRoot(packageName) {
71
71
  function toServerUrl(absolutePath, packageRoot) {
72
72
  return `/${relative(packageRoot, absolutePath).replace(/\\/g, "/")}`;
73
73
  }
74
- /**
75
- * Parse a JavaScript default value string from CEM into a JSON-safe value.
76
- * @internal
77
- */
78
- export function parseDefaultValue(raw) {
79
- const trimmed = raw.trim();
80
- if (trimmed === "" || trimmed === "undefined" || trimmed === "null") {
81
- return "";
82
- }
83
- if (trimmed === "true") {
84
- return true;
85
- }
86
- if (trimmed === "false") {
87
- return false;
88
- }
89
- if (trimmed.startsWith("'") || trimmed.startsWith('"')) {
90
- return trimmed.slice(1, -1);
91
- }
92
- const num = Number(trimmed);
93
- if (!Number.isNaN(num)) {
94
- return num;
95
- }
96
- try {
97
- return JSON.parse(trimmed);
98
- }
99
- catch {
100
- return "";
101
- }
102
- }
103
- /**
104
- * Load a Custom Elements Manifest and extract default state for
105
- * each custom element declaration. Returns a map of tag names to
106
- * their default property values.
107
- */
108
- function loadDefaultStateFromCEM(packageName, tagPrefix) {
109
- const cemPath = resolveSpecifier(`${packageName}/custom-elements.json`);
110
- if (!cemPath) {
111
- return new Map();
112
- }
113
- try {
114
- const cem = JSON.parse(readFileSync(cemPath, "utf8"));
115
- const result = new Map();
116
- for (const mod of cem.modules ?? []) {
117
- for (const decl of mod.declarations ?? []) {
118
- if (!decl.customElement) {
119
- continue;
120
- }
121
- const tagName = decl.tagName ?? `${tagPrefix}-${decl.name?.toLowerCase()}`;
122
- const state = {};
123
- for (const member of decl.members ?? []) {
124
- if (member.kind !== "field" || member.privacy !== "public") {
125
- continue;
126
- }
127
- state[member.name] =
128
- member.default != null
129
- ? parseDefaultValue(String(member.default))
130
- : "";
131
- }
132
- if (Object.keys(state).length > 0) {
133
- result.set(tagName, state);
134
- }
135
- }
136
- }
137
- return result;
138
- }
139
- catch {
140
- return new Map();
141
- }
142
- }
143
74
  /**
144
75
  * Load artifacts for a component from a monolithic package
145
76
  * (e.g., `@fluentui/web-components/button/template.html`).
@@ -155,7 +86,7 @@ function loadMonolithicComponent(packageName, componentDir, packageRoot) {
155
86
  }
156
87
  /**
157
88
  * Load artifacts for a component from a per-component package
158
- * (e.g., `@mai-ui/button/template.html`).
89
+ * (e.g., `@contoso/button/template.html`).
159
90
  */
160
91
  function loadPerPackageComponent(reg) {
161
92
  const fTemplatePath = resolveSpecifier(`${reg.packageName}/template.html`);
@@ -263,9 +194,9 @@ export function buildState(queryObj) {
263
194
  *
264
195
  * Supports two modes:
265
196
  * - **`packageName`**: Scans a monolithic package's dist directory for
266
- * components in subdirectories (Fluent-style).
197
+ * components in subdirectories (monolithic layout).
267
198
  * - **`components`**: Uses an explicit list of per-component packages
268
- * with flat exports (MAI-style).
199
+ * with flat exports.
269
200
  */
270
201
  export function createSSRRenderer(options) {
271
202
  const { tagPrefix } = options;
@@ -284,13 +215,13 @@ export function createSSRRenderer(options) {
284
215
  // Collect component artifacts from either mode.
285
216
  const artifacts = [];
286
217
  if (options.components) {
287
- // Per-component packages (MAI-style).
218
+ // Per-component packages
288
219
  for (const reg of options.components) {
289
220
  artifacts.push(loadPerPackageComponent(reg));
290
221
  }
291
222
  }
292
223
  else if (options.packageName) {
293
- // Monolithic package (Fluent-style).
224
+ // Monolithic package
294
225
  const packageRoot = resolvePackageRoot(options.packageName);
295
226
  const distDir = join(packageRoot, options.distDir ?? "dist/esm");
296
227
  const pattern = "**/*.template.html";
@@ -306,29 +237,13 @@ export function createSSRRenderer(options) {
306
237
  artifacts.push(loadMonolithicComponent(options.packageName, componentDir, packageRoot));
307
238
  }
308
239
  }
309
- // Populate maps and collect default state from CEM.
310
- const defaultStateByTag = new Map();
240
+ // Populate template and style maps from collected artifacts.
311
241
  for (const art of artifacts) {
312
242
  if (art.fTemplate) {
313
243
  fTemplatesByName.set(art.componentName, art.fTemplate);
314
244
  }
315
245
  styleUrlsByName.set(art.componentName, art.stylesUrl);
316
246
  }
317
- // Load CEM defaults per-package (each package may contain multiple elements).
318
- if (options.components) {
319
- for (const reg of options.components) {
320
- const cemDefaults = loadDefaultStateFromCEM(reg.packageName, tagPrefix);
321
- for (const [tag, state] of cemDefaults) {
322
- defaultStateByTag.set(tag, state);
323
- }
324
- }
325
- }
326
- else if (options.packageName) {
327
- const cemDefaults = loadDefaultStateFromCEM(options.packageName, tagPrefix);
328
- for (const [tag, state] of cemDefaults) {
329
- defaultStateByTag.set(tag, state);
330
- }
331
- }
332
247
  // Inject styles into f-templates, then parse into the WASM
333
248
  // templates map (tag-name → inner template content).
334
249
  const templatesMap = {};
@@ -370,11 +285,7 @@ export function createSSRRenderer(options) {
370
285
  return {
371
286
  render(queryObj = {}) {
372
287
  const entryHtml = buildEntryHtml(queryObj);
373
- const requestState = buildState(queryObj);
374
- // Merge CEM default state (base) with request state (overrides).
375
- const tagName = String(queryObj.tagName ?? "");
376
- const defaults = defaultStateByTag.get(tagName) ?? {};
377
- const state = { ...defaults, ...requestState };
288
+ const state = buildState(queryObj);
378
289
  let fixture = "";
379
290
  if (entryHtml) {
380
291
  try {
@@ -391,7 +302,7 @@ export function createSSRRenderer(options) {
391
302
  const openTagRe = new RegExp(`(<${nestedTag}(?=[\\s>/])[^>]*>)(?!\\s*<template[\\s>])`, "g");
392
303
  fixture = fixture.replace(openTagRe, (_, open) => {
393
304
  // Render this element in isolation.
394
- const solo = wasm.render_with_templates(`${open}</${nestedTag}>`, templatesJson, JSON.stringify(defaultStateByTag.get(nestedTag) ?? {}), "camelCase");
305
+ const solo = wasm.render_with_templates(`${open}</${nestedTag}>`, templatesJson, "{}", "camelCase");
395
306
  // Extract the DSD that was injected.
396
307
  const dsdStart = solo.indexOf("<template shadowrootmode");
397
308
  const dsdEnd = solo.lastIndexOf("</template>");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/fast-test-harness",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "author": {
5
5
  "name": "Microsoft",
6
6
  "url": "https://discord.gg/FcSNfg4"