Skip to content

Commit

Permalink
update unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh Edney committed Jan 30, 2024
1 parent ecf659b commit 9c61593
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 15 deletions.
53 changes: 42 additions & 11 deletions packages/react-native-cli/src/lib/Xcode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import xcode, { Project } from 'xcode'
const DOCS_LINK = 'https://docs.bugsnag.com/platforms/react-native/react-native/showing-full-stacktraces/#ios'
const UNLOCATED_PROJ_MSG = `The Xcode project was not in the expected location and so couldn't be updated automatically.
Update the "Bundle React Native Code And Images" build phase with the following environment variables:
export EXTRA_PACKAGER_ARGS="--sourcemap-output $TMPDIR/$(md5 -qs "$CONFIGURATION_BUILD_DIR")-main.jsbundle.map""
Please see ${DOCS_LINK} for more information`

See ${DOCS_LINK} for more information`
const EXTRA_PACKAGER_ARGS = ['"$(SRCROOT)/.xcode.env.local"', '"$(SRCROOT)/.xcode.env"']

export async function updateXcodeProject (projectRoot: string, endpoint: string|undefined, logger: Logger) {
const iosDir = path.join(projectRoot, 'ios')
Expand All @@ -32,8 +31,8 @@ export async function updateXcodeProject (projectRoot: string, endpoint: string|

const buildPhaseMap = proj?.hash?.project?.objects?.PBXShellScriptBuildPhase || []
logger.info('Ensuring React Native build phase outputs source maps')
const didUpdate = await updateXcodeEnv(iosDir, logger)

const didUpdate = await updateBuildReactNativeTask(buildPhaseMap, iosDir, logger)
logger.info('Adding build phase to upload source maps to Bugsnag')
const didAdd = await addUploadSourceMapsTask(proj, buildPhaseMap, endpoint, logger)

Expand All @@ -45,6 +44,31 @@ export async function updateXcodeProject (projectRoot: string, endpoint: string|
logger.success('Written changes to Xcode project')
}

async function updateBuildReactNativeTask (buildPhaseMap: Record<string, Record<string, unknown>>, iosDir: string, logger: Logger): Promise<boolean> {
let didAnythingUpdate = false
for (const shellBuildPhaseKey in buildPhaseMap) {
const phase = buildPhaseMap[shellBuildPhaseKey]
// The shell script can vary slightly... Vanilla RN projects contain
// ../node_modules/react-native/scripts/react-native-xcode.sh
// and ejected Expo projects contain
// `node --print "require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'"`
// so we need a little leniency
if (typeof phase.shellScript === 'string' && phase.shellScript.includes('/react-native-xcode.sh')) {
let didThisUpdate
[phase.inputPaths, didThisUpdate] = addExtraInputFiles(shellBuildPhaseKey, phase.inputPaths as string[], logger)
if (didThisUpdate) {
didAnythingUpdate = true
}
}
}

if (didAnythingUpdate) {
await updateXcodeEnv(iosDir, logger)
}

return didAnythingUpdate
}

async function addUploadSourceMapsTask (
proj: Project,
buildPhaseMap: Record<string, Record<string, unknown>>,
Expand Down Expand Up @@ -72,35 +96,42 @@ async function addUploadSourceMapsTask (
return true
}

function addExtraInputFiles (phaseId: string, existingInputFiles: string[], logger: Logger): [string[], boolean] {
if (arrayContainsElements(existingInputFiles, EXTRA_PACKAGER_ARGS)) {
logger.warn(`The "Bundle React Native Code and Images" build phase (${phaseId}) already includes the required arguments`)
return [existingInputFiles, false]
}
return [EXTRA_PACKAGER_ARGS.concat(existingInputFiles), true]
}

function arrayContainsElements (mainArray: any[], subArray: any[]): boolean {
return subArray.every(element => mainArray.some(mainElement => mainElement === element))
}

async function updateXcodeEnv (iosDir: string, logger: Logger): Promise<boolean> {
const searchString = 'SOURCEMAP_FILE='
const sourceMapFilePath = 'ios/build/sourcemaps/main.jsbundle.map'
const envFilePath = path.join(iosDir, '.xcode.env')

try {
// Check if the file exists
await fs.stat(envFilePath)
await fs.readFile(envFilePath)

// If the file exists, read its content
const xcodeEnvData = await fs.readFile(envFilePath, 'utf8')

if (xcodeEnvData.includes(searchString)) {
if (xcodeEnvData?.includes(searchString)) {
logger.warn(`The .xcode.env file already contains a section for "${searchString}"`)
return false
} else {
// Append the new data to the existing content
const newData = `${xcodeEnvData}\n\n# React Native Source Map File\nexport ${searchString}${sourceMapFilePath}`
await fs.writeFile(envFilePath, newData, 'utf8')
return true
}
} catch (error) {
// If the file doesn't exist, create it
if (error.code === 'ENOENT') {
const newData = `export NODE_BINARY=$(command -v node)\n# React Native Source Map File\nexport ${searchString}${sourceMapFilePath}`
await fs.writeFile(envFilePath, newData, 'utf8')
return true
} else {
// Other error occurred
console.error(`Error updating .xcode.env file: ${error.message}`)
return false
}
Expand Down
8 changes: 4 additions & 4 deletions packages/react-native-cli/src/lib/__test__/Xcode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ test('updateXcodeProject(): success', async () => {
await updateXcodeProject('/random/path', undefined, logger)

expect(readFileSyncMock).toHaveBeenCalledWith('/random/path/ios/BugsnagReactNativeCliTest.xcodeproj/project.pbxproj', 'utf8')
expect(writeFileMock).toHaveBeenCalledTimes(1)
expect(writeFileMock).toHaveBeenCalledTimes(2)

// the added build phase gets a generated build ID, so we need to figure out what that is before doing an exact string match
const matches = /([A-Z0-9]{24}) \/\* Upload source maps to Bugsnag \*\/ = \{/.exec(writeFileMock.mock.calls[0][1] as string)
const matches = /([A-Z0-9]{24}) \/\* Upload source maps to Bugsnag \*\/ = \{/.exec(writeFileMock.mock.calls[1] as unknown as string)
if (!matches) throw new Error('Failed to detect build ID')
const generatedPhaseId = matches[1]
const expectedOutput = (await loadFixture(path.join(__dirname, 'fixtures', 'project-after.pbxproj')))
Expand Down Expand Up @@ -86,10 +86,10 @@ test('updateXcodeProject(): success with custom endpoint', async () => {
await updateXcodeProject('/random/path', 'https://upload.example.com', logger)

expect(readFileSyncMock).toHaveBeenCalledWith('/random/path/ios/BugsnagReactNativeCliTest.xcodeproj/project.pbxproj', 'utf8')
expect(writeFileMock).toHaveBeenCalledTimes(1)
expect(writeFileMock).toHaveBeenCalledTimes(2)

// the added build phase gets a generated build ID, so we need to figure out what that is before doing an exact string match
const matches = /([A-Z0-9]{24}) \/\* Upload source maps to Bugsnag \*\/ = \{/.exec(writeFileMock.mock.calls[0][1] as string)
const matches = /([A-Z0-9]{24}) \/\* Upload source maps to Bugsnag \*\/ = \{/.exec(writeFileMock.mock.calls[1] as unknown as string)
if (!matches) {
throw new Error('Failed to detect build ID')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@
files = (
);
inputPaths = (
"$(SRCROOT)/.xcode.env.local",
"$(SRCROOT)/.xcode.env",
);
name = "Bundle React Native code and images";
outputPaths = (
Expand All @@ -384,6 +386,8 @@
files = (
);
inputPaths = (
"$(SRCROOT)/.xcode.env.local",
"$(SRCROOT)/.xcode.env",
);
name = "Bundle React Native Code And Images";
outputPaths = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@
files = (
);
inputPaths = (
"$(SRCROOT)/.xcode.env.local",
"$(SRCROOT)/.xcode.env",
);
name = "Bundle React Native code and images";
outputPaths = (
Expand All @@ -384,6 +386,8 @@
files = (
);
inputPaths = (
"$(SRCROOT)/.xcode.env.local",
"$(SRCROOT)/.xcode.env",
);
name = "Bundle React Native Code And Images";
outputPaths = (
Expand Down

0 comments on commit 9c61593

Please sign in to comment.