diff --git a/.changeset/lazy-pugs-reply.md b/.changeset/lazy-pugs-reply.md new file mode 100644 index 0000000..c222b41 --- /dev/null +++ b/.changeset/lazy-pugs-reply.md @@ -0,0 +1,5 @@ +--- +'react-native-legal': patch +--- + +Fixes an issue where licenses with unusual indentation caused YAML errors, breaking the LicencePlist script during the build. This ensures proper processing and prevents build failures. diff --git a/.github/workflows/expo-example-e2e.yml b/.github/workflows/expo-example-e2e.yml new file mode 100644 index 0000000..f15878b --- /dev/null +++ b/.github/workflows/expo-example-e2e.yml @@ -0,0 +1,127 @@ +name: Expo Example E2E Tests + +on: + push: + branches: [main] + paths: + - 'packages/react-native-legal/**' + - 'examples/expo-example/**' + - '.github/workflows/expo-example-e2e.yml' + pull_request: + paths: + - 'packages/react-native-legal/**' + - 'examples/expo-example/**' + - '.github/workflows/expo-example-e2e.yml' + +jobs: + android-e2e: + name: Android E2E Tests + runs-on: ubuntu-latest + timeout-minutes: 45 + defaults: + run: + working-directory: examples/expo-example + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - 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: Prebuild + run: yarn expo prebuild --platform android + + - name: Build Android + run: | + cd android && ./gradlew assembleRelease || { + echo "Error: Failed to build Android app" + exit 1 + } + + - name: Install Maestro + run: | + curl -Ls "https://get.maestro.mobile.dev" | bash + echo "$HOME/.maestro/bin" >> $GITHUB_PATH + source $HOME/.bash_profile + maestro --version + + - name: Run Android E2E tests + uses: reactivecircus/android-emulator-runner@v2 + with: + profile: pixel_7 + api-level: 34 + target: google_apis + arch: x86_64 + disable-animations: true + script: | + adb install -r android/app/build/outputs/apk/release/app-release.apk || { + echo "Error: Failed to install Android APK" + exit 1 + } + yarn test:e2e:android + + ios-e2e: + name: iOS E2E Tests + runs-on: macos-latest + timeout-minutes: 45 + defaults: + run: + working-directory: examples/expo-example + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Prebuild + run: yarn expo prebuild --platform ios + + - name: Setup iOS Simulator + uses: futureware-tech/simulator-action@v2 + with: + model: 'iPhone 15' + + - name: Build iOS + run: | + xcodebuild -workspace ios/ReactNativeLegalExpoExample.xcworkspace \ + -scheme ReactNativeLegalExpoExample \ + -sdk iphonesimulator \ + -configuration Release \ + -derivedDataPath build || { + echo "Error: Failed to build iOS app" + exit 1 + } + + - name: Install iOS app + run: | + xcrun simctl install booted ./build/Build/Products/Release-iphonesimulator/ReactNativeLegalExpoExample.app || { + echo "Error: Failed to install iOS app" + exit 1 + } + + - name: Install Maestro + run: | + curl -Ls "https://get.maestro.mobile.dev" | bash + echo "$HOME/.maestro/bin" >> $GITHUB_PATH + source $HOME/.bash_profile + maestro --version + + - name: Run iOS E2E tests + run: yarn test:e2e:ios \ No newline at end of file diff --git a/examples/bare-example/.gitignore b/examples/bare-example/.gitignore index f13c9ea..df12100 100644 --- a/examples/bare-example/.gitignore +++ b/examples/bare-example/.gitignore @@ -64,3 +64,6 @@ yarn-error.log # testing /coverage + +# RNEF +.rnef/ diff --git a/examples/bare-example/App.tsx b/examples/bare-example/App.tsx index 532094c..3d1de17 100644 --- a/examples/bare-example/App.tsx +++ b/examples/bare-example/App.tsx @@ -7,7 +7,7 @@ const BUTTON_RIPPLE_COLOR = '#8232ffba'; export default function App() { function launchNotice() { - ReactNativeLegal.launchLicenseListScreen('OSS Notice'); + ReactNativeLegal.launchLicenseListScreen('Licences'); } return ( diff --git a/examples/bare-example/e2e/android-flow.yaml b/examples/bare-example/e2e/android-flow.yaml new file mode 100644 index 0000000..2e99d73 --- /dev/null +++ b/examples/bare-example/e2e/android-flow.yaml @@ -0,0 +1,45 @@ +appId: com.reactnativelegalbareexample +--- +# Launch the app fresh to avoid cached state +- launchApp: + clearState: true + +# Wait for the app to be ready +- extendedWaitUntil: + visible: "Tap to see list of OSS libraries" + timeout: 30000 + +# Open the OSS libraries list +- tapOn: "Tap to see list of OSS libraries" +- assertVisible: "Licences" + +# Scroll to find "react-native-legal" +- scrollUntilVisible: + centerElement: true + element: + text: "react-native-legal" + direction: DOWN + speed: 75 + timeout: 100000 + +# Verify contributor information +- assertVisible: "Mateusz Mędrek (https://github.com/mateusz1913)" + +# Open license details +- tapOn: + text: "MIT License" + below: "react-native-legal" + + +# Verify MIT license text is displayed +- assertVisible: ".*MIT License.*" +- assertVisible: ".*Permission is hereby granted.*" +- assertVisible: ".*THE SOFTWARE IS PROVIDED \"AS IS\".*" + +# Navigate back to the list +- back +- assertVisible: "Licences" + +# Navigate back to home screen +- back +- assertVisible: "Tap to see list of OSS libraries" diff --git a/examples/bare-example/e2e/ios-flow.yaml b/examples/bare-example/e2e/ios-flow.yaml new file mode 100644 index 0000000..6059a42 --- /dev/null +++ b/examples/bare-example/e2e/ios-flow.yaml @@ -0,0 +1,52 @@ +appId: org.reactjs.native.example.ReactNativeLegalBareExample +--- +# Launch the app fresh to avoid cached state +- launchApp: + clearState: true + +# Wait for the app to be ready +- extendedWaitUntil: + visible: "Tap to see list of OSS libraries" + timeout: 30000 + +# Open the OSS libraries list +- tapOn: "Tap to see list of OSS libraries" +- assertVisible: "Licences" + +# Scroll to find "react-native-legal" +- scrollUntilVisible: + element: + text: "react-native-legal" + direction: DOWN + speed: 100 + +# Open license details +- tapOn: "react-native-legal" + +# Verify MIT license text is displayed +- assertVisible: "MIT" + +# Navigate back to the list +- tapOn: "Licences" +- assertVisible: "Licences" + +# Test React-Core license +- scrollUntilVisible: + element: "React-Core" + direction: DOWN + speed: 100 + +# Open license details +- tapOn: "React-Core" + +# Verify React-Core license text is displayed +- assertVisible: "Copyright (c) Meta Platforms, Inc. and affiliates." + +# Navigate back to the list +- tapOn: "Licences" +- assertVisible: "Licences" + +# Navigate back to home screen +- tapOn: + id: "UICloseButtonBackground" +- assertVisible: "Tap to see list of OSS libraries" diff --git a/examples/bare-example/ios/Podfile b/examples/bare-example/ios/Podfile index 3e2b3ed..bc57659 100644 --- a/examples/bare-example/ios/Podfile +++ b/examples/bare-example/ios/Podfile @@ -18,7 +18,7 @@ if linkage != nil end target 'ReactNativeLegalBareExample' do - config = use_native_modules! + config = use_native_modules!(['npx', 'rnef', 'config', '-p', 'ios']) use_react_native!( :path => config[:reactNativePath], diff --git a/examples/bare-example/ios/Podfile.lock b/examples/bare-example/ios/Podfile.lock index 3fb54ba..a46068a 100644 --- a/examples/bare-example/ios/Podfile.lock +++ b/examples/bare-example/ios/Podfile.lock @@ -1510,7 +1510,7 @@ PODS: - React-logger (= 0.76.7) - React-perflogger (= 0.76.7) - React-utils (= 0.76.7) - - ReactNativeLegal (1.0.0): + - ReactNativeLegal (1.1.0): - DoubleConversion - glog - hermes-engine @@ -1803,10 +1803,10 @@ SPEC CHECKSUMS: React-utils: 9f9a6a31d703b136eb1614d914c10a3c96b1e6dd ReactCodegen: 5d7e2d2948a6629a51a59ebc99f620e2afb13ee5 ReactCommon: 04292c6f596181ebf755e7929d96d2148542b0e8 - ReactNativeLegal: 3514fb143867dc60703a0a04af5d9c200c8ae83f + ReactNativeLegal: 9ef07788333fd105354eeafd6892a29e52cfaae0 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 90d80701b27946c4b23461c00a7207f300a6ff71 -PODFILE CHECKSUM: d8cf13800dea3fa1eb0928883853c6b37979b1d5 +PODFILE CHECKSUM: 281ef88fa74b8505eb17a64f6c9548afb72d4104 COCOAPODS: 1.15.2 diff --git a/examples/bare-example/ios/ReactNativeLegalBareExample.xcodeproj/project.pbxproj b/examples/bare-example/ios/ReactNativeLegalBareExample.xcodeproj/project.pbxproj index 604ee65..84b4645 100644 --- a/examples/bare-example/ios/ReactNativeLegalBareExample.xcodeproj/project.pbxproj +++ b/examples/bare-example/ios/ReactNativeLegalBareExample.xcodeproj/project.pbxproj @@ -270,7 +270,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + shellScript = "set -e\n\nif [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\nsource \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\nsource \"$PODS_ROOT/../.xcode.env.local\"\nfi\nexport CONFIG_CMD=\"dummy-workaround-value\"\nexport CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@rnef/cli/package.json')) + '/dist/src/bin.js'\")\"\n\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; diff --git a/examples/bare-example/package.json b/examples/bare-example/package.json index b4814e7..c2a67ce 100644 --- a/examples/bare-example/package.json +++ b/examples/bare-example/package.json @@ -8,7 +8,10 @@ "lint": "eslint .", "start": "react-native start --config ./metro.config.js", "test": "jest", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "test:e2e:ios": "maestro test ./e2e/ios-flow.yaml", + "test:e2e:android": "maestro test ./e2e/android-flow.yaml", + "test:e2e": "yarn test:e2e:ios && yarn test:e2e:android" }, "dependencies": { "react": "18.3.1", @@ -25,6 +28,10 @@ "@react-native/babel-preset": "0.76.7", "@react-native/metro-config": "0.76.7", "@react-native/typescript-config": "0.76.7", + "@rnef/cli": "^0.4.1", + "@rnef/platform-android": "^0.4.1", + "@rnef/platform-ios": "^0.4.1", + "@rnef/plugin-metro": "^0.4.1", "@types/react": "~18.3.12", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.6.3", @@ -34,4 +41,4 @@ "engines": { "node": ">=18" } -} \ No newline at end of file +} diff --git a/examples/bare-example/rnef.config.mjs b/examples/bare-example/rnef.config.mjs new file mode 100644 index 0000000..580abe1 --- /dev/null +++ b/examples/bare-example/rnef.config.mjs @@ -0,0 +1,12 @@ +import { platformAndroid } from '@rnef/platform-android'; +import { platformIOS } from '@rnef/platform-ios'; +import { pluginMetro } from '@rnef/plugin-metro'; + +export default { + bundler: pluginMetro(), + platforms: { + ios: platformIOS(), + android: platformAndroid(), + }, + remoteCacheProvider: 'github-actions', +}; diff --git a/examples/expo-example/.nvmrc b/examples/expo-example/.nvmrc new file mode 100644 index 0000000..016e34b --- /dev/null +++ b/examples/expo-example/.nvmrc @@ -0,0 +1 @@ +v20.17.0 diff --git a/examples/expo-example/App.tsx b/examples/expo-example/App.tsx index 74d3dfd..c490321 100644 --- a/examples/expo-example/App.tsx +++ b/examples/expo-example/App.tsx @@ -8,7 +8,7 @@ const BUTTON_RIPPLE_COLOR = '#8232ffba'; export default function App() { function launchNotice() { - ReactNativeLegal.launchLicenseListScreen('OSS Notice'); + ReactNativeLegal.launchLicenseListScreen('Licences'); } return ( diff --git a/examples/expo-example/app.json b/examples/expo-example/app.json index 0c80bb2..8b5ae99 100644 --- a/examples/expo-example/app.json +++ b/examples/expo-example/app.json @@ -15,7 +15,10 @@ "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true, - "bundleIdentifier": "com.reactnativelegal.expoexample" + "bundleIdentifier": "com.reactnativelegal.expoexample", + "infoPlist": { + "ITSAppUsesNonExemptEncryption": false + } }, "android": { "adaptiveIcon": { @@ -42,6 +45,12 @@ } ], "react-native-legal" - ] + ], + "extra": { + "eas": { + "projectId": "58a16ff5-f983-4966-9d2f-9ba2b653b081" + } + }, + "owner": "piotrski" } } diff --git a/examples/expo-example/e2e/android-flow.yaml b/examples/expo-example/e2e/android-flow.yaml new file mode 100644 index 0000000..4014863 --- /dev/null +++ b/examples/expo-example/e2e/android-flow.yaml @@ -0,0 +1,45 @@ +appId: com.reactnativelegal.expoexample +--- +# Launch the app fresh to avoid cached state +- launchApp: + clearState: true + +# Wait for the app to be ready +- extendedWaitUntil: + visible: "Tap to see list of OSS libraries" + timeout: 30000 + +# Open the OSS libraries list +- tapOn: "Tap to see list of OSS libraries" +- assertVisible: "Licences" + +# Scroll to find "react-native-legal" +- scrollUntilVisible: + centerElement: true + element: + text: "react-native-legal" + direction: DOWN + speed: 75 + timeout: 100000 + +# Verify contributor information +- assertVisible: "Mateusz Mędrek (https://github.com/mateusz1913)" + +# Open license details +- tapOn: + text: "MIT License" + below: "react-native-legal" + + +# Verify MIT license text is displayed +- assertVisible: ".*MIT License.*" +- assertVisible: ".*Permission is hereby granted.*" +- assertVisible: ".*THE SOFTWARE IS PROVIDED \"AS IS\".*" + +# Navigate back to the list +- back +- assertVisible: "Licences" + +# Navigate back to home screen +- back +- assertVisible: "Tap to see list of OSS libraries" diff --git a/examples/expo-example/e2e/ios-flow.yaml b/examples/expo-example/e2e/ios-flow.yaml new file mode 100644 index 0000000..8195cab --- /dev/null +++ b/examples/expo-example/e2e/ios-flow.yaml @@ -0,0 +1,52 @@ +appId: com.reactnativelegal.expoexample +--- +# Launch the app fresh to avoid cached state +- launchApp: + clearState: true + +# Wait for the app to be ready +- extendedWaitUntil: + visible: "Tap to see list of OSS libraries" + timeout: 30000 + +# Open the OSS libraries list +- tapOn: "Tap to see list of OSS libraries" +- assertVisible: "Licences" + +# Scroll to find "react-native-legal" +- scrollUntilVisible: + element: + text: "react-native-legal" + direction: DOWN + speed: 100 + +# Open license details +- tapOn: "react-native-legal" + +# Verify MIT license text is displayed +- assertVisible: "MIT" + +# Navigate back to the list +- tapOn: "Licences" +- assertVisible: "Licences" + +# Test React-Core license +- scrollUntilVisible: + element: "React-Core" + direction: DOWN + speed: 100 + +# Open license details +- tapOn: "React-Core" + +# Verify React-Core license text is displayed +- assertVisible: "Copyright (c) Meta Platforms, Inc. and affiliates." + +# Navigate back to the list +- tapOn: "Licences" +- assertVisible: "Licences" + +# Navigate back to home screen +- tapOn: + id: "UICloseButtonBackground" +- assertVisible: "Tap to see list of OSS libraries" diff --git a/examples/expo-example/eas.json b/examples/expo-example/eas.json new file mode 100644 index 0000000..08ab8a7 --- /dev/null +++ b/examples/expo-example/eas.json @@ -0,0 +1,27 @@ +{ + "cli": { + "version": ">= 15.0.12", + "appVersionSource": "remote" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal" + }, + "testing": { + "distribution": "internal", + "ios": { + "simulator": true + } + }, + "preview": { + "distribution": "internal" + }, + "production": { + "autoIncrement": true + } + }, + "submit": { + "production": {} + } +} diff --git a/examples/expo-example/package.json b/examples/expo-example/package.json index 170572c..d9f7c37 100644 --- a/examples/expo-example/package.json +++ b/examples/expo-example/package.json @@ -8,7 +8,10 @@ "android": "expo run:android", "ios": "expo run:ios", "web": "expo start --web", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "test:e2e:ios": "maestro test ./e2e/ios-flow.yaml", + "test:e2e:android": "maestro test ./e2e/android-flow.yaml", + "test:e2e": "yarn test:e2e:ios && yarn test:e2e:android" }, "dependencies": { "expo": "~52.0.36", @@ -24,4 +27,4 @@ "@types/react": "~18.3.12" }, "private": true -} +} \ No newline at end of file diff --git a/packages/react-native-legal/plugin-utils/src/common.ts b/packages/react-native-legal/plugin-utils/src/common.ts index 42db921..1e7caa0 100644 --- a/packages/react-native-legal/plugin-utils/src/common.ts +++ b/packages/react-native-legal/plugin-utils/src/common.ts @@ -8,6 +8,7 @@ type LicenseObj = { author?: string; content?: string; description?: string; + file?: string; type?: string; url?: string; version: string; @@ -31,10 +32,11 @@ type AboutLibrariesLicenseJsonPayload = { }; type LicensePlistPayload = { - body: string; name: string; source?: string; version: string; + body?: string; + file?: string; }; function compareObjects(a: unknown, b: unknown): boolean { @@ -110,6 +112,7 @@ function scanPackage( result[packageName] = { author: parseAuthorField(localPackageJson), content: licenseFiles?.[0] ? fs.readFileSync(licenseFiles[0], { encoding: 'utf-8' }) : undefined, + file: licenseFiles?.[0] ? licenseFiles[0] : undefined, description: localPackageJson.description, type: parseLicenseField(localPackageJson), url: parseRepositoryFieldToUrl(localPackageJson), @@ -243,11 +246,15 @@ export function generateLicensePlistNPMOutput(licenses: Record