Skip to content

Commit

Permalink
test(react-navigation): move react navigation tests to dynamic fixtur…
Browse files Browse the repository at this point in the history
…e setup (#2201)

* test(react-native): move react navigation scenarios to dynamic test fixture

* ci(react-native): run react navigation scenarios as part of main test run

* skip react navigation tests on new arch

* enable react navigation tests in full pipeline

* move legacy android builder step to full pipeline

* tidy up fixture generation script
  • Loading branch information
yousif-bugsnag committed Sep 23, 2024
1 parent 6ae56c4 commit c289314
Show file tree
Hide file tree
Showing 14 changed files with 326 additions and 108 deletions.
2 changes: 0 additions & 2 deletions .buildkite/basic/react-native-android-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ steps:
manual:
permit_on_passed: true
env:
SKIP_NAVIGATION_SCENARIOS: "true"
RN_VERSION: "{{matrix}}"
RCT_NEW_ARCH_ENABLED: "0"
concurrency: 25
Expand Down Expand Up @@ -111,7 +110,6 @@ steps:
manual:
permit_on_passed: true
env:
SKIP_NAVIGATION_SCENARIOS: "true"
RCT_NEW_ARCH_ENABLED: "1"
RN_VERSION: "{{matrix}}"
concurrency: 25
Expand Down
2 changes: 0 additions & 2 deletions .buildkite/basic/react-native-ios-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ steps:
manual:
permit_on_passed: true
env:
SKIP_NAVIGATION_SCENARIOS: "true"
RN_VERSION: "{{matrix}}"
RCT_NEW_ARCH_ENABLED: "0"
concurrency: 25
Expand Down Expand Up @@ -110,7 +109,6 @@ steps:
- --aws-public-ip
env:
RCT_NEW_ARCH_ENABLED: "1"
SKIP_NAVIGATION_SCENARIOS: "true"
RN_VERSION: "{{matrix}}"
retry:
manual:
Expand Down
18 changes: 18 additions & 0 deletions .buildkite/full/pipeline.full.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
steps:

#
# Java 11 Android builder base - used by React Native and React Native CLI (< 0.73)
#
- label: ":docker: Build Java 11 Android Builder base image"
key: "android-builder-base-java-11"
timeout_in_minutes: 30
plugins:
- docker-compose#v4.12.0:
build: android-builder-base-java-11
image-repository: 855461928731.dkr.ecr.us-west-1.amazonaws.com/js
cache-from: android-builder-base-java-11:855461928731.dkr.ecr.us-west-1.amazonaws.com/js:android-builder-base-java-11
- docker-compose#v4.12.0:
push: android-builder-base-java-11:855461928731.dkr.ecr.us-west-1.amazonaws.com/js:android-builder-base-java-11
retry:
automatic:
- exit_status: "*"
limit: 1

#
# Upload full React Native pipelines
#
Expand Down
2 changes: 0 additions & 2 deletions .buildkite/full/react-native-android-pipeline.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ steps:
manual:
permit_on_passed: true
env:
SKIP_NAVIGATION_SCENARIOS: "true"
RN_VERSION: "{{matrix}}"
RCT_NEW_ARCH_ENABLED: "0"
concurrency: 25
Expand Down Expand Up @@ -362,7 +361,6 @@ steps:
manual:
permit_on_passed: true
env:
SKIP_NAVIGATION_SCENARIOS: "true"
RCT_NEW_ARCH_ENABLED: "1"
RN_VERSION: "{{matrix}}"
concurrency: 25
Expand Down
2 changes: 0 additions & 2 deletions .buildkite/full/react-native-ios-pipeline.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,6 @@ steps:
manual:
permit_on_passed: true
env:
SKIP_NAVIGATION_SCENARIOS: "true"
RN_VERSION: "{{matrix}}"
RCT_NEW_ARCH_ENABLED: "0"
concurrency: 25
Expand Down Expand Up @@ -329,7 +328,6 @@ steps:
- --aws-public-ip
env:
RCT_NEW_ARCH_ENABLED: "1"
SKIP_NAVIGATION_SCENARIOS: "true"
RN_VERSION: "{{matrix}}"
retry:
manual:
Expand Down
18 changes: 0 additions & 18 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,6 @@ steps:
queue: macos-14
command: scripts/license_finder.sh

#
# Java 11 Android builder base - used by React Native and React Native CLI (< 0.73)
#
- label: ":docker: Build Java 11 Android Builder base image"
key: "android-builder-base-java-11"
timeout_in_minutes: 30
plugins:
- docker-compose#v4.12.0:
build: android-builder-base-java-11
image-repository: 855461928731.dkr.ecr.us-west-1.amazonaws.com/js
cache-from: android-builder-base-java-11:855461928731.dkr.ecr.us-west-1.amazonaws.com/js:android-builder-base-java-11
- docker-compose#v4.12.0:
push: android-builder-base-java-11:855461928731.dkr.ecr.us-west-1.amazonaws.com/js:android-builder-base-java-11
retry:
automatic:
- exit_status: "*"
limit: 1

#
# Publish/package notifier
#
Expand Down
204 changes: 136 additions & 68 deletions scripts/generate-react-native-fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,33 @@ if (!process.env.REGISTRY_URL) {

const notifierVersion = process.env.NOTIFIER_VERSION || common.determineVersion()

const rnVersion = process.env.RN_VERSION
const reactNativeVersion = process.env.RN_VERSION
const ROOT_DIR = resolve(__dirname, '../')

const isNewArchEnabled = process.env.RCT_NEW_ARCH_ENABLED === 'true' || process.env.RCT_NEW_ARCH_ENABLED === '1'

let fixturePath = 'test/react-native/features/fixtures/generated/'
if (process.env.RCT_NEW_ARCH_ENABLED === '1') {
if (isNewArchEnabled) {
fixturePath += 'new-arch/'
} else {
fixturePath += 'old-arch/'
}

const fixtureDir = resolve(ROOT_DIR, fixturePath, rnVersion)
const fixtureDir = resolve(ROOT_DIR, fixturePath, reactNativeVersion)

const replacementFilesDir = resolve(ROOT_DIR, 'test/react-native/features/fixtures/app/dynamic/')

const DEPENDENCIES = [
'react-native-file-access@3.0.4'
'react-native-file-access@3.0.4',
`@bugsnag/react-native@${notifierVersion}`
]

const REACT_NAVIGATION_DEPENDENCIES = [
`@bugsnag/plugin-react-navigation@${notifierVersion}`,
'@react-navigation/native',
'@react-navigation/native-stack',
'react-native-screens',
'react-native-safe-area-context'
]

if (!process.env.SKIP_GENERATE_FIXTURE) {
Expand All @@ -40,76 +51,16 @@ if (!process.env.SKIP_GENERATE_FIXTURE) {
}

// create the test fixture
const RNInitArgs = [`react-native@${process.env.RN_VERSION}`, 'init', 'reactnative', '--directory', fixtureDir, '--version', rnVersion, '--npm', '--skip-install']
const RNInitArgs = [`react-native@${process.env.RN_VERSION}`, 'init', 'reactnative', '--directory', fixtureDir, '--version', reactNativeVersion, '--npm', '--skip-install']
execFileSync('npx', RNInitArgs, { stdio: 'inherit' })

// replace the App.js/App.tsx file with our own App.js file
fs.readdirSync(resolve(fixtureDir))
.filter((file) => /App\.[tj]sx?$/.test(file))
.map((file) => fs.unlinkSync(resolve(fixtureDir, file)))
replaceGeneratedFixtureFiles()

fs.copyFileSync(
resolve(replacementFilesDir, 'App.js'),
resolve(fixtureDir, 'App.js')
)

// replace the AndroidManifest.xml file with our own
fs.copyFileSync(
resolve(replacementFilesDir, 'android/AndroidManifest.xml'),
resolve(fixtureDir, 'android/app/src/main/AndroidManifest.xml')
)

// replace the Info.plist file with our own
fs.copyFileSync(
resolve(replacementFilesDir, 'ios/Info.plist'),
resolve(fixtureDir, 'ios/reactnative/Info.plist')
)

// copy the exportOptions.plist file
fs.copyFileSync(
resolve(replacementFilesDir, 'ios/exportOptions.plist'),
resolve(fixtureDir, 'exportOptions.plist')
)

// update pbxproj
let pbxProjContents = fs.readFileSync(`${fixtureDir}/ios/reactnative.xcodeproj/project.pbxproj`, 'utf8')
pbxProjContents = pbxProjContents.replaceAll('org.reactjs.native.example', 'com.bugsnag.fixtures')

fs.writeFileSync(`${fixtureDir}/ios/reactnative.xcodeproj/project.pbxproj`, pbxProjContents)

// update Podfile
let podfileContents = fs.readFileSync(`${fixtureDir}/ios/Podfile`, 'utf8')

// use static frameworks (this fixes an issue with react-native-file-access on 0.75)
if (parseFloat(rnVersion) >= 0.75) {
podfileContents = podfileContents.replace(/target 'reactnative' do/, 'use_frameworks! :linkage => :static\ntarget \'reactnative\' do')
}

// disable Flipper
if (podfileContents.includes('use_flipper!')) {
podfileContents = podfileContents.replace(/use_flipper!/, '# use_flipper!')
} else if (podfileContents.includes(':flipper_configuration')) {
podfileContents = podfileContents.replace(/:flipper_configuration/, '# :flipper_configuration')
}

fs.writeFileSync(`${fixtureDir}/ios/Podfile`, podfileContents)

const fixtureDependencyArgs = DEPENDENCIES.join(' ')

// install test fixture dependencies and local packages
execSync(`npm install --save ${fixtureDependencyArgs}`, { cwd: fixtureDir, stdio: 'inherit' })

// install @bugsnag/react-native from the registry
execSync(`npm install --save @bugsnag/react-native@${notifierVersion} --registry ${process.env.REGISTRY_URL}`, { cwd: fixtureDir, stdio: 'inherit' })

// install the scenario launcher package
const scenarioLauncherPackage = `${ROOT_DIR}/test/react-native/features/fixtures/scenario-launcher`
execSync(`npm pack ${scenarioLauncherPackage} --pack-destination ${fixtureDir}`, { cwd: ROOT_DIR, stdio: 'inherit' })
execSync('npm install --save bugsnag-react-native-scenarios-1.0.0.tgz', { cwd: fixtureDir, stdio: 'inherit' })
installFixtureDependencies()
}

if (process.env.BUILD_ANDROID === 'true' || process.env.BUILD_ANDROID === '1') {
if (process.env.RCT_NEW_ARCH_ENABLED === 'true' || process.env.RCT_NEW_ARCH_ENABLED === '1') {
if (isNewArchEnabled) {
// If we're building with the new architecture, replace the gradle.properties file
fs.copyFileSync(
resolve(replacementFilesDir, 'android/newarch.gradle.properties'),
Expand Down Expand Up @@ -163,3 +114,120 @@ if (process.env.BUILD_IOS === 'true' || process.env.BUILD_IOS === '1') {

execFileSync('xcrun', exportArgs, { cwd: fixtureDir, stdio: 'inherit' })
}

function installFixtureDependencies () {
if (!isNewArchEnabled) {
DEPENDENCIES.push(...REACT_NAVIGATION_DEPENDENCIES)
}

const fixtureDependencyArgs = DEPENDENCIES.join(' ')

// install test fixture dependencies
execSync(`npm install --save ${fixtureDependencyArgs} --registry ${process.env.REGISTRY_URL}`, { cwd: fixtureDir, stdio: 'inherit' })

// install the scenario launcher package
const scenarioLauncherPackage = `${ROOT_DIR}/test/react-native/features/fixtures/scenario-launcher`
execSync(`npm pack ${scenarioLauncherPackage} --pack-destination ${fixtureDir}`, { cwd: ROOT_DIR, stdio: 'inherit' })
execSync('npm install --save bugsnag-react-native-scenarios-1.0.0.tgz', { cwd: fixtureDir, stdio: 'inherit' })
}

/** Replace native files generated by react-native cli with pre-configured files */
function replaceGeneratedFixtureFiles () {
// replace the App.js/App.tsx file with our own App.js file
fs.readdirSync(resolve(fixtureDir))
.filter((file) => /App\.[tj]sx?$/.test(file))
.map((file) => fs.unlinkSync(resolve(fixtureDir, file)))

fs.copyFileSync(
resolve(replacementFilesDir, 'App.js'),
resolve(fixtureDir, 'App.js')
)

// replace the AndroidManifest.xml file with our own
fs.copyFileSync(
resolve(replacementFilesDir, 'android/AndroidManifest.xml'),
resolve(fixtureDir, 'android/app/src/main/AndroidManifest.xml')
)

// replace the Info.plist file with our own
fs.copyFileSync(
resolve(replacementFilesDir, 'ios/Info.plist'),
resolve(fixtureDir, 'ios/reactnative/Info.plist')
)

// copy the exportOptions.plist file
fs.copyFileSync(
resolve(replacementFilesDir, 'ios/exportOptions.plist'),
resolve(fixtureDir, 'exportOptions.plist')
)

// update pbxproj
let pbxProjContents = fs.readFileSync(`${fixtureDir}/ios/reactnative.xcodeproj/project.pbxproj`, 'utf8')
pbxProjContents = pbxProjContents.replaceAll('org.reactjs.native.example', 'com.bugsnag.fixtures')

fs.writeFileSync(`${fixtureDir}/ios/reactnative.xcodeproj/project.pbxproj`, pbxProjContents)

// update Podfile
let podfileContents = fs.readFileSync(`${fixtureDir}/ios/Podfile`, 'utf8')

// use static frameworks (this fixes an issue with react-native-file-access on 0.75)
if (parseFloat(reactNativeVersion) >= 0.75) {
podfileContents = podfileContents.replace(/target 'reactnative' do/, 'use_frameworks! :linkage => :static\ntarget \'reactnative\' do')
}

// disable Flipper
if (podfileContents.includes('use_flipper!')) {
podfileContents = podfileContents.replace(/use_flipper!/, '# use_flipper!')
} else if (podfileContents.includes(':flipper_configuration')) {
podfileContents = podfileContents.replace(/:flipper_configuration/, '# :flipper_configuration')
}

fs.writeFileSync(`${fixtureDir}/ios/Podfile`, podfileContents)

// react navigation setup
if (!isNewArchEnabled) {
configureReactNavigationAndroid()
}
}

function configureReactNavigationAndroid () {
const fileExtension = parseFloat(reactNativeVersion) < 0.73 ? 'java' : 'kt'
let mainActivityPattern, mainActivityReplacement
if (fileExtension === 'java') {
mainActivityPattern = 'public class MainActivity extends ReactActivity {'
mainActivityReplacement = `
import android.os.Bundle;
public class MainActivity extends ReactActivity {
/**
* Required for react-navigation/native implementation
* https://reactnavigation.org/docs/getting-started/#installing-dependencies-into-a-bare-react-native-project
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
`
} else if (fileExtension === 'kt') {
mainActivityPattern = 'class MainActivity : ReactActivity() {'
mainActivityReplacement = `
import android.os.Bundle
class MainActivity : ReactActivity() {
/**
* Required for react-navigation/native implementation
* https://reactnavigation.org/docs/getting-started/#installing-dependencies-into-a-bare-react-native-project
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(null)
}
`
}

const mainActivityPath = `${fixtureDir}/android/app/src/main/java/com/reactnative/MainActivity.${fileExtension}`
let mainActivityContents = fs.readFileSync(mainActivityPath, 'utf8')
mainActivityContents = mainActivityContents.replace(mainActivityPattern, mainActivityReplacement)
fs.writeFileSync(mainActivityPath, mainActivityContents)
}
18 changes: 11 additions & 7 deletions test/react-native/features/fixtures/app/dynamic/App.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import { SafeAreaView, StyleSheet, View, Text } from 'react-native'
import { launchScenario } from '@bugsnag/react-native-scenarios'

const App = () => {
const [scenario, setScenario] = useState(null)

useEffect(() => {
launchScenario()
launchScenario(setScenario)
}, [])

return (
<SafeAreaView style={styles.container}>
<View>
<Text>React Native Test App</Text>
</View>
</SafeAreaView>
scenario !== null ? scenario.view() : (
<SafeAreaView style={styles.container}>
<View>
<Text>React Native Test App</Text>
</View>
</SafeAreaView>
)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"react-native-navigation": "*"
},
"peerDependencies": {
"@bugsnag/plugin-react-navigation": "*",
"@react-navigation/native": "*",
"@react-navigation/native-stack": "*",
"react": "*",
"react-native": "*",
"react-native-file-access": "*"
Expand Down
Loading

0 comments on commit c289314

Please sign in to comment.