diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4989c12 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,131 @@ +name: React Native CI + +on: + pull_request: + branches: main + push: + branches: main + schedule: + - cron: '0 0 * * *' # Runs at 00:00 UTC every day + +jobs: + ios-build: + name: iOS Build + runs-on: macos-latest + defaults: + run: + working-directory: example + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: example/package-lock.json + + - name: Cache CocoaPods + uses: actions/cache@v4 + with: + path: | + example/ios/Pods + key: ${{ runner.os }}-pods-${{ hashFiles('example/ios/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- + + - name: Install dependencies + run: | + npm install --frozen-lockfile + cd ios && pod install + + - name: Install Maestro CLI + run: | + curl -Ls "https://get.maestro.mobile.dev" | bash + brew tap facebook/fb + brew install facebook/fb/idb-companion + + - name: Add Maestro to path + run: echo "${HOME}/.maestro/bin" >> $GITHUB_PATH + + - name: Start packager + run: npm start & + + - name: Build iOS + run: | + npm run ios + + - name: Setup iOS simulator + run: | + UDID=$(xcrun simctl list devices | grep "iPhone" | grep "Booted" | head -1 | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})") + if [ -z "$UDID" ]; then + UDID=$(xcrun simctl list devices available | grep "iPhone" | head -1 | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})") + xcrun simctl boot "${UDID}" + fi + open -a Simulator + xcrun simctl launch "${UDID}" com.jscexample + + - name: Run iOS tests + run: | + export MAESTRO_DRIVER_STARTUP_TIMEOUT=1500000 + export MAESTRO_WAIT_TIMEOUT=10000 + npm run test:e2e + + + android-build: + name: Android Build + runs-on: ubuntu-latest + defaults: + run: + working-directory: example + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: example/package-lock.json + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: '17' + + - name: Install dependencies + run: npm install --frozen-lockfile + + - name: Start packager + run: npm start & + + - name: Install Maestro CLI + run: | + curl -Ls "https://get.maestro.mobile.dev" | bash + + - name: Add Maestro to path + run: echo "${HOME}/.maestro/bin" >> $GITHUB_PATH + + - name: Create AVD and generate snapshot for caching + uses: reactivecircus/android-emulator-runner@v2 + with: + target: aosp_atd + api-level: 30 + arch: x86 + ram-size: 4096M + channel: canary + profile: pixel + avd-name: Pixel_3a_API_30_AOSP + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + emulator-boot-timeout: 12000 + disable-animations: false + working-directory: example + script: | + npm run android + npm run test:e2e diff --git a/example/App.tsx b/example/App.tsx index 125fe1b..34b66f4 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -1,13 +1,6 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - * - * @format - */ - -import React from 'react'; -import type {PropsWithChildren} from 'react'; +import React, { useState } from 'react'; import { + Button, SafeAreaView, ScrollView, StatusBar, @@ -16,52 +9,127 @@ import { useColorScheme, View, } from 'react-native'; - import { Colors, - DebugInstructions, Header, - LearnMoreLinks, - ReloadInstructions, } from 'react-native/Libraries/NewAppScreen'; -type SectionProps = PropsWithChildren<{ - title: string; -}>; - -function Section({children, title}: SectionProps): React.JSX.Element { - const isDarkMode = useColorScheme() === 'dark'; - return ( - - - {title} - - - {children} - - - ); -} - function App(): React.JSX.Element { const isDarkMode = useColorScheme() === 'dark'; + const [testResult, setTestResult] = useState('No test run yet'); + const [testName, setTestName] = useState(''); const backgroundStyle = { backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, }; + const testConsoleLog = () => { + console.log('Hello from JSC'); + setTestName('Console Test Result'); + setTestResult('Hello from JSC'); + }; + + const testBasicOperations = () => { + const mathResult = 2 + 2; + const stringResult = 'Hello ' + 'World'; + const arrayResult = [1, 2, 3].map(x => x * 2); + + const result = `Math: ${mathResult}\nString: ${stringResult}\nArray: ${arrayResult}`; + console.log(result); + setTestName('Basic Operations Result'); + setTestResult(result); + }; + + const testComplexOperations = () => { + const obj = { a: 1, b: 2 }; + const square = (x: number) => x * x; + const squareResult = square(4); + + let result = `Object: ${JSON.stringify(obj)}\nSquare(4): ${squareResult}`; + + try { + // eslint-disable-next-line no-eval + const dynamicFn = eval('(x) => x * 3'); + const dynamicResult = dynamicFn(4); + result += `\nDynamic function(4): ${dynamicResult}`; + } catch (error) { + result += `\nDynamic function error: ${error}`; + } + + console.log(result); + setTestName('Complex Operations Result'); + setTestResult(result); + }; + + const testGlobalAccess = () => { + const result = `SetTimeout exists: ${typeof global.setTimeout === 'function'}`; + console.log(result); + setTestName('Global Access Result'); + setTestResult(result); + }; + + const testErrorHandling = () => { + let results: string[] = []; + + try { + throw new Error('Custom error'); + } catch (error) { + if (error instanceof Error) { + results.push(`Regular error: ${error.message}`); + } + } + + try { + const undefined1 = undefined; + // @ts-ignore + undefined1.someMethod(); + } catch (error) { + if (error instanceof Error) { + results.push(`Type error: ${error.message}`); + } + } + + try { + // eslint-disable-next-line no-eval + eval('syntax error{'); + } catch (error) { + if (error instanceof Error) { + results.push(`Eval error: ${error.message}`); + } + } + + const result = results.join('\n'); + console.log(result); + setTestName('Error Handling Result'); + setTestResult(result); + }; + + const testAsync = async () => { + try { + const result = await new Promise((resolve) => { + setTimeout(() => resolve('Regular async completed'), 1000); + }); + console.log('Regular async result:', result); + setTestName('Async Test Result'); + setTestResult(String(result)); + } catch (error) { + setTestName('Async Error'); + setTestResult(String(error)); + } + }; + + const testMemoryAndPerformance = () => { + const arr = new Array(1000000); + for (let i = 0; i < arr.length; i++) { + arr[i] = i; + } + const result = `Array length: ${arr.length}`; + + console.log(result); + setTestName('Memory & Performance Result'); + setTestResult(result); + }; + return (
-
- Edit App.tsx to change this - screen and then come back to see your edits. -
-
- -
-
- -
-
- Read the docs to discover what to do next: -
- + style={[ + styles.container, + {backgroundColor: isDarkMode ? Colors.black : Colors.white}, + ]}> +