From c2893147b4c85779362843c12057e224f8445e4e Mon Sep 17 00:00:00 2001 From: Yousif <74918474+yousif-bugsnag@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:32:46 +0100 Subject: [PATCH] test(react-navigation): move react navigation tests to dynamic fixture 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 --- .../basic/react-native-android-pipeline.yml | 2 - .../basic/react-native-ios-pipeline.yml | 2 - .buildkite/full/pipeline.full.yml | 18 ++ .../react-native-android-pipeline.full.yml | 2 - .../full/react-native-ios-pipeline.full.yml | 2 - .buildkite/pipeline.yml | 18 -- scripts/generate-react-native-fixture.js | 204 ++++++++++++------ .../features/fixtures/app/dynamic/App.js | 18 +- .../fixtures/scenario-launcher/package.json | 3 + .../src/lib/ScenarioLauncher.js | 14 +- ...ctNavigationBreadcrumbsDisabledScenario.js | 73 +++++++ ...actNavigationBreadcrumbsEnabledScenario.js | 72 +++++++ .../scenario-launcher/src/scenarios/index.js | 4 + test/react-native/features/navigation.feature | 2 +- 14 files changed, 326 insertions(+), 108 deletions(-) create mode 100644 test/react-native/features/fixtures/scenario-launcher/src/scenarios/ReactNavigationBreadcrumbsDisabledScenario.js create mode 100644 test/react-native/features/fixtures/scenario-launcher/src/scenarios/ReactNavigationBreadcrumbsEnabledScenario.js diff --git a/.buildkite/basic/react-native-android-pipeline.yml b/.buildkite/basic/react-native-android-pipeline.yml index 788d1fe74..714915454 100644 --- a/.buildkite/basic/react-native-android-pipeline.yml +++ b/.buildkite/basic/react-native-android-pipeline.yml @@ -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 @@ -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 diff --git a/.buildkite/basic/react-native-ios-pipeline.yml b/.buildkite/basic/react-native-ios-pipeline.yml index f168adf87..00f0f6704 100644 --- a/.buildkite/basic/react-native-ios-pipeline.yml +++ b/.buildkite/basic/react-native-ios-pipeline.yml @@ -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 @@ -110,7 +109,6 @@ steps: - --aws-public-ip env: RCT_NEW_ARCH_ENABLED: "1" - SKIP_NAVIGATION_SCENARIOS: "true" RN_VERSION: "{{matrix}}" retry: manual: diff --git a/.buildkite/full/pipeline.full.yml b/.buildkite/full/pipeline.full.yml index 45ee8cd03..01d682239 100644 --- a/.buildkite/full/pipeline.full.yml +++ b/.buildkite/full/pipeline.full.yml @@ -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 # diff --git a/.buildkite/full/react-native-android-pipeline.full.yml b/.buildkite/full/react-native-android-pipeline.full.yml index 87482fc3f..1a1285b94 100644 --- a/.buildkite/full/react-native-android-pipeline.full.yml +++ b/.buildkite/full/react-native-android-pipeline.full.yml @@ -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 @@ -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 diff --git a/.buildkite/full/react-native-ios-pipeline.full.yml b/.buildkite/full/react-native-ios-pipeline.full.yml index 91fb44b84..86a2846de 100644 --- a/.buildkite/full/react-native-ios-pipeline.full.yml +++ b/.buildkite/full/react-native-ios-pipeline.full.yml @@ -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 @@ -329,7 +328,6 @@ steps: - --aws-public-ip env: RCT_NEW_ARCH_ENABLED: "1" - SKIP_NAVIGATION_SCENARIOS: "true" RN_VERSION: "{{matrix}}" retry: manual: diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 59f2633b6..401022a08 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -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 # diff --git a/scripts/generate-react-native-fixture.js b/scripts/generate-react-native-fixture.js index e5217fae8..4df76bd5a 100644 --- a/scripts/generate-react-native-fixture.js +++ b/scripts/generate-react-native-fixture.js @@ -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) { @@ -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'), @@ -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) +} diff --git a/test/react-native/features/fixtures/app/dynamic/App.js b/test/react-native/features/fixtures/app/dynamic/App.js index 38bfb256d..8fa4ee360 100644 --- a/test/react-native/features/fixtures/app/dynamic/App.js +++ b/test/react-native/features/fixtures/app/dynamic/App.js @@ -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 ( - - - React Native Test App - - + scenario !== null ? scenario.view() : ( + + + React Native Test App + + + ) ) } diff --git a/test/react-native/features/fixtures/scenario-launcher/package.json b/test/react-native/features/fixtures/scenario-launcher/package.json index 824eb96d2..bc497a36d 100644 --- a/test/react-native/features/fixtures/scenario-launcher/package.json +++ b/test/react-native/features/fixtures/scenario-launcher/package.json @@ -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": "*" diff --git a/test/react-native/features/fixtures/scenario-launcher/src/lib/ScenarioLauncher.js b/test/react-native/features/fixtures/scenario-launcher/src/lib/ScenarioLauncher.js index 4ef7fbafd..e389e43f3 100644 --- a/test/react-native/features/fixtures/scenario-launcher/src/lib/ScenarioLauncher.js +++ b/test/react-native/features/fixtures/scenario-launcher/src/lib/ScenarioLauncher.js @@ -3,7 +3,7 @@ import { getCurrentCommand } from './CommandRunner' import { NativeInterface } from './native' import Bugsnag from '@bugsnag/react-native' -async function runScenario (scenarioName, apiKey, notifyEndpoint, sessionEndpoint, scenarioData) { +async function runScenario (scenarioName, apiKey, notifyEndpoint, sessionEndpoint, scenarioData, setScenario) { console.error(`[Bugsnag ScenarioLauncher] running scenario: ${scenarioName}`) const nativeConfig = { @@ -20,8 +20,6 @@ async function runScenario (scenarioName, apiKey, notifyEndpoint, sessionEndpoin // create the scenario and allow it to modify the configuration const scenario = new Scenarios[scenarioName](nativeConfig, jsConfig, scenarioData) - console.error(`[Bugsnag ScenarioLauncher] with config: ${JSON.stringify(nativeConfig)} (native) and ${JSON.stringify(jsConfig)} (js)`) - // clear persistent data console.error('[Bugsnag ScenarioLauncher] clearing persistent data') NativeInterface.clearPersistentData() @@ -36,7 +34,10 @@ async function runScenario (scenarioName, apiKey, notifyEndpoint, sessionEndpoin // run the scenario console.error('launching scenario') - setTimeout(() => scenario.run(), 1) + setTimeout(() => { + scenario.run() + if (typeof scenario.view === 'function') setScenario(scenario) + }, 1) } async function startBugsnag (scenarioName, apiKey, notifyEndpoint, sessionEndpoint, scenarioData) { @@ -67,7 +68,7 @@ async function startBugsnag (scenarioName, apiKey, notifyEndpoint, sessionEndpoi Bugsnag.start(jsConfig) } -export async function launchScenario () { +export async function launchScenario (setScenario) { const command = await getCurrentCommand() switch (command.action) { @@ -78,7 +79,8 @@ export async function launchScenario () { command.api_key, command.notify, command.sessions, - command.scenario_data + command.scenario_data, + setScenario ) case 'start-bugsnag': diff --git a/test/react-native/features/fixtures/scenario-launcher/src/scenarios/ReactNavigationBreadcrumbsDisabledScenario.js b/test/react-native/features/fixtures/scenario-launcher/src/scenarios/ReactNavigationBreadcrumbsDisabledScenario.js new file mode 100644 index 000000000..8c6c37362 --- /dev/null +++ b/test/react-native/features/fixtures/scenario-launcher/src/scenarios/ReactNavigationBreadcrumbsDisabledScenario.js @@ -0,0 +1,73 @@ +import Scenario from './Scenario' +import Bugsnag from '@bugsnag/react-native' +import BugsnagPluginReactNavigation from '@bugsnag/plugin-react-navigation' +import * as React from 'react' +import { View, Text } from 'react-native' +import { NavigationContainer } from '@react-navigation/native' +import { createNativeStackNavigator } from '@react-navigation/native-stack' + +const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) + +export class ReactNavigationBreadcrumbsDisabledScenario extends Scenario { + constructor (configuration, jsConfig) { + super() + configuration.enabledBreadcrumbTypes = ['process', 'request', 'log'] + jsConfig.plugins = [new BugsnagPluginReactNavigation()] + } + + view () { + const BugsnagNavigationContainer = Bugsnag.getPlugin('reactNavigation').createNavigationContainer(NavigationContainer) + const Stack = createNativeStackNavigator() + return ( + + + + + + + ) + } + + run () { + } +} + +function HomeScreen ({ navigation }) { + React.useEffect(() => { + (async () => { + await delay(100) + Bugsnag.notify(new Error('HomeNavigationError')) + await delay(250) + navigation.navigate('Details') + })() + }, []) + + return ( + + Home Screen + + ) +} + +function DetailsScreen ({ navigation }) { + React.useEffect(() => { + (async () => { + await delay(100) + Bugsnag.notify(new Error('DetailsNavigationError')) + await delay(250) + throw new Error('DetailsNavigationUnhandledError') + })() + }, []) + + return ( + + Details Screen + + ) +} diff --git a/test/react-native/features/fixtures/scenario-launcher/src/scenarios/ReactNavigationBreadcrumbsEnabledScenario.js b/test/react-native/features/fixtures/scenario-launcher/src/scenarios/ReactNavigationBreadcrumbsEnabledScenario.js new file mode 100644 index 000000000..6d3fdedd1 --- /dev/null +++ b/test/react-native/features/fixtures/scenario-launcher/src/scenarios/ReactNavigationBreadcrumbsEnabledScenario.js @@ -0,0 +1,72 @@ +import Scenario from './Scenario' +import Bugsnag from '@bugsnag/react-native' +import BugsnagPluginReactNavigation from '@bugsnag/plugin-react-navigation' +import * as React from 'react' +import { View, Text } from 'react-native' +import { NavigationContainer } from '@react-navigation/native' +import { createNativeStackNavigator } from '@react-navigation/native-stack' + +const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) + +export class ReactNavigationBreadcrumbsEnabledScenario extends Scenario { + constructor (_configuration, jsConfig) { + super() + jsConfig.plugins = [new BugsnagPluginReactNavigation()] + } + + view () { + const BugsnagNavigationContainer = Bugsnag.getPlugin('reactNavigation').createNavigationContainer(NavigationContainer) + const Stack = createNativeStackNavigator() + return ( + + + + + + + ) + } + + run () { + } +} + +function HomeScreen ({ navigation }) { + React.useEffect(() => { + (async () => { + await delay(100) + Bugsnag.notify(new Error('HomeNavigationError')) + await delay(250) + navigation.navigate('Details') + })() + }, []) + + return ( + + Home Screen + + ) +} + +function DetailsScreen ({ navigation }) { + React.useEffect(() => { + (async () => { + await delay(100) + Bugsnag.notify(new Error('DetailsNavigationError')) + await delay(250) + throw new Error('DetailsNavigationUnhandledError') + })() + }, []) + + return ( + + Details Screen + + ) +} diff --git a/test/react-native/features/fixtures/scenario-launcher/src/scenarios/index.js b/test/react-native/features/fixtures/scenario-launcher/src/scenarios/index.js index 9fbb92d42..ea540f5be 100644 --- a/test/react-native/features/fixtures/scenario-launcher/src/scenarios/index.js +++ b/test/react-native/features/fixtures/scenario-launcher/src/scenarios/index.js @@ -71,3 +71,7 @@ export { UnhandledOverrideJsErrorScenario } from './UnhandledOverrideJsErrorScen export { FeatureFlagsScenario } from './FeatureFlagsScenario' export { FeatureFlagsNativeCrashScenario } from './FeatureFlagsNativeCrashScenario' export { NativeFeatureFlagsScenario } from './NativeFeatureFlagsScenario' + +// navigation.feature +export { ReactNavigationBreadcrumbsEnabledScenario } from './ReactNavigationBreadcrumbsEnabledScenario' +export { ReactNavigationBreadcrumbsDisabledScenario } from './ReactNavigationBreadcrumbsDisabledScenario' diff --git a/test/react-native/features/navigation.feature b/test/react-native/features/navigation.feature index 5ddcae29b..335427dfd 100644 --- a/test/react-native/features/navigation.feature +++ b/test/react-native/features/navigation.feature @@ -1,4 +1,4 @@ -@navigation +@navigation @skip_new_arch Feature: Navigation plugin features Scenario: Navigating screens causes breadcrumbs and context to be updated