From c0b0f06a2590a65405673dffdeb624460133f448 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Mon, 14 Sep 2026 10:11:50 -0700 Subject: [PATCH 1/5] initial wdio test implementation --- .changeset/inline-wdio-story-tests.md | 7 + .../references/tests-and-stories.md | 12 + apps/storybook/AGENTS.md | 5 + apps/storybook/README.md | 11 + apps/storybook/agent-map.yaml | 6 + packages/agentic/components/AGENTS.md | 4 + packages/agentic/components/package.json | 6 +- .../components/src/components/AGENTS.md | 3 + .../src/components/button/button.stories.tsx | 23 +- packages/agentic/components/tsconfig.json | 3 + .../agentic/components/tsconfig.stories.json | 19 ++ .../references/test-integration.md | 12 + .../agentic/desktop-driver/src/storybook.ts | 1 + .../src/wdio/DesktopWebdriver.ts | 44 ++- .../agentic/desktop-driver/src/wdio/index.ts | 4 +- .../src/wdio/wdioRunner.contract.cjs | 9 +- packages/agentic/storybook-desktop/README.md | 91 ++++++ .../storybook-desktop/config/babel.cjs | 3 +- .../config/transform-story-tests.cjs | 261 ++++++++++++++++++ .../config/transform-story-tests.d.cts | 15 + .../agentic/storybook-desktop/package.json | 10 +- .../src/cli/DesktopStorybookCli.test.ts | 45 +++ .../src/cli/DesktopStorybookCli.ts | 74 +++++ .../storybook-desktop/src/cli/README.md | 7 + .../src/cli/createDesktopStorybookCommand.ts | 30 ++ .../storybook-desktop/src/cli/index.ts | 1 + .../storybook-desktop/src/config/index.ts | 1 + .../src/config/makeDesktopStorybookConfig.ts | 10 + .../src/config/transformStoryTests.test.ts | 93 +++++++ .../storybook-desktop/src/config/wdio.test.ts | 27 ++ .../storybook-desktop/src/config/wdio.ts | 30 ++ .../src/driver/storyManifest.test.ts | 58 ++++ .../src/driver/storyManifest.ts | 8 + .../src/testing/extractWdioTests.test.ts | 218 +++++++++++++++ .../src/testing/extractWdioTests.ts | 35 +++ .../storybook-desktop/src/testing/index.ts | 1 + .../src/testing/runWdioTests.ts | 245 ++++++++++++++++ .../storybook-desktop/src/testing/types.ts | 15 + .../src/testing/wdio.types.test.ts | 37 +++ .../src/testing/wdioRunner.contract.cjs | 126 +++++++++ .../src/testing/wdioRunner.test.ts | 36 +++ .../storybook-desktop/src/testing/worker.ts | 37 +++ yarn.lock | 6 + 43 files changed, 1673 insertions(+), 16 deletions(-) create mode 100644 .changeset/inline-wdio-story-tests.md create mode 100644 packages/agentic/components/tsconfig.stories.json create mode 100644 packages/agentic/storybook-desktop/config/transform-story-tests.cjs create mode 100644 packages/agentic/storybook-desktop/config/transform-story-tests.d.cts create mode 100644 packages/agentic/storybook-desktop/src/config/transformStoryTests.test.ts create mode 100644 packages/agentic/storybook-desktop/src/config/wdio.test.ts create mode 100644 packages/agentic/storybook-desktop/src/config/wdio.ts create mode 100644 packages/agentic/storybook-desktop/src/testing/extractWdioTests.test.ts create mode 100644 packages/agentic/storybook-desktop/src/testing/extractWdioTests.ts create mode 100644 packages/agentic/storybook-desktop/src/testing/index.ts create mode 100644 packages/agentic/storybook-desktop/src/testing/runWdioTests.ts create mode 100644 packages/agentic/storybook-desktop/src/testing/types.ts create mode 100644 packages/agentic/storybook-desktop/src/testing/wdio.types.test.ts create mode 100644 packages/agentic/storybook-desktop/src/testing/wdioRunner.contract.cjs create mode 100644 packages/agentic/storybook-desktop/src/testing/wdioRunner.test.ts create mode 100644 packages/agentic/storybook-desktop/src/testing/worker.ts diff --git a/.changeset/inline-wdio-story-tests.md b/.changeset/inline-wdio-story-tests.md new file mode 100644 index 0000000000..42ba5ee87e --- /dev/null +++ b/.changeset/inline-wdio-story-tests.md @@ -0,0 +1,7 @@ +--- +"@fluentui-react-native/storybook-desktop": minor +"@fluentui-react-native/desktop-driver": minor +"@fluentui-react-native/components": patch +--- + +Add opt-in executable WebdriverIO callbacks in React Native stories, Storybook-owned test configuration, isolated Node execution, and Button examples. diff --git a/.github/skills/agentic-component-authoring/references/tests-and-stories.md b/.github/skills/agentic-component-authoring/references/tests-and-stories.md index 3c1069e2d4..092259fbbb 100644 --- a/.github/skills/agentic-component-authoring/references/tests-and-stories.md +++ b/.github/skills/agentic-component-authoring/references/tests-and-stories.md @@ -145,6 +145,18 @@ use role, accessible name, state, and value assertions to verify the public accessibility contract. Button, Checkbox, and Input defaults are the canonical initial examples. +For executable Node tests, Button also demonstrates a top-level `wdio` +callback typed with `WdioStory` from +`@fluentui-react-native/storybook-desktop/testing`. Wrap `StoryObj` in `WdioStory` +and import the type only. The callback receives `browser`, `expect`, `desktop`, +`signal`, and `skip`; it cannot capture story-module bindings. Node-compatible +helpers may be dynamically imported inside the callback. The shared Babel +configuration strips test bodies before Metro resolves dependencies. +Run these with `yarn storybook test --`, independently of the static +plan runner. See the [executable test contract](../../../../packages/agentic/storybook-desktop/README.md#executable-tests-inside-stories). +The components package's `test:stories` project checks the experimental +Button stories without emitting them into the component library. + Button uses focused appearance, size, shape, icon, selection, disabled, and constrained-content stories. Icon uses a source and size overview plus focused font, image, SVG, size, color, and accessibility stories. diff --git a/apps/storybook/AGENTS.md b/apps/storybook/AGENTS.md index 366136cdab..69f4fc2a12 100644 --- a/apps/storybook/AGENTS.md +++ b/apps/storybook/AGENTS.md @@ -48,6 +48,11 @@ Read [`agent-map.yaml`](agent-map.yaml) first for the compact architecture, look - Authored tests belong in component story `parameters.desktopDriver`, not in this app. The app owns identity, package discovery, platform exclusions, and generated manifests. +- Executable tests may instead use a top-level component story `wdio` + callback, run with `yarn storybook test --`. Defaults belong in + `storybook.config.mts` under `wdio`; the shared Babel config removes + callbacks from native bundles. Keep this opt-in runner separate from the + static-plan smoke gate. - Keep `storybook-desktop.generated`, reports, trees, screenshots, and run manifests ignored. Never patch generated runtime identity or story manifests. - Treat the exact-platform and portable-plan digests as contracts. A dynamic or diff --git a/apps/storybook/README.md b/apps/storybook/README.md index 929e5af860..1a97c1060a 100644 --- a/apps/storybook/README.md +++ b/apps/storybook/README.md @@ -26,6 +26,17 @@ app-owned, while the platform-neutral UI, configuration helpers, and `storybook- from the shared package. The app exposes only the shared CLI entry points; native lifecycle scripts remain package-owned. +Button also experiments with executable `wdio` callbacks directly in its +stories. Run `yarn storybook test --macos --list` to discover them. With +`yarn storybook driver --macos` running and the app launched via +`yarn storybook run --macos`, run +`yarn storybook test --macos --story 'components-button--*'`. +The shared runner extracts the callbacks for Node, injects WebdriverIO and +native assertions, and leaves test dependencies out of Metro. Optional runner +defaults belong under `wdio` in `storybook.config.mts`; no separate test config +is needed. See the [authoring contract](../../packages/agentic/storybook-desktop/README.md#executable-tests-inside-stories). +This command is separate from the existing static-plan smoke gate. + ## Layout ``` diff --git a/apps/storybook/agent-map.yaml b/apps/storybook/agent-map.yaml index f9c54bc42a..1a84ccc1f3 100644 --- a/apps/storybook/agent-map.yaml +++ b/apps/storybook/agent-map.yaml @@ -26,6 +26,12 @@ services: instance: command: yarn storybook instance -- policy: use-reported-ports-and-target-id + executable_tests: + command: yarn storybook test -- + discovery: yarn storybook test -- --list + authoring: top-level wdio callbacks in CSF3 stories + config: storybook.config.mts wdio settings + lifecycle: attached-session-per-story-with-isolated-node-worker agent_commands: generate_manifest: yarn storybook manifest -- diff --git a/packages/agentic/components/AGENTS.md b/packages/agentic/components/AGENTS.md index ec8dde4746..ebb72b8898 100644 --- a/packages/agentic/components/AGENTS.md +++ b/packages/agentic/components/AGENTS.md @@ -12,6 +12,10 @@ invariants; detailed authoring recipes live in the - Portable desktop story tests are static `parameters.desktopDriver` plans. Use the public authoring types, stable `testID` selectors, declarative capability requirements, and no platform branches or executable callbacks. +- Executable tests are a separate opt-in top-level `wdio` callback, typed + with `WdioStory` from `storybook-desktop/testing` using `import type`. + Keep callbacks self-contained; dynamically import Node helpers inside + them so the shared Babel transform can exclude them from native bundles. - Storybook application, native project, Metro, bundle, or CocoaPods work follows the [Storybook instructions](../../../apps/storybook/AGENTS.md) and the `agentic-storybook-development` skill. - Native React Native Windows Fabric component work follows the diff --git a/packages/agentic/components/package.json b/packages/agentic/components/package.json index 089a38cac4..f8ef186834 100644 --- a/packages/agentic/components/package.json +++ b/packages/agentic/components/package.json @@ -35,7 +35,8 @@ "format": "fluentui-scripts format", "lint": "fluentui-scripts lint", "report:spec-source-drift": "node ./scripts/spec-contracts/report-spec-source-drift.cjs", - "test": "node ./scripts/spec-contracts/check-spec-contracts.cjs && fluentui-scripts jest", + "test": "node ./scripts/spec-contracts/check-spec-contracts.cjs && yarn test:stories && fluentui-scripts jest", + "test:stories": "tsc -p tsconfig.stories.json", "update-snapshots": "fluentui-scripts jest -u" }, "dependencies": { @@ -48,12 +49,14 @@ "@fluentui-react-native/desktop-driver": "workspace:*", "@fluentui-react-native/focus-zone": "workspace:*", "@fluentui-react-native/scripts": "workspace:*", + "@fluentui-react-native/storybook-desktop": "workspace:*", "@office-iss/react-native-win32": "^0.81.0", "@react-native-community/cli": "^20.0.0", "@react-native-community/cli-platform-android": "^20.0.0", "@react-native-community/cli-platform-ios": "^20.0.0", "@react-native/babel-preset": "^0.81.0", "@react-native/metro-config": "^0.81.0", + "@storybook/react-native": "^10.4.7", "@testing-library/react-native": "catalog:", "@types/react": "~19.1.4", "react": "19.1.4", @@ -61,6 +64,7 @@ "react-native-macos": "^0.81.0", "react-native-svg": "^15.12.1", "react-native-windows": "^0.81.0", + "storybook": "^10.4.0", "test-renderer": "catalog:" }, "peerDependencies": { diff --git a/packages/agentic/components/src/components/AGENTS.md b/packages/agentic/components/src/components/AGENTS.md index 104b6aa11d..96703f1a42 100644 --- a/packages/agentic/components/src/components/AGENTS.md +++ b/packages/agentic/components/src/components/AGENTS.md @@ -59,6 +59,9 @@ audit. satisfying `DesktopStoryTests`. Use one stable `testID` per interacted element, declare capabilities, and assert public native semantics rather than implementation structure. +- For imperative tests, use the separate top-level `wdio` callback contract + demonstrated by Button and run it through `storybook test`. Do not put + functions in static plans or import Node helpers at story-module scope. ## Focused references diff --git a/packages/agentic/components/src/components/button/button.stories.tsx b/packages/agentic/components/src/components/button/button.stories.tsx index ea49a1937d..9bf94a220a 100644 --- a/packages/agentic/components/src/components/button/button.stories.tsx +++ b/packages/agentic/components/src/components/button/button.stories.tsx @@ -5,6 +5,7 @@ import { StyleSheet, Text, View } from 'react-native'; import type { Meta, StoryObj } from '@storybook/react-native'; import type { DesktopStoryTests } from '@fluentui-react-native/desktop-driver/authoring'; +import type { WdioStory } from '@fluentui-react-native/storybook-desktop/testing'; import { Button } from './button'; import type { ButtonAppearance, ButtonShape, ButtonSize } from './button.types'; @@ -74,10 +75,17 @@ const meta: Meta = { export default meta; -type Story = StoryObj; +type Story = WdioStory>; export const Default: Story = { tags: ['desktop-e2e'], + wdio: async ({ browser, expect }) => { + const assert: typeof import('node:assert') = (await import('node:assert')).default; + const button = await browser.$('~agentic-storybook-button'); + await expect(button).toExist(); + await expect(button).toBeEnabled(); + assert.strictEqual(await button.getTagName(), 'button'); + }, parameters: { desktopDriver: { version: 1, @@ -243,6 +251,16 @@ export const Selected: Story = { }; export const ExternallyDrivenSelection: Story = { + wdio: async ({ browser, desktop, expect }) => { + const favorite = await browser.$('~agentic-storybook-button-favorite'); + const reset = await browser.$('~agentic-storybook-button-reset'); + await expect(favorite).toBeEnabled(); + await desktop.expect({ state: 'checked', target: { testId: 'agentic-storybook-button-favorite' }, value: false }); + await favorite.click(); + await desktop.expect({ state: 'checked', target: { testId: 'agentic-storybook-button-favorite' }, value: true }); + await reset.click(); + await desktop.expect({ state: 'checked', target: { testId: 'agentic-storybook-button-favorite' }, value: false }); + }, render: () => { const ToggleGroup = () => { const [selected, setSelected] = useState(false); @@ -254,8 +272,9 @@ export const ExternallyDrivenSelection: Story = { onPress={() => setSelected(!selected)} selected={selected} selectedIcon={filledStarIcon} + testID="agentic-storybook-button-favorite" /> -