Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/bright-books-configure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fluentui-react-native/storybook-desktop": minor
"@fluentui-react-native/storybook-desktop-runtime": minor
---

Add reusable platform-aware configuration and CLI APIs for serving, preparing, bundling, building, running, and smoke testing desktop Storybook applications. The package now owns complete Windows Fabric and Win32 smoke lifecycles, native host launch, synchronized story traversal, desktop UX checks, process cleanup, and per-enlistment bundle and service isolation. React Native runtime code and peers are isolated in a companion package so Yarn's pnpm linker can invoke the peer-free CLI through a physical workspace locator.
17 changes: 17 additions & 0 deletions .changeset/fresh-dragons-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@fluentui-react-native/desktop-driver": minor
"@fluentui-react-native/components": patch
"@fluentui-react-native/storybook-desktop": minor
"@fluentui-react-native/storybook-desktop-runtime": minor
---

Add the platform-neutral W3C desktop driver and integrate Storybook manifests,
authenticated runtime readiness, deterministic preview resets, and same-process
driver supervision. Add portable Button, Checkbox, and Input story plans for
WebdriverIO and agent validation. Desktop Storybook smoke runs can now either
traverse the complete catalog or traverse it and then execute the authored
desktop-e2e plans.
Smoke startup now waits through the initial Metro compilation, macOS cleanup
terminates the exact bundle-identifier process, and Windows CI installs the
required Windows App Runtime while the shared registration lifecycle installs
the SDK-provided Debug VCLibs frameworks.
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,38 @@ layout order, or native class names. Keep the initial args deterministic and
add identifiers only to the small smoke set that agents and CI actively
validate.

Portable desktop automation is authored inline under
`parameters.desktopDriver` and typed with `DesktopStoryTests` from
`@fluentui-react-native/desktop-driver/authoring`:

```tsx
export const Default: Story = {
tags: ['desktop-e2e'],
parameters: {
desktopDriver: {
version: 1,
tests: [
{
id: 'enabled-button',
steps: [
{ action: 'wait', target: { testId: 'story-button' } },
{ expect: { state: 'enabled', target: { testId: 'story-button' }, value: true } },
],
},
],
} satisfies DesktopStoryTests,
},
};
```

Keep the plan a static JSON literal. Storybook extracts it without importing
the React Native module, so variables, functions, spreads, computed properties,
and runtime platform branches are rejected. Express real differences with
`platforms`, `requires`, and explicit skip results. Use `testID` for actions;
use role, accessible name, state, and value assertions to verify the public
accessibility contract. Button, Checkbox, and Input defaults are the canonical
initial examples.

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.

