docs: correct capture examples and RAW conversion semantics #413
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, master] | |
| pull_request: | |
| branches: [main, master] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| # Harden npm installs against transient registry network blips (e.g. | |
| # ECONNRESET) seen intermittently on hosted runners. | |
| NPM_CONFIG_FETCH_RETRIES: 5 | |
| NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: 20000 | |
| NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: 120000 | |
| jobs: | |
| lint-and-type-check: | |
| name: Code Quality (Strict) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps # Need --legacy-peer-deps for react-native-windows@0.76.17 | |
| - name: Check formatting (no prettier diff allowed) | |
| run: npm run format:check | |
| - name: Run TypeScript type check (strict) | |
| run: npm run type-check | |
| - name: Run linter (strict - no warnings allowed) | |
| run: npm run lint:ci | |
| - name: Build project | |
| run: npm run build | |
| - name: Run unit tests | |
| run: npm test | |
| build-android: | |
| name: Build Android APK | |
| runs-on: ubuntu-latest | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| # The Android assembleDebug build (RN new arch) exhausts the runner's | |
| # ~14 GB of free disk. Reclaim space by removing large preinstalled | |
| # toolchains we don't use, while keeping the Android SDK/NDK. Runs | |
| # before Node/Java setup so their tool caches are untouched. | |
| - name: Free up disk space | |
| run: | | |
| echo "Disk before cleanup:"; df -h / | |
| sudo rm -rf /usr/share/dotnet \ | |
| /opt/ghc /usr/local/.ghcup \ | |
| /opt/hostedtoolcache/CodeQL \ | |
| /usr/local/share/boost \ | |
| /usr/local/share/powershell \ | |
| /usr/local/lib/node_modules || true | |
| sudo docker image prune --all --force || true | |
| sudo swapoff -a || true | |
| sudo rm -f /mnt/swapfile || true | |
| sudo apt-get clean || true | |
| echo "Disk after cleanup:"; df -h / | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Setup Java JDK | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '17' | |
| - name: Cache Gradle | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle- | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps # Need --legacy-peer-deps for react-native-windows@0.76.17 | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example dependencies | |
| working-directory: example | |
| run: npm ci --legacy-peer-deps | |
| - name: Run library Java unit tests | |
| working-directory: example/android | |
| run: | | |
| echo "🧪 Running library JUnit tests..." | |
| ./gradlew :react-native-view-shot:testDebugUnitTest --no-daemon --stacktrace | |
| - name: Build Android APK | |
| working-directory: example/android | |
| run: | | |
| echo "🔨 Building Android APK..." | |
| ./gradlew assembleDebug --no-daemon --stacktrace -PnewArchEnabled=true | |
| - name: Upload Android APK | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-apk | |
| path: example/android/app/build/outputs/apk/debug/app-debug.apk | |
| lint-podspec: | |
| name: Lint CocoaPods Podspec | |
| runs-on: macos-15 | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup CocoaPods | |
| uses: maxim-lobanov/setup-cocoapods@v1 | |
| with: | |
| version: latest | |
| - name: Lint podspec | |
| run: pod lib lint --allow-warnings --quick | |
| build-ios: | |
| name: Build iOS App | |
| runs-on: macos-15 # Use latest macOS with Xcode 16.1+ | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Cache CocoaPods | |
| uses: actions/cache@v4 | |
| with: | |
| path: example/ios/Pods | |
| key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pods- | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps # Need --legacy-peer-deps for react-native-windows@0.76.17 | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example dependencies | |
| working-directory: example | |
| run: npm ci --legacy-peer-deps | |
| - name: Setup CocoaPods | |
| uses: maxim-lobanov/setup-cocoapods@v1 | |
| with: | |
| version: latest | |
| - name: Pod install | |
| working-directory: example/ios | |
| run: pod install --verbose | |
| - name: Build iOS | |
| working-directory: example | |
| run: | | |
| xcodebuild -workspace ios/ViewShotExample.xcworkspace \ | |
| -scheme ViewShotExample \ | |
| -configuration Debug \ | |
| -destination 'generic/platform=iOS Simulator' \ | |
| build CODE_SIGNING_ALLOWED=NO | |
| - name: Run iOS unit tests | |
| working-directory: example | |
| run: | | |
| # Pick the first available iPhone simulator and pass its UDID | |
| # (not its name) to xcodebuild. We use UDID because simulator | |
| # display names can contain parentheses (e.g. "iPhone SE | |
| # (3rd generation)"), and parsing them with a name-based regex | |
| # is brittle: stripping the trailing "(...)" loses version | |
| # disambiguation, and quoting parentheses on the | |
| # `-destination name=...` form is fragile across shells. | |
| # UDIDs are opaque and stable, so `id=$UDID` always resolves | |
| # exactly one device regardless of name shape. We also avoid | |
| # hard-coding e.g. "iPhone 16" which may disappear when Xcode | |
| # updates on macos-15. | |
| UDID=$(xcrun simctl list devices available -j | \ | |
| jq -r '.devices | to_entries[] | |
| | select(.key | startswith("com.apple.CoreSimulator.SimRuntime.iOS")) | |
| | .value[] | |
| | select(.name | startswith("iPhone ")) | |
| | .udid' | head -1) | |
| if [ -z "$UDID" ]; then | |
| echo "::error::No iPhone simulator available on the runner" | |
| xcrun simctl list devices available | |
| exit 1 | |
| fi | |
| echo "Using simulator UDID: $UDID" | |
| xcrun simctl list devices available | grep "$UDID" || true | |
| xcodebuild test \ | |
| -workspace ios/ViewShotExample.xcworkspace \ | |
| -scheme ViewShotExampleTests \ | |
| -destination "platform=iOS Simulator,id=$UDID" \ | |
| CODE_SIGNING_ALLOWED=NO | |
| - name: Upload iOS Build Logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ios-build-logs | |
| path: ~/Library/Logs/DiagnosticReports/ | |
| build-ios-new-arch: | |
| name: Build iOS App (New Architecture) | |
| runs-on: macos-15 | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Cache CocoaPods (new arch) | |
| uses: actions/cache@v4 | |
| with: | |
| path: example/ios/Pods | |
| key: ${{ runner.os }}-pods-newarch-${{ hashFiles('**/Podfile.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pods-newarch- | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example dependencies | |
| working-directory: example | |
| run: npm ci --legacy-peer-deps | |
| - name: Setup CocoaPods | |
| uses: maxim-lobanov/setup-cocoapods@v1 | |
| with: | |
| version: latest | |
| - name: Pod install (new arch) | |
| working-directory: example/ios | |
| env: | |
| RCT_NEW_ARCH_ENABLED: '1' | |
| run: pod install --verbose | |
| - name: Build iOS (new arch) | |
| working-directory: example | |
| run: | | |
| xcodebuild -workspace ios/ViewShotExample.xcworkspace \ | |
| -scheme ViewShotExample \ | |
| -configuration Debug \ | |
| -destination 'generic/platform=iOS Simulator' \ | |
| build CODE_SIGNING_ALLOWED=NO | |
| - name: Upload iOS Build Logs (new arch) | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ios-build-logs-new-arch | |
| path: ~/Library/Logs/DiagnosticReports/ | |
| test-windows-unit: | |
| name: Run Windows unit tests | |
| runs-on: ubuntu-latest | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '8.0.x' | |
| - name: Cache NuGet packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-nuget-${{ hashFiles('windows/RNViewShot.Tests/RNViewShot.Tests.csproj') }} | |
| restore-keys: ${{ runner.os }}-nuget- | |
| - name: Run dotnet tests | |
| run: | | |
| dotnet test windows/RNViewShot.Tests/RNViewShot.Tests.csproj \ | |
| --nologo --verbosity normal \ | |
| --logger "trx;LogFileName=test-results.trx" \ | |
| --results-directory TestResults | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-unit-test-results | |
| path: TestResults/ | |
| build-windows: | |
| name: Build Windows | |
| # Pinned to windows-2022 (VS 2022). GitHub migrated windows-latest to | |
| # Visual Studio 2026 (18.x) in June 2026, which react-native-windows 0.76 | |
| # does not support — its CLI only accepts VS in range [17.11.0, 18.0). | |
| runs-on: windows-2022 | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: | | |
| package-lock.json | |
| example-windows/package-lock.json | |
| - name: Setup MSBuild | |
| uses: microsoft/setup-msbuild@v2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '8.0.x' | |
| - name: Ensure Windows SDK 10.0.22621 | |
| shell: pwsh | |
| run: | | |
| $sdkPath = "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0" | |
| if (Test-Path $sdkPath) { | |
| Write-Host "Windows SDK 10.0.22621 already installed" | |
| exit 0 | |
| } | |
| Write-Host "Installed SDKs:" | |
| Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\Include" -ErrorAction SilentlyContinue | Select-Object Name | ForEach-Object { Write-Host " $($_.Name)" } | |
| $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" | |
| $info = & $vswhere -latest -prerelease -products * -format json | ConvertFrom-Json | |
| if (-not $info) { throw "vswhere returned no VS install" } | |
| Write-Host "VS productId: $($info.productId)" | |
| $vsInstaller = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vs_installer.exe" | |
| $args = @( | |
| 'modify', | |
| '--channelId', $info.channelId, | |
| '--productId', $info.productId, | |
| '--add', 'Microsoft.VisualStudio.Component.Windows11SDK.22621', | |
| '--quiet', '--norestart' | |
| ) | |
| Write-Host "Running: $vsInstaller $($args -join ' ')" | |
| $proc = Start-Process -FilePath $vsInstaller -ArgumentList $args -Wait -PassThru | |
| Write-Host "vs_installer exit code: $($proc.ExitCode)" | |
| if (-not (Test-Path $sdkPath)) { | |
| throw "Windows SDK 10.0.22621 still missing after install attempt" | |
| } | |
| Write-Host "Windows SDK 10.0.22621 installed" | |
| - name: Cache NuGet packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-nuget-build-${{ hashFiles('**/packages.lock.json') }} | |
| restore-keys: ${{ runner.os }}-nuget-build- | |
| - name: Install root dependencies | |
| run: npm install --legacy-peer-deps | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example-windows dependencies | |
| working-directory: example-windows | |
| run: npm ci --legacy-peer-deps | |
| - name: Run Windows autolinking | |
| working-directory: example-windows | |
| run: npx --no-install react-native autolink-windows --no-telemetry | |
| - name: Build example-windows | |
| working-directory: example-windows | |
| shell: pwsh | |
| run: | | |
| npx --no-install react-native run-windows ` | |
| --arch x64 ` | |
| --no-launch ` | |
| --no-deploy ` | |
| --no-packager ` | |
| --no-autolink ` | |
| --logging ` | |
| --buildLogDirectory buildlogs ` | |
| --no-telemetry | |
| - name: Verify build artifacts | |
| working-directory: example-windows | |
| shell: pwsh | |
| run: | | |
| $exe = "windows/ViewShotWindowsExample/bin/x64/Debug/ViewShotWindowsExample.exe" | |
| $rnviewshot = "windows/ViewShotWindowsExample/bin/x64/Debug/RNViewShot.winmd" | |
| if (-not (Test-Path $exe)) { | |
| Write-Error "Missing $exe"; exit 1 | |
| } | |
| if (-not (Test-Path $rnviewshot)) { | |
| Write-Error "Missing $rnviewshot — RNViewShot module did not link"; exit 1 | |
| } | |
| Write-Host "OK: $exe and RNViewShot.winmd present" | |
| - name: Upload Windows app package | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-app | |
| path: example-windows/windows/ViewShotWindowsExample/AppPackages/** | |
| if-no-files-found: error | |
| - name: Upload Windows build logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-build-logs | |
| path: | | |
| example-windows/buildlogs/** | |
| example-windows/windows/**/*.log | |
| example-windows/windows/**/*.err | |
| # Temporarily disabled - Android Detox setup needs native library conflict resolution | |
| # The Android build works fine, but Detox test APK has libfbjni.so conflicts | |
| # Using functional E2E tests instead (test-all-features.js) | |
| # test-e2e-android: | |
| # name: E2E Tests (Android) | |
| # runs-on: ubuntu-latest | |
| # needs: build-android | |
| # | |
| # steps: | |
| # - name: Checkout code | |
| # uses: actions/checkout@v4 | |
| # | |
| # - name: Setup Node.js | |
| # uses: actions/setup-node@v4 | |
| # with: | |
| # node-version: '20' | |
| # cache: 'npm' | |
| # | |
| # - name: Setup Java JDK | |
| # uses: actions/setup-java@v4 | |
| # with: | |
| # distribution: 'temurin' | |
| # java-version: '17' | |
| # | |
| # - name: Enable KVM group perms | |
| # run: | | |
| # echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| # sudo udevadm control --reload-rules | |
| # sudo udevadm trigger --name-match=kvm | |
| # | |
| # - name: Cache Gradle | |
| # uses: actions/cache@v4 | |
| # with: | |
| # path: | | |
| # ~/.gradle/caches | |
| # ~/.gradle/wrapper | |
| # key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
| # restore-keys: | | |
| # ${{ runner.os }}-gradle- | |
| # | |
| # - name: Cache AVD | |
| # uses: actions/cache@v4 | |
| # id: avd-cache | |
| # with: | |
| # path: | | |
| # ~/.android/avd/* | |
| # ~/.android/adb* | |
| # key: avd-34 | |
| # | |
| # - name: Create AVD and generate snapshot for caching | |
| # if: steps.avd-cache.outputs.cache-hit != 'true' | |
| # uses: reactivecircus/android-emulator-runner@v2 | |
| # with: | |
| # api-level: 34 | |
| # target: google_apis | |
| # arch: x86_64 | |
| # profile: Nexus 6 | |
| # force-avd-creation: false | |
| # emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| # disable-animations: false | |
| # script: echo "Generated AVD snapshot for caching." | |
| # | |
| # - name: Install dependencies | |
| # run: npm install --legacy-peer-deps | |
| # | |
| # - name: Build library | |
| # run: npm run build | |
| # | |
| # - name: Install example dependencies | |
| # working-directory: example | |
| # run: npm ci --legacy-peer-deps | |
| # | |
| # - name: Build Android APK for E2E | |
| # working-directory: example/android | |
| # run: ./gradlew assembleDebug | |
| # | |
| # - name: Run Functional E2E Tests (Android) | |
| # uses: reactivecircus/android-emulator-runner@v2 | |
| # with: | |
| # api-level: 34 | |
| # target: google_apis | |
| # arch: x86_64 | |
| # profile: Nexus 6 | |
| # force-avd-creation: false | |
| # emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| # disable-animations: true | |
| # script: | | |
| # echo "🚀 Starting Functional E2E Tests for Android" | |
| # cd example | |
| # | |
| # # Start Metro bundler in background | |
| # npm start & | |
| # METRO_PID=$! | |
| # | |
| # # Wait for Metro to start | |
| # sleep 30 | |
| # | |
| # # Run our comprehensive feature tests | |
| # echo "📱 Running comprehensive ViewShot feature tests..." | |
| # node test-all-features.js | |
| # | |
| # # Kill Metro | |
| # kill $METRO_PID || true | |
| # | |
| # - name: Upload E2E Test Results | |
| # if: always() | |
| # uses: actions/upload-artifact@v4 | |
| # with: | |
| # name: e2e-test-results-android | |
| # path: | | |
| # example/test-*.png | |
| # example/test-report.json | |
| test-detox-ios: | |
| name: Detox E2E Tests (iOS) | |
| runs-on: macos-15 | |
| needs: build-ios | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Cache CocoaPods | |
| uses: actions/cache@v4 | |
| with: | |
| path: example/ios/Pods | |
| key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pods- | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps | |
| - name: Install applesimutils for iOS Detox | |
| run: | | |
| # Try to install via brew tap first. Homebrew's tap-trust gate | |
| # refuses to load formulae from community taps by default, so | |
| # the tap must be explicitly trusted or `brew install` fails. | |
| if brew tap wix/brew && brew trust wix/brew && brew install applesimutils 2>/dev/null; then | |
| echo "✅ Installed applesimutils via brew tap" | |
| else | |
| echo "⚠️ Brew install failed, trying manual compilation..." | |
| # Download source and compile | |
| curl -L https://github.com/wix/AppleSimulatorUtils/archive/0.9.6.tar.gz | tar -xz | |
| cd AppleSimulatorUtils-0.9.6 | |
| xcodebuild -project applesimutils.xcodeproj -scheme applesimutils -configuration Release -derivedDataPath ./build clean build | |
| sudo cp ./build/Build/Products/Release/applesimutils /usr/local/bin/ | |
| echo "✅ Installed applesimutils via compilation" | |
| fi | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example dependencies | |
| working-directory: example | |
| run: npm ci --legacy-peer-deps | |
| - name: Setup CocoaPods | |
| uses: maxim-lobanov/setup-cocoapods@v1 | |
| with: | |
| version: latest | |
| - name: Pod install | |
| working-directory: example/ios | |
| run: pod install --verbose | |
| - name: Build iOS for Detox | |
| working-directory: example | |
| run: npm run build:e2e:ios | |
| - name: Start Metro Bundler | |
| working-directory: example | |
| run: | | |
| npm start & | |
| echo $! > metro.pid | |
| sleep 30 | |
| - name: Run Detox Tests | |
| working-directory: example | |
| run: npm run test:e2e:ios | |
| continue-on-error: true | |
| id: detox_ios_tests | |
| - name: Stop Metro Bundler | |
| if: always() | |
| working-directory: example | |
| run: | | |
| if [ -f metro.pid ]; then | |
| kill $(cat metro.pid) || true | |
| rm metro.pid | |
| fi | |
| - name: Upload Detox Screenshots | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: detox-screenshots-ios | |
| path: example/artifacts/**/*.png | |
| - name: Upload Detox Test Results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: detox-test-results-ios | |
| path: | | |
| example/e2e/test-results/**/* | |
| example/artifacts/**/*.xml | |
| - name: Report iOS test results | |
| if: always() | |
| # Non-blocking: Jest SummaryReporter crashes (isSet TypeError) and the | |
| # snapshot-content-container / style-filters suites are known-failing. | |
| run: | | |
| if [ "${{ steps.detox_ios_tests.outcome }}" == "failure" ]; then | |
| echo "⚠️ Detox exited with error (may be Jest reporter crash, check artifacts for actual results)" | |
| else | |
| echo "✅ Detox iOS tests completed" | |
| fi | |
| # Android Detox is disabled on CI: native library conflicts not yet | |
| # reproduced outside the runner. Track via the PR description. | |
| # test-detox-android: | |
| # name: Detox E2E Tests (Android) | |
| # runs-on: ubuntu-latest | |
| # needs: build-android | |
| # timeout-minutes: 60 | |
| build-web-example: | |
| name: Build Web Example | |
| runs-on: ubuntu-latest | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install root dependencies | |
| run: npm install --legacy-peer-deps | |
| - name: Build library | |
| run: npm run build | |
| - name: Install web example dependencies | |
| working-directory: example-web | |
| run: npm ci | |
| - name: Build web example (production) | |
| working-directory: example-web | |
| run: npm run build | |
| - name: Verify build output | |
| working-directory: example-web | |
| run: | | |
| echo "✅ Web build completed successfully" | |
| ls -lh dist/ | |
| du -sh dist/ | |
| echo "" | |
| echo "Build contains:" | |
| ls -1 dist/ | |
| - name: Upload web build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: web-build | |
| path: example-web/dist/ | |
| retention-days: 7 | |
| - name: Build summary | |
| working-directory: example-web | |
| run: | | |
| echo "### Web Build Summary 🌐" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ Production build successful" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Bundle size:** $(du -sh dist/ | cut -f1)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Files:**" >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| ls -lh dist/ >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| test-web-example: | |
| name: Test Web Example (Playwright) | |
| runs-on: ubuntu-latest | |
| needs: build-web-example | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install root dependencies | |
| run: npm install --legacy-peer-deps | |
| - name: Build library | |
| run: npm run build | |
| - name: Install web example dependencies | |
| working-directory: example-web | |
| run: npm ci | |
| - name: Install Playwright Browsers | |
| working-directory: example-web | |
| run: npx playwright install chromium --with-deps | |
| - name: Start web development server | |
| working-directory: example-web | |
| run: | | |
| npx webpack serve --mode development --port 3000 & | |
| echo $! > web-server.pid | |
| sleep 30 | |
| # Wait for server to be ready | |
| for i in {1..30}; do | |
| if curl -s http://localhost:3000 > /dev/null; then | |
| echo "✅ Web server is ready" | |
| break | |
| fi | |
| echo "⏳ Waiting for web server... ($i/30)" | |
| sleep 2 | |
| done | |
| - name: Run Playwright tests | |
| working-directory: example-web | |
| run: npm run test:e2e | |
| id: playwright_tests | |
| - name: Stop web development server | |
| if: always() | |
| working-directory: example-web | |
| run: | | |
| if [ -f web-server.pid ]; then | |
| kill $(cat web-server.pid) || true | |
| rm web-server.pid | |
| fi | |
| - name: Check for snapshot differences | |
| if: failure() && steps.playwright_tests.outcome == 'failure' | |
| working-directory: example-web | |
| run: | | |
| if [ -d "test-results" ]; then | |
| echo "## 📸 Visual Regression Detected" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Some visual snapshots have changed. This could be due to:" >> $GITHUB_STEP_SUMMARY | |
| echo "- UI changes in the web example" >> $GITHUB_STEP_SUMMARY | |
| echo "- Different rendering between platforms (Linux CI vs local macOS)" >> $GITHUB_STEP_SUMMARY | |
| echo "- Font or styling differences" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**To update snapshots:**" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Download the \`web-snapshots-actual\` artifact (uploaded below)" >> $GITHUB_STEP_SUMMARY | |
| echo "2. Take the \`*-actual.png\` files and copy them over the matching baselines in \`example-web/e2e/snapshots/reference/viewshot.spec.ts-snapshots/\` (drop the \`-actual\` suffix)" >> $GITHUB_STEP_SUMMARY | |
| echo "3. Commit the updated snapshots" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Diff files available in artifacts:**" >> $GITHUB_STEP_SUMMARY | |
| find test-results -name "*-diff.png" | head -5 | while read file; do | |
| echo "- \`$(basename "$file")\`" >> $GITHUB_STEP_SUMMARY | |
| done | |
| fi | |
| - name: Upload generated snapshots (actual from test-results) | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: web-snapshots-actual | |
| path: | | |
| example-web/test-results/**/*-actual.png | |
| example-web/test-results/**/*-expected.png | |
| example-web/test-results/**/*-diff.png | |
| retention-days: 30 | |
| if-no-files-found: ignore | |
| - name: Upload Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report | |
| path: example-web/playwright-report/ | |
| retention-days: 7 | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-test-results | |
| path: example-web/test-results/ | |
| retention-days: 7 | |
| - name: Fail if tests failed | |
| if: steps.playwright_tests.outcome == 'failure' | |
| run: exit 1 | |