diff --git a/packages/@luke-ui/react/src/blockquote/blockquote.browser.test.tsx b/packages/@luke-ui/react/src/blockquote/blockquote.browser.test.tsx new file mode 100644 index 00000000..4661fbce --- /dev/null +++ b/packages/@luke-ui/react/src/blockquote/blockquote.browser.test.tsx @@ -0,0 +1,13 @@ +import { testConformance } from '../conformance/helpers.js'; +import { render } from '../test-utils/render.js'; +import { Blockquote } from './index.js'; + +testConformance({ + path: 'blockquote', + getTarget: (result) => { + const target = result.container.firstElementChild; + if (!(target instanceof HTMLElement)) throw new Error('Expected a Blockquote element.'); + return target; + }, + render: (props = {}) => render(
Quoted text), +}); diff --git a/packages/@luke-ui/react/src/code/code.browser.test.tsx b/packages/@luke-ui/react/src/code/code.browser.test.tsx new file mode 100644 index 00000000..29004c2d --- /dev/null +++ b/packages/@luke-ui/react/src/code/code.browser.test.tsx @@ -0,0 +1,13 @@ +import { testConformance } from '../conformance/helpers.js'; +import { render } from '../test-utils/render.js'; +import { Code } from './index.js'; + +testConformance({ + path: 'code', + getTarget: (result) => { + const target = result.container.firstElementChild; + if (!(target instanceof HTMLElement)) throw new Error('Expected a Code element.'); + return target; + }, + render: (props = {}) => render(
npm install),
+});
diff --git a/packages/@luke-ui/react/src/conformance/manifest.ts b/packages/@luke-ui/react/src/conformance/manifest.ts
index 370df091..f127db1c 100644
--- a/packages/@luke-ui/react/src/conformance/manifest.ts
+++ b/packages/@luke-ui/react/src/conformance/manifest.ts
@@ -12,36 +12,42 @@ export type ComponentTestManifestEntry = {
// Keep this list explicit. An empty `conformance` list is a deliberate exception, not an omission.
export const componentTestManifest = [
- ['Blockquote', 'blockquote', [], 'none', 'none'],
+ ['Blockquote', 'blockquote', ['dom'], 'none', 'none'],
['Box', 'box', ['dom'], 'none', 'applicable'],
['Button', 'button', ['dom'], 'required', 'applicable'],
- ['Button primitive', 'primitives/button', [], 'none', 'none'],
+ ['Button primitive', 'primitives/button', ['dom'], 'none', 'none'],
// Fields take `inputRef` and RAC moves `id` onto the control, so they cannot satisfy `dom`.
['Checkbox', 'checkbox', ['field'], 'required', 'applicable'],
+ // Multi-part field root. RAC moves `id` onto the control.
['Checkbox primitive', 'primitives/checkbox', [], 'none', 'none'],
- ['Code', 'code', [], 'none', 'none'],
+ ['Code', 'code', ['dom'], 'none', 'none'],
// Fields take `inputRef` and RAC moves `id` onto the control, so they cannot satisfy `dom`.
['ComboboxField', 'combobox-field', ['field'], 'required', 'applicable'],
+ // Multi-part collection. No single root receives the public DOM props.
['Combobox primitive', 'primitives/combobox', [], 'none', 'none'],
- ['Em', 'em', [], 'none', 'none'],
- ['Emoji', 'emoji', [], 'none', 'applicable'],
+ ['Em', 'em', ['dom'], 'none', 'none'],
+ ['Emoji', 'emoji', ['dom'], 'none', 'applicable'],
+ // Multi-part composition. No single root is the documented element.
['Field primitive', 'primitives/field', [], 'none', 'none'],
- ['Heading', 'heading', [], 'none', 'applicable'],
+ ['Heading', 'heading', ['dom'], 'none', 'applicable'],
+ // Public props are a closed set. `ref` and `data-*` are not forwarded.
['Icon', 'icon', [], 'none', 'applicable'],
['IconButton', 'icon-button', ['dom'], 'required', 'applicable'],
+ // Multi-part composition. No single root is the documented element.
['Input group primitive', 'primitives/input-group', [], 'none', 'none'],
- ['Kbd', 'kbd', [], 'none', 'none'],
+ ['Kbd', 'kbd', ['dom'], 'none', 'none'],
['Link', 'link', ['dom'], 'required', 'applicable'],
- ['LoadingSkeleton', 'loading-skeleton', [], 'none', 'applicable'],
- ['LoadingSpinner', 'loading-spinner', [], 'none', 'applicable'],
- ['Numeral', 'numeral', [], 'none', 'applicable'],
- ['Quote', 'quote', [], 'none', 'none'],
- ['Strong', 'strong', [], 'none', 'none'],
- ['Text', 'text', [], 'none', 'applicable'],
+ ['LoadingSkeleton', 'loading-skeleton', ['dom'], 'none', 'applicable'],
+ ['LoadingSpinner', 'loading-spinner', ['dom'], 'none', 'applicable'],
+ ['Numeral', 'numeral', ['dom'], 'none', 'applicable'],
+ ['Quote', 'quote', ['dom'], 'none', 'none'],
+ ['Strong', 'strong', ['dom'], 'none', 'none'],
+ ['Text', 'text', ['dom'], 'none', 'applicable'],
// Fields take `inputRef` and RAC moves `id` onto the control, so they cannot satisfy `dom`.
['TextField', 'text-field', ['field'], 'required', 'applicable'],
+ // Theme utilities, not a component with a rendered root.
['Theme', 'theme', [], 'none', 'none'],
- ['VisuallyHidden', 'visually-hidden', [], 'none', 'none'],
+ ['VisuallyHidden', 'visually-hidden', ['dom'], 'none', 'none'],
].map(([name, path, conformance, integrationTripwire, visualApplicability]) => ({
conformance,
integrationTripwire,
diff --git a/packages/@luke-ui/react/src/em/em.browser.test.tsx b/packages/@luke-ui/react/src/em/em.browser.test.tsx
new file mode 100644
index 00000000..f15e9e06
--- /dev/null
+++ b/packages/@luke-ui/react/src/em/em.browser.test.tsx
@@ -0,0 +1,13 @@
+import { testConformance } from '../conformance/helpers.js';
+import { render } from '../test-utils/render.js';
+import { Em } from './index.js';
+
+testConformance({
+ path: 'em',
+ getTarget: (result) => {
+ const target = result.container.firstElementChild;
+ if (!(target instanceof HTMLElement)) throw new Error('Expected an Em element.');
+ return target;
+ },
+ render: (props = {}) => render(stressed),
+});
diff --git a/packages/@luke-ui/react/src/emoji/emoji.browser.test.tsx b/packages/@luke-ui/react/src/emoji/emoji.browser.test.tsx
new file mode 100644
index 00000000..aa3a08f0
--- /dev/null
+++ b/packages/@luke-ui/react/src/emoji/emoji.browser.test.tsx
@@ -0,0 +1,13 @@
+import { testConformance } from '../conformance/helpers.js';
+import { render } from '../test-utils/render.js';
+import { Emoji } from './index.js';
+
+testConformance({
+ path: 'emoji',
+ getTarget: (result) => {
+ const target = result.locator.getByRole('img', { name: 'Celebration' }).element();
+ if (!(target instanceof HTMLElement)) throw new Error('Expected an Emoji element.');
+ return target;
+ },
+ render: (props = {}) => render(short quote), +}); diff --git a/packages/@luke-ui/react/src/strong/strong.browser.test.tsx b/packages/@luke-ui/react/src/strong/strong.browser.test.tsx new file mode 100644 index 00000000..a852439e --- /dev/null +++ b/packages/@luke-ui/react/src/strong/strong.browser.test.tsx @@ -0,0 +1,13 @@ +import { testConformance } from '../conformance/helpers.js'; +import { render } from '../test-utils/render.js'; +import { Strong } from './index.js'; + +testConformance({ + path: 'strong', + getTarget: (result) => { + const target = result.container.firstElementChild; + if (!(target instanceof HTMLElement)) throw new Error('Expected a Strong element.'); + return target; + }, + render: (props = {}) => render(important), +}); diff --git a/packages/@luke-ui/react/src/text/text.browser.test.tsx b/packages/@luke-ui/react/src/text/text.browser.test.tsx new file mode 100644 index 00000000..674c002a --- /dev/null +++ b/packages/@luke-ui/react/src/text/text.browser.test.tsx @@ -0,0 +1,13 @@ +import { testConformance } from '../conformance/helpers.js'; +import { render } from '../test-utils/render.js'; +import { Text } from './index.js'; + +testConformance({ + path: 'text', + getTarget: (result) => { + const target = result.container.firstElementChild; + if (!(target instanceof HTMLElement)) throw new Error('Expected a Text element.'); + return target; + }, + render: (props = {}) => render(