Expand All @@ -132,8 +164,8 @@ yarn workspace @fluentui-react-native/components format
yarn workspace @fluentui-react-native/components lint
yarn workspace @fluentui-react-native/components build
yarn workspace @fluentui-react-native/components test
yarn workspace @fluentui-react-native/agentic-components-storybook bundle:macos
yarn workspace @fluentui-react-native/agentic-components-storybook bundle:windows
yarn workspace @fluentui-react-native/agentic-components-storybook storybook bundle --macos
yarn workspace @fluentui-react-native/agentic-components-storybook storybook bundle --windows
```

Run the smallest affected package test while iterating. Run the full package sequence before completion. Run the root
Expand Down
52 changes: 38 additions & 14 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,15 +208,15 @@ jobs:
- name: Bundle macOS
run: |
set -eox pipefail
yarn bundle:macos
yarn storybook bundle --macos
working-directory: apps/storybook

- name: Pod install
run: yarn pods:macos
run: yarn storybook prep --macos
working-directory: apps/storybook

- name: Build macOS app
run: yarn macos:build
- name: Run through storybook smoke tests
run: yarn storybook smoke --macos --mode stories-and-tests
working-directory: apps/storybook
env:
CCACHE_DISABLE: 1
Expand Down Expand Up @@ -355,18 +355,36 @@ jobs:
- name: Build packages
run: yarn build

- name: Bundle Windows
run: yarn bundle:windows
- name: Prep Windows Storybook
run: yarn storybook prep --windows
working-directory: apps/storybook

- name: Generate RNW app
run: yarn install-windows-test-app --use-nuget
working-directory: apps/storybook
- name: Install Windows App Runtime 1.8
shell: pwsh
run: |
$installer = Join-Path $env:RUNNER_TEMP 'WindowsAppRuntimeInstall-x64.exe'
Invoke-WebRequest 'https://aka.ms/windowsappsdk/1.8/1.8.260804001/windowsappruntimeinstall-x64.exe' -OutFile $installer
& $installer --quiet
if ($LASTEXITCODE -ne 0) {
throw "Windows App Runtime installer exited with code $LASTEXITCODE."
}

- name: Build RNW app
run: yarn rnx-cli run-windows --no-packager --no-deploy --no-launch
$runtime = Get-AppxPackage -Name 'Microsoft.WindowsAppRuntime.1.8'
if (-not $runtime) {
throw 'Microsoft.WindowsAppRuntime.1.8 was not registered for the runner user.'
}

- name: Smoke test Windows Storybook
run: yarn storybook smoke --windows --mode stories-and-tests
working-directory: apps/storybook

- name: Upload Windows Storybook artifacts
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: Storybook_windows_Dump
path: apps/storybook/artifacts/windows

win32:
name: Win32 PR
runs-on: windows-latest
Expand Down Expand Up @@ -414,7 +432,6 @@ jobs:
path: |
apps/E2E/reports
apps/E2E/errorShots
apps/storybook/artifacts/win32

win32-storybook:
name: Win32 Storybook PR
Expand All @@ -436,10 +453,17 @@ jobs:
- name: Build packages
run: yarn build

- name: Bundle Win32
run: yarn bundle:win32
- name: Smoke test Win32 Storybook
run: yarn storybook smoke --win32 --mode stories-and-tests
working-directory: apps/storybook

- name: Upload Win32 Storybook artifacts
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: Storybook_win32_Dump
path: apps/storybook/artifacts/win32

check-changesets:
name: Check for Changesets
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ packageExtensions:
dependencies:
"@svgr/plugin-jsx": "*"
"@svgr/plugin-svgo": "*"
"@storybook/react-native@10.5.4":
dependencies:
esbuild: "0.28.1"
"@wdio/appium-service@*":
dependencies:
appium: "*"
Expand Down
2 changes: 2 additions & 0 deletions apps/storybook/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by the withStorybook metro wrapper / storybook-generate script
src/storybook.requires.ts
src/storybook.requires.js
storybook-desktop.generated/

# Metro bundle output
*.jsbundle
Expand All @@ -12,6 +13,7 @@ macos/build/
macos/DerivedData/
macos/Podfile.lock
macos/.xcode.env
macos/.storybook-desktop/
macos/*.xcodeproj/
macos/*.xcworkspace/
windows/.vs/
Expand Down
84 changes: 56 additions & 28 deletions apps/storybook/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,56 +13,86 @@ Read this file, `README.md`, and `package.json` before changing the Storybook ap
- Run `yarn` from the repository root only after dependency manifests change or when a declared command fails because a
dependency is missing.
- Preserve unrelated manifest and lockfile edits already present in the worktree.
- Keep only native command exceptions and ownership-specific smoke settings in `storybook.config.mts`. Standard macOS
and Windows prep, bundle, build, and run commands come from the shared config, derive identity from `app.json`, and
route through `rnx-cli`. Use `yarn storybook <command> --<platform>`; do not add platform aliases or app-local
lifecycle scripts.
- `app.json` owns the custom `storybook.testIDPrefix`. Do not create another
app identity file or duplicate the prefix in runtime source.

## Desktop Driver workflow

- Use `yarn storybook manifest --<platform>` to validate static story-plan
extraction.
- Use `yarn storybook driver --<platform>` to start Metro, the Storybook
channel/MCP listener, and the WebDriver listener under one owned supervisor.
- Use the app's `yarn desktop-driver` script for JSON story-run and agent
commands against that listener.
- The Stage 1 provider is deliberately fake. Do not add Windows or macOS native
automation code until the corresponding Stage 2 plan begins.
- Authored tests belong in component story `parameters.desktopDriver`, not in
this app. The app owns identity, package discovery, platform exclusions, and
generated manifests.
- 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
invalid plan must fail generation rather than disappear from the manifest.
- Preserve nonce-authenticated runtime hello/readiness/error messages and
native story-root verification; do not fall back to uncorrelated channel
events.

## macOS native workflow

- Run `pods:macos` for normal project generation and pod installation. Do not run `pod install --project-directory=...`
from the repository root: CocoaPods keeps that working directory for React Native CLI autolinking under the pnpm
linker.
- Run `pods:macos:update` when generated Pods came from an older React Native macOS patch release and CocoaPods reports a
changed local podspec.
- Run `bundle:macos` to verify the JavaScript bundle, `macos:build` for a non-launching native build, and
`macos:build:clean` after changing pods, native workarounds, Xcode settings, or React Native versions.
- Run `yarn storybook prep --macos` for project generation and Pod installation. Do not run CocoaPods from the
repository root because subprocess dependency resolution must start in this workspace.
- Run `yarn storybook bundle --macos` for the JavaScript bundle, `yarn storybook build --macos` for a non-launching
native build, and `yarn storybook smoke --macos` for the complete owned lifecycle.
- Preserve the shared smoke instance context: its canonical-root hash coordinates the macOS bundle identifier,
Storybook port, Metro port, generated runtime polyfill, and exact app shutdown. Do not replace those values with
process-name matching or fixed smoke ports.
- Only `macos/Podfile` is hand-authored. The workspace, Pods, Podfile.lock, build directory, and DerivedData are generated
and ignored; never patch or commit them.
and ignored; `storybook-desktop.generated` and `macos/.storybook-desktop` are generated instance state. Never patch or
commit these outputs.
- Diagnose the first actionable CocoaPods or compiler error before editing configuration. If autolinking claims a listed
dependency is missing, verify resolution from this app directory before adding another dependency.
- Avoid patching generated pod source. If a temporary source patch is unavoidable, document its exact version boundary.
When removing it, reinstall the affected pod and perform a clean build so stale generated source cannot mask the result.

## Windows native workflow

- Run `windows:info` before investigating a machine-specific toolchain failure.
- Use `windows:generate` to regenerate the Fabric solution, `windows` for the ordinary development build,
`windows:build` for a non-deploying native build, and `windows:offline` for the bundled Release workflow.
- Use `windows:agent` for the complete agent workflow: start the channel server and Metro, build and launch the app,
and validate the smoke stories through stable UI Automation selectors. Use `windows:agent:stop` to stop only the
process IDs recorded by that session.
- Use `yarn storybook prep --windows`, `bundle --windows`, `build --windows`, and `run --windows` for individual
stages. Use `yarn storybook smoke --windows --mode stories` for the package-owned generation, channel server, native
build and registration, Metro launch, full indexed-story traversal, and ownership-safe cleanup. Use
`--mode stories-and-tests` to run the component-authored desktop-e2e plans after the complete traversal.
- Keep the Windows story-pattern overrides in `storybook.config.mts`; the
current Accordion and Callout Fabric stories still fail-fast the RNW 0.81
host during traversal.
- WinAppDriver screenshots are not a reliable capture path for WinAppSDK Composition content. After selecting a story
with `storybook:control`, use the agent host's desktop screenshot tool when visual evidence is required.
through the Storybook control channel, use the agent host's desktop screenshot tool when visual evidence is required.
- Build logs, automation evidence, visual trees, screenshots, and session manifests belong under ignored
`artifacts/windows`.
- Stable native automation selectors use explicit `testID` props. Do not select by visible text, layout order, or
generated native class name.
- The Storybook REST control helper is `storybook:control`; `storybook:smoke` selects every indexed story and waits for
its rendered event.
- Keep generated solutions, packages, registrations, and build outputs uncommitted.

## Win32 native workflow

- Win32 is the `@office-iss/react-native-win32` Paper endpoint hosted by
`@office-iss/rex-win32`; do not treat it as the React Native Windows Fabric
endpoint or generate a `react-native-test-app` project for it.
- Run `bundle:win32` before `win32`. The bundle is the native dependency source
- Run `yarn storybook bundle --win32` before `yarn storybook run --win32`. The bundle is the native dependency source
for the prebuilt REX host.
- Keep package discovery and platform-specific story inclusion in
`storybook.config.mts`; keep `src/main.ts` as the shared config adapter.
- Keep shared Storybook source platform-neutral. Win32-specific source belongs
in `.win32.ts` or `.win32.tsx` files, and Metro platform resolution belongs in
`metro.config.js`.
- REX 0.81.1's V8 cannot parse the Unicode-property regular expressions
bundled by the current Storybook release. Keep their compatibility transform
bundled by the current Storybook release. Keep the shared desktop package's compatibility transform
scoped to Win32; remove it when the REX engine supports Unicode property
escapes.
- Win32 uses desktop-only chrome in `StorybookUI.win32.tsx` because
- Win32 uses desktop-only chrome in
`../../packages/agentic/storybook-desktop-runtime/src/StorybookUI.win32.tsx` because
react-native-win32 omits window dimensions and Storybook's mobile LiteUI
drawer crashes the Paper host. Keep its default layout conceptually aligned
with desktop LiteUI: persistent resizable Sidebar, story preview, and
Expand All @@ -75,16 +105,14 @@ Read this file, `README.md`, and `package.json` before changing the Storybook ap
- Keep macOS and Windows on upstream LiteUI. Replacing it with the reduced
Win32 chrome would regress addon controls and responsive behavior while
increasing local maintenance.
- Generate Win32 stories with `prebuild:win32`. It intentionally excludes the
ListItem and Accordion stories because their Paper implementations fail-fast
crash REX 0.81.1. Callout stories and the Callout-backed portal chrome remain
included; keep the ordinary `prebuild` catalog unchanged for macOS and
Windows.
- Keep the Win32 story-pattern override in `storybook.config.mts`. It intentionally excludes ListItem and Accordion
because their Paper implementations fail-fast crash REX 0.81.1.
- Keep the Win32 window title distinct from the Windows Fabric title so
automation never attaches to the wrong endpoint.
- Use `win32:ci` for the complete bundle/launch/smoke workflow. Its logs belong
under ignored `artifacts/win32`, and it must stop only the process IDs it
started or resolved by its exact port and window title.
- Use `yarn storybook smoke --win32 --mode stories` for the package-owned bundle, launch, native desktop-chrome checks,
full story traversal, and cleanup. Use `--mode stories-and-tests` to run the component-authored desktop-e2e plans
afterward. Logs belong under ignored `artifacts/win32`. Keep `build --win32` unsupported because the endpoint uses a
prebuilt host rather than an app-owned native project.

## Validation

Expand Down
Loading
Loading