Skip to content

Stack update (latest RN Fabric support, TypeScript migration, example rebooted, CI tested) #45

Stack update (latest RN Fabric support, TypeScript migration, example rebooted, CI tested)

Stack update (latest RN Fabric support, TypeScript migration, example rebooted, CI tested) #45

Workflow file for this run

name: CI
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
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.74.47
- 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
- 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.74.47
- name: Build library
run: npm run build
- name: Install example dependencies
working-directory: example
run: npm ci --legacy-peer-deps
- name: Build Android APK
working-directory: example/android
run: ./gradlew assembleDebug --no-daemon
- name: Upload Android APK
uses: actions/upload-artifact@v4
with:
name: android-apk
path: example/android/app/build/outputs/apk/debug/app-debug.apk
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.74.47
- 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 'platform=iOS Simulator,name=iPhone 16,OS=18.4' \
build 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-windows:
name: Build Windows
runs-on: windows-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 dependencies
run: npm install --legacy-peer-deps # Need --legacy-peer-deps for react-native-windows@0.74.47 vs @types/react@19
- name: Build library
run: npm run build
- name: Setup MSBuild with UWP support
uses: microsoft/setup-msbuild@v2
with:
msbuild-architecture: x64
- name: Setup NuGet
uses: nuget/setup-nuget@v2
with:
nuget-version: 'latest'
- name: Verify Windows 10 SDK
run: |
Write-Host "🔧 Checking Windows 10 SDK and UWP tools..."
# Check Windows 10 SDK
$sdkPath = "C:\Program Files (x86)\Windows Kits\10"
if (Test-Path $sdkPath) {
Write-Host "✅ Windows 10 SDK found"
$versions = dir "$sdkPath\bin" -Directory -Name | Sort-Object -Descending
Write-Host "Available SDK versions: $($versions -join ', ')"
}
# Check MSBuild XAML targets
$xamlTargets = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\WindowsXaml"
if (Test-Path $xamlTargets) {
Write-Host "✅ XAML targets found at $xamlTargets"
dir $xamlTargets -Recurse -Name "*.targets" | Select-Object -First 5
} else {
Write-Host "❌ XAML targets not found, checking alternative locations..."
# Check other common locations
$altPath = "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Microsoft\WindowsXaml"
if (Test-Path $altPath) {
Write-Host "✅ Found XAML targets at $altPath"
}
}
- name: Verify react-native-windows installation
run: |
Write-Host "🔍 Checking react-native-windows structure..."
if (Test-Path "node_modules\react-native-windows") {
Write-Host "✅ react-native-windows found"
dir "node_modules\react-native-windows\" -Name | Select-Object -First 20
if (Test-Path "node_modules\react-native-windows\PropertySheets") {
Write-Host "✅ PropertySheets found"
dir "node_modules\react-native-windows\PropertySheets\" -Name
if (Test-Path "node_modules\react-native-windows\PropertySheets\External") {
Write-Host "✅ PropertySheets\External found"
dir "node_modules\react-native-windows\PropertySheets\External\" -Name
} else {
Write-Host "❌ PropertySheets\External missing - checking alternative locations..."
# Check if PropertySheets structure changed in newer versions
if (Test-Path "node_modules\react-native-windows\Microsoft.ReactNative.Uwp.CSharpLib.props") {
Write-Host "✅ Found props file in root directory"
} else {
Write-Host "❌ Props file not found in expected locations"
Write-Host "Available files in react-native-windows:"
dir "node_modules\react-native-windows\" -Recurse -Name "*.props" | Select-Object -First 10
}
}
} else {
Write-Host "❌ PropertySheets missing - checking for alternative structure..."
Write-Host "Available directories in react-native-windows:"
dir "node_modules\react-native-windows\" -Directory -Name | Select-Object -First 10
}
} else {
Write-Host "❌ react-native-windows not found"
exit 1
}
- name: Build Windows project (C# project only)
working-directory: windows/RNViewShot
run: |
Write-Host "🔨 Building Windows C# project only..."
Write-Host "Using MSBuild for UWP project..."
# Set explicit paths for MSBuild extensions
$env:MSBuildExtensionsPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild"
$env:VisualStudioVersion = "17.0"
# Try to restore packages first
nuget restore RNViewShot.csproj
# Build with explicit parameters
msbuild RNViewShot.csproj `
/p:Configuration=Debug `
/p:Platform="AnyCPU" `
/p:ReactNativeWindowsDir="../../node_modules/react-native-windows/" `
/p:MSBuildExtensionsPath="C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild" `
/p:VisualStudioVersion="17.0" `
/verbosity:normal
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-e2e-ios:
name: 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: 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: List available iOS simulators
run: |
echo "📱 Available iOS simulators:"
xcrun simctl list devices available
- name: Build iOS for E2E
working-directory: example
run: |
echo "🍎 Building iOS app for E2E testing..."
xcodebuild -workspace ios/ViewShotExample.xcworkspace \
-scheme ViewShotExample \
-configuration Debug \
-sdk iphonesimulator \
-derivedDataPath ios/build \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest' \
build
- name: Run Functional E2E Tests (iOS)
working-directory: example
run: |
echo "🚀 Starting Functional E2E Tests for iOS"
# Start Metro bundler in background
npm start &
METRO_PID=$!
# Wait for Metro to start
sleep 30
# Start iOS Simulator
echo "📱 Starting iOS Simulator..."
xcrun simctl boot "iPhone 16 Pro" || true
sleep 10
# Install the app
echo "📦 Installing iOS app..."
xcrun simctl install "iPhone 16 Pro" ios/build/Build/Products/Debug-iphonesimulator/ViewShotExample.app
# Launch the app
echo "🚀 Launching iOS app..."
xcrun simctl launch "iPhone 16 Pro" com.viewshotexample
# Wait for app to start
sleep 10
# Create iOS-compatible test script
echo "📱 Running iOS-compatible feature tests..."
cat > test-ios-features.js << 'EOF'
#!/usr/bin/env node
const { execSync } = require('child_process');
console.log('🍎 iOS ViewShot Feature Test');
console.log('============================');
try {
// Take screenshots using iOS simulator
console.log('📸 Taking iOS simulator screenshots...');
// Screenshot 1: Home screen
execSync('xcrun simctl io "iPhone 16 Pro" screenshot test-ios-home.png', { stdio: 'pipe' });
console.log('✅ Home screen captured');
// Simulate tap on Basic ViewShot (approximate coordinates)
execSync('xcrun simctl io "iPhone 16 Pro" tap 200 400', { stdio: 'pipe' });
await new Promise(resolve => setTimeout(resolve, 2000));
// Screenshot 2: Basic test screen
execSync('xcrun simctl io "iPhone 16 Pro" screenshot test-ios-basic.png', { stdio: 'pipe' });
console.log('✅ Basic test screen captured');
// Simulate tap on capture button
execSync('xcrun simctl io "iPhone 16 Pro" tap 200 500', { stdio: 'pipe' });
await new Promise(resolve => setTimeout(resolve, 3000));
// Screenshot 3: After capture
execSync('xcrun simctl io "iPhone 16 Pro" screenshot test-ios-after-capture.png', { stdio: 'pipe' });
console.log('✅ After capture screenshot taken');
console.log('🎉 iOS E2E test completed successfully!');
} catch (error) {
console.error('❌ iOS E2E test failed:', error.message);
process.exit(1);
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
EOF
node test-ios-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-ios
path: |
example/test-ios-*.png