Files
b51e189da0 chore(mobile): Add mobile Storybook screenshots to PRs (#6868)
* fix(ci): Fix web Storybook screenshots and add mobile support

## Web Storybook Screenshots Fixes

- Fix story ID generation to use `title` from meta export instead of file paths
  - Previously generated wrong IDs like `features-portfolio-components-x`
  - Now correctly generates `features-portfolio-x` matching Storybook's IDs
- Fix git credentials for pushing to storage branch
  - Was failing with "could not read Username" error
  - Now uses same working credential helper as page-screenshots workflow

## Mobile Storybook Screenshots (New)

Add new workflow to capture and post mobile component screenshots to PRs:
- Triggers on changes to `apps/mobile/src/**/*.stories.tsx`
- Builds mobile Storybook for web using React Native Web
- Serves locally and captures with Playwright (iPhone 14 Pro viewport)
- Posts screenshots to PR with 📱 Mobile Storybook Screenshots heading

New files:
- .github/workflows/mobile-storybook-screenshots.yml
- .github/scripts/generate-mobile-story-urls.js
- .github/scripts/capture-mobile-screenshots.js

* test: Add test changes to trigger screenshot workflows

- Add title to web CopyButton story (was missing explicit title)
- Add layout parameter to mobile SafeButton story

These changes test the screenshot workflows on this PR.

* fix(ci): Add path traversal protection to mobile screenshot server

Prevent potential path traversal attacks by:
- Resolving and normalizing the static directory path
- Validating that resolved file paths stay within the static directory
- Returning 403 Forbidden for any path traversal attempts

* fix(ci): Fix mobile Storybook build and add security fixes

- Run build from apps/mobile directory to fix monorepo path resolution
- Add --disable-telemetry flag to avoid telemetry module resolution error
- Add path traversal protection to static file server

* fix(ci): Add graceful handling for mobile Storybook build failures

The mobile Storybook build has a known issue with monorepo path
resolution for telemetry metadata. This update:

- Makes the build step continue on error with a warning
- Skips screenshot capture when build fails
- Prevents workflow failure when Storybook build issues occur

* fix(ci): Address Copilot review suggestions

- Fix regex pattern for title extraction to handle quotes properly
  (handles titles with apostrophes like "It's working")
- Fix binary content encoding in static server (don't use utf-8 for images/fonts)
- Filter out error screenshots from PR comments
- Use greedy regex matching for component names with hyphens

* chore: Remove test story changes

Revert test changes made to trigger screenshot workflows:
- Remove title from CopyButton story
- Remove layout parameter from SafeButton story

* refactor: Remove web storybook fixes (moved to separate PR)

The web storybook screenshot fixes have been extracted to PR #6870.
This PR now only contains the new mobile storybook screenshots feature.

* chore(mobile): Fix theme type errors and trigger mobile screenshots workflow

- Add optional chaining to theme property access throughout mobile app
- Fixes TypeScript errors where theme properties were possibly undefined
- Add comment to SafeButton story to trigger mobile screenshots workflow

* fix(mobile): Disable Storybook telemetry to fix monorepo build

Add core.disableTelemetry config to prevent telemetry module from
attempting to resolve framework packages in monorepo structure,
which was causing the build to fail.

* fix(mobile): Use standard framework config instead of path resolution

Replace path resolution workaround with standard framework configuration
to fix 'Cannot find module' error in CI environment.

* fix(ci): Access Storybook iframe for mobile screenshots

Storybook renders stories inside an iframe, so we need to:
1. Wait for the iframe to load
2. Access the iframe's content frame
3. Look for #storybook-root inside the iframe

This fixes the 'element is hidden' error in screenshot capture.

* fix(ci): Add comprehensive mocks for mobile Storybook web build

Fix mobile Storybook rendering by mocking React Native modules that
don't work in web browsers.

Changes:
- Mock react-native-worklets with version 0.5.0 and all required functions
- Create standalone mock for react-native-reanimated (hooks, animations, utilities)
- Mock react-refresh to prevent production bundle errors
- Mock react-native-quick-crypto using Node.js crypto
- Update capture script to wait for #storybook-root to be visible
- Use webpack aliases to replace native modules with mocks

The mobile Storybook now successfully renders components in a web browser
and screenshots can be captured with Playwright.

* test: Trigger mobile screenshots workflow

Trigger the mobile screenshots workflow by modifying a story file.

* fix(ci): Set STORYBOOK_WEB=true for mobile Storybook build

The build needs STORYBOOK_WEB=true to use the web configuration with mocks.
Without this, it tries to build the native Storybook which doesn't have
the webpack aliases for React Native module mocks.

* fix(ci): Handle direct iframe.html navigation in screenshot capture

The script was looking for iframe#storybook-preview-iframe, but story URLs
navigate directly to iframe.html which renders content without the wrapper.

Changes:
- Detect if URL contains 'iframe.html' and use page directly
- Only look for iframe wrapper when on main Storybook page
- Simplify content selection to work with direct iframe rendering

Tested locally - all 5 screenshots now capture successfully.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2025-12-22 18:02:31 +01:00

34 lines
972 B
JavaScript

/* eslint-disable */
// Mock react-refresh for Storybook to prevent production bundle errors
// Provides no-op implementations of all required functions
const noop = () => {};
module.exports = {
injectIntoGlobalHook: noop,
createSignatureFunctionForTransform: noop,
isLikelyComponentType: () => false,
getFamilyByType: () => null,
getFamilyByID: () => null,
findAffectedHostInstances: noop,
collectCustomHooksForSignature: noop,
hasUnrecoverableErrors: () => false,
setSignature: noop,
performReactRefresh: noop,
register: noop,
__esModule: true,
default: {
injectIntoGlobalHook: noop,
createSignatureFunctionForTransform: noop,
isLikelyComponentType: () => false,
getFamilyByType: () => null,
getFamilyByID: () => null,
findAffectedHostInstances: noop,
collectCustomHooksForSignature: noop,
hasUnrecoverableErrors: () => false,
setSignature: noop,
performReactRefresh: noop,
register: noop,
},
};