From d0cc3b4c201998ef70241d27cc8ea234664d1a5e Mon Sep 17 00:00:00 2001 From: Maxence Harm Date: Wed, 27 Mar 2019 01:40:46 +0100 Subject: [PATCH 1/2] bug: apply update patch in subdirectory if necessary --- .../upgrade/__tests__/upgrade.test.js | 37 ++++++++++++++++--- packages/cli/src/commands/upgrade/upgrade.js | 22 +++++++++-- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js b/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js index edef33b7d..4e938749e 100644 --- a/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js +++ b/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js @@ -20,17 +20,22 @@ jest.mock('execa', () => { stdout: '{"react": "16.6.3"}', }); } + if (command === 'git' && args[0] === 'rev-parse') { + return Promise.resolve({ + stdout: 'ReactNativeApp/', + }); + } return Promise.resolve({stdout: ''}); }); return module; }); jest.mock( - '/project/root/node_modules/react-native/package.json', + '/project/root/ReactNativeApp/node_modules/react-native/package.json', () => ({name: 'react-native', version: '0.57.8'}), {virtual: true}, ); jest.mock( - '/project/root/package.json', + '/project/root/ReactNativeApp/package.json', () => ({name: 'TestApp', dependencies: {'react-native': '^0.57.8'}}), {virtual: true}, ); @@ -94,6 +99,19 @@ test('uses latest version of react-native when none passed', async () => { expect(execa).toBeCalledWith('npm', ['info', 'react-native', 'version']); }, 60000); +test('applies patch in current working directory', async () => { + (fetch: any).mockImplementation(() => Promise.resolve(samplePatch)); + await upgrade.func([newVersion], ctx, opts); + expect(execa).toBeCalledWith('git', [ + 'apply', + 'tmp-upgrade-rn.patch', + '--exclude=ReactNativeApp/package.json', + '-p2', + '--3way', + '--directory=ReactNativeApp/', + ]); +}); + test('errors when invalid version passed', async () => { await upgrade.func(['next'], ctx, opts); expect(logger.error).toBeCalledWith( @@ -137,9 +155,10 @@ test('fetches regular patch, adds remote, applies patch, installs deps, removes expect(flushOutput()).toMatchInlineSnapshot(` "info Fetching diff between v0.57.8 and v0.58.4... [fs] write tmp-upgrade-rn.patch -$ execa git apply --check tmp-upgrade-rn.patch --exclude=package.json -p2 --3way +$ execa git rev-parse --show-prefix +$ execa git apply --check tmp-upgrade-rn.patch --exclude=ReactNativeApp/package.json -p2 --3way --directory=ReactNativeApp/ info Applying diff... -$ execa git apply tmp-upgrade-rn.patch --exclude=package.json -p2 --3way +$ execa git apply tmp-upgrade-rn.patch --exclude=ReactNativeApp/package.json -p2 --3way --directory=ReactNativeApp/ [fs] unlink tmp-upgrade-rn.patch $ execa git status -s info Installing \\"react-native@0.58.4\\" and its peer dependencies... @@ -173,6 +192,11 @@ test('cleans up if patching fails,', async () => { stderr: 'error: .flowconfig: does not exist in index\n', }); } + if (command === 'git' && args[0] === 'rev-parse') { + return Promise.resolve({ + stdout: 'ReactNativeApp/', + }); + } return Promise.resolve({stdout: ''}); }); try { @@ -185,9 +209,10 @@ test('cleans up if patching fails,', async () => { expect(flushOutput()).toMatchInlineSnapshot(` "info Fetching diff between v0.57.8 and v0.58.4... [fs] write tmp-upgrade-rn.patch -$ execa git apply --check tmp-upgrade-rn.patch --exclude=package.json -p2 --3way +$ execa git rev-parse --show-prefix +$ execa git apply --check tmp-upgrade-rn.patch --exclude=ReactNativeApp/package.json -p2 --3way --directory=ReactNativeApp/ info Applying diff (excluding: package.json, .flowconfig)... -$ execa git apply tmp-upgrade-rn.patch --exclude=package.json --exclude=.flowconfig -p2 --3way +$ execa git apply tmp-upgrade-rn.patch --exclude=ReactNativeApp/package.json --exclude=ReactNativeApp/.flowconfig -p2 --3way --directory=ReactNativeApp/ error: .flowconfig: does not exist in index error Automatically applying diff failed [fs] unlink tmp-upgrade-rn.patch diff --git a/packages/cli/src/commands/upgrade/upgrade.js b/packages/cli/src/commands/upgrade/upgrade.js index 816e6cdd0..57e065307 100644 --- a/packages/cli/src/commands/upgrade/upgrade.js +++ b/packages/cli/src/commands/upgrade/upgrade.js @@ -137,9 +137,15 @@ const applyPatch = async ( tmpPatchFile: string, ) => { let filesToExclude = ['package.json']; + const {stdout: relativePathFromRoot} = await execa('git', [ + 'rev-parse', + '--show-prefix', + ]); try { try { - const excludes = filesToExclude.map(e => `--exclude=${e}`); + const excludes = filesToExclude.map( + e => `--exclude=${path.join(relativePathFromRoot, e)}`, + ); await execa('git', [ 'apply', '--check', @@ -147,6 +153,7 @@ const applyPatch = async ( ...excludes, '-p2', '--3way', + `--directory=${relativePathFromRoot}`, ]); logger.info('Applying diff...'); } catch (error) { @@ -160,8 +167,17 @@ const applyPatch = async ( logger.info(`Applying diff (excluding: ${filesToExclude.join(', ')})...`); } finally { - const excludes = filesToExclude.map(e => `--exclude=${e}`); - await execa('git', ['apply', tmpPatchFile, ...excludes, '-p2', '--3way']); + const excludes = filesToExclude.map( + e => `--exclude=${path.join(relativePathFromRoot, e)}`, + ); + await execa('git', [ + 'apply', + tmpPatchFile, + ...excludes, + '-p2', + '--3way', + `--directory=${relativePathFromRoot}`, + ]); } } catch (error) { if (error.stderr) { From 9f21fd52928bba9d0969e36107b7fda6ff2adf3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sat, 6 Apr 2019 20:30:44 +0200 Subject: [PATCH 2/2] add tests instead of changing existing ones --- .prettierrc.js | 5 + .../upgrade/__tests__/upgrade.test.js | 112 ++++++++++++------ packages/cli/src/commands/upgrade/upgrade.js | 1 + 3 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 .prettierrc.js diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..50e441707 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,5 @@ +// added for Jest inline snapshots to not use default Prettier config +module.exports = { + bracketSpacing: false, + trailingComma: "all" +} diff --git a/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js b/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js index 4e938749e..d66a4bb37 100644 --- a/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js +++ b/packages/cli/src/commands/upgrade/__tests__/upgrade.test.js @@ -12,33 +12,31 @@ import loadConfig from '../../../tools/config'; jest.mock('https'); jest.mock('fs'); jest.mock('path'); -jest.mock('execa', () => { - const module = jest.fn((command, args) => { - mockPushLog('$', 'execa', command, args); - if (command === 'npm' && args[3] === '--json') { - return Promise.resolve({ - stdout: '{"react": "16.6.3"}', - }); - } - if (command === 'git' && args[0] === 'rev-parse') { - return Promise.resolve({ - stdout: 'ReactNativeApp/', - }); - } - return Promise.resolve({stdout: ''}); - }); - return module; -}); +jest.mock('execa'); jest.mock( - '/project/root/ReactNativeApp/node_modules/react-native/package.json', + '/project/root/node_modules/react-native/package.json', () => ({name: 'react-native', version: '0.57.8'}), {virtual: true}, ); jest.mock( - '/project/root/ReactNativeApp/package.json', + '/project/root/package.json', () => ({name: 'TestApp', dependencies: {'react-native': '^0.57.8'}}), {virtual: true}, ); +jest.mock( + '/project/root/NestedApp/node_modules/react-native/package.json', + () => ({name: 'react-native', version: '0.57.8'}), + {virtual: true}, +); +jest.mock( + '/project/root/NestedApp/package.json', + () => ({ + name: 'TestAppNested', + dependencies: {'react-native': '^0.57.8'}, + }), + {virtual: true}, +); +jest.mock('../../../tools/config'); jest.mock('../../../tools/packageManager', () => ({ install: args => { mockPushLog('$ yarn add', ...args); @@ -57,14 +55,32 @@ jest.mock('@react-native-community/cli-tools', () => ({ }, })); +const mockExecaDefault = (command, args) => { + mockPushLog('$', 'execa', command, args); + if (command === 'npm' && args[3] === '--json') { + return Promise.resolve({stdout: '{"react": "16.6.3"}'}); + } + if (command === 'git' && args[0] === 'rev-parse') { + return Promise.resolve({stdout: ''}); + } + return Promise.resolve({stdout: ''}); +}; + +const mockExecaNested = (command, args) => { + mockPushLog('$', 'execa', command, args); + if (command === 'npm' && args[3] === '--json') { + return Promise.resolve({stdout: '{"react": "16.6.3"}'}); + } + if (command === 'git' && args[0] === 'rev-parse') { + return Promise.resolve({stdout: 'NestedApp/'}); + } + return Promise.resolve({stdout: ''}); +}; + const currentVersion = '0.57.8'; const newVersion = '0.58.4'; const olderVersion = '0.56.0'; - -jest.mock('../../../tools/config'); - const ctx = loadConfig(); - const opts = { legacy: false, }; @@ -80,11 +96,13 @@ const flushOutput = () => stripAnsi(logs.join('\n')); beforeEach(() => { jest.clearAllMocks(); + jest.restoreAllMocks(); // $FlowFixMe fs.writeFileSync = jest.fn(filename => mockPushLog('[fs] write', filename)); // $FlowFixMe fs.unlinkSync = jest.fn((...args) => mockPushLog('[fs] unlink', args)); logs = []; + (execa: any).mockImplementation(mockExecaDefault); }); afterEach(() => { @@ -99,16 +117,19 @@ test('uses latest version of react-native when none passed', async () => { expect(execa).toBeCalledWith('npm', ['info', 'react-native', 'version']); }, 60000); -test('applies patch in current working directory', async () => { +test('applies patch in current working directory when nested', async () => { (fetch: any).mockImplementation(() => Promise.resolve(samplePatch)); - await upgrade.func([newVersion], ctx, opts); + (execa: any).mockImplementation(mockExecaNested); + const config = {...ctx, root: '/project/root/NestedApp'}; + await upgrade.func([newVersion], config, opts); + expect(execa).toBeCalledWith('git', [ 'apply', 'tmp-upgrade-rn.patch', - '--exclude=ReactNativeApp/package.json', + '--exclude=NestedApp/package.json', '-p2', '--3way', - '--directory=ReactNativeApp/', + '--directory=NestedApp/', ]); }); @@ -156,9 +177,9 @@ test('fetches regular patch, adds remote, applies patch, installs deps, removes "info Fetching diff between v0.57.8 and v0.58.4... [fs] write tmp-upgrade-rn.patch $ execa git rev-parse --show-prefix -$ execa git apply --check tmp-upgrade-rn.patch --exclude=ReactNativeApp/package.json -p2 --3way --directory=ReactNativeApp/ +$ execa git apply --check tmp-upgrade-rn.patch --exclude=package.json -p2 --3way --directory= info Applying diff... -$ execa git apply tmp-upgrade-rn.patch --exclude=ReactNativeApp/package.json -p2 --3way --directory=ReactNativeApp/ +$ execa git apply tmp-upgrade-rn.patch --exclude=package.json -p2 --3way --directory= [fs] unlink tmp-upgrade-rn.patch $ execa git status -s info Installing \\"react-native@0.58.4\\" and its peer dependencies... @@ -177,6 +198,31 @@ success Upgraded React Native to v0.58.4 🎉. Now you can review and commit the }), ).toMatchSnapshot('RnDiffApp is replaced with app name (TestApp)'); }, 60000); +test('fetches regular patch, adds remote, applies patch, installs deps, removes remote when updated from nested directory', async () => { + (fetch: any).mockImplementation(() => Promise.resolve(samplePatch)); + (execa: any).mockImplementation(mockExecaNested); + const config = {...ctx, root: '/project/root/NestedApp'}; + await upgrade.func([newVersion], config, opts); + expect(flushOutput()).toMatchInlineSnapshot(` +"info Fetching diff between v0.57.8 and v0.58.4... +[fs] write tmp-upgrade-rn.patch +$ execa git rev-parse --show-prefix +$ execa git apply --check tmp-upgrade-rn.patch --exclude=NestedApp/package.json -p2 --3way --directory=NestedApp/ +info Applying diff... +$ execa git apply tmp-upgrade-rn.patch --exclude=NestedApp/package.json -p2 --3way --directory=NestedApp/ +[fs] unlink tmp-upgrade-rn.patch +$ execa git status -s +info Installing \\"react-native@0.58.4\\" and its peer dependencies... +$ execa npm info react-native@0.58.4 peerDependencies --json +$ yarn add react-native@0.58.4 react@16.6.3 +$ execa git add package.json +$ execa git add yarn.lock +$ execa git add package-lock.json +info Running \\"git status\\" to check what changed... +$ execa git status +success Upgraded React Native to v0.58.4 🎉. Now you can review and commit the changes" +`); +}, 60000); test('cleans up if patching fails,', async () => { (fetch: any).mockImplementation(() => Promise.resolve(samplePatch)); (execa: any).mockImplementation((command, args) => { @@ -193,9 +239,7 @@ test('cleans up if patching fails,', async () => { }); } if (command === 'git' && args[0] === 'rev-parse') { - return Promise.resolve({ - stdout: 'ReactNativeApp/', - }); + return Promise.resolve({stdout: ''}); } return Promise.resolve({stdout: ''}); }); @@ -210,9 +254,9 @@ test('cleans up if patching fails,', async () => { "info Fetching diff between v0.57.8 and v0.58.4... [fs] write tmp-upgrade-rn.patch $ execa git rev-parse --show-prefix -$ execa git apply --check tmp-upgrade-rn.patch --exclude=ReactNativeApp/package.json -p2 --3way --directory=ReactNativeApp/ +$ execa git apply --check tmp-upgrade-rn.patch --exclude=package.json -p2 --3way --directory= info Applying diff (excluding: package.json, .flowconfig)... -$ execa git apply tmp-upgrade-rn.patch --exclude=ReactNativeApp/package.json --exclude=ReactNativeApp/.flowconfig -p2 --3way --directory=ReactNativeApp/ +$ execa git apply tmp-upgrade-rn.patch --exclude=package.json --exclude=.flowconfig -p2 --3way --directory= error: .flowconfig: does not exist in index error Automatically applying diff failed [fs] unlink tmp-upgrade-rn.patch diff --git a/packages/cli/src/commands/upgrade/upgrade.js b/packages/cli/src/commands/upgrade/upgrade.js index 57e065307..e5f153753 100644 --- a/packages/cli/src/commands/upgrade/upgrade.js +++ b/packages/cli/src/commands/upgrade/upgrade.js @@ -137,6 +137,7 @@ const applyPatch = async ( tmpPatchFile: string, ) => { let filesToExclude = ['package.json']; + // $FlowFixMe ThenableChildProcess is incompatible with Promise const {stdout: relativePathFromRoot} = await execa('git', [ 'rev-parse', '--show-prefix',