Skip to content

Commit

Permalink
feat: apply upgrade patch in subdirectory if necessary (react-native-…
Browse files Browse the repository at this point in the history
…community#272)

* bug: apply update patch in subdirectory if necessary

* add tests instead of changing existing ones
  • Loading branch information
maxaggedon authored and thymikee committed Apr 6, 2019
1 parent 49352be commit 4d8cfe4
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// added for Jest inline snapshots to not use default Prettier config
module.exports = {
bracketSpacing: false,
trailingComma: "all"
}
109 changes: 89 additions & 20 deletions packages/cli/src/commands/upgrade/__tests__/upgrade.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,7 @@ 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"}',
});
}
return Promise.resolve({stdout: ''});
});
return module;
});
jest.mock('execa');
jest.mock(
'/project/root/node_modules/react-native/package.json',
() => ({name: 'react-native', version: '0.57.8'}),
Expand All @@ -34,6 +23,20 @@ jest.mock(
() => ({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);
Expand All @@ -52,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,
};
Expand All @@ -75,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(() => {
Expand All @@ -94,6 +117,22 @@ 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 when nested', 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(execa).toBeCalledWith('git', [
'apply',
'tmp-upgrade-rn.patch',
'--exclude=NestedApp/package.json',
'-p2',
'--3way',
'--directory=NestedApp/',
]);
});

test('errors when invalid version passed', async () => {
await upgrade.func(['next'], ctx, opts);
expect(logger.error).toBeCalledWith(
Expand Down Expand Up @@ -137,9 +176,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=package.json -p2 --3way --directory=
info Applying diff...
$ execa git apply tmp-upgrade-rn.patch --exclude=package.json -p2 --3way
$ 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...
Expand All @@ -158,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) => {
Expand All @@ -173,6 +238,9 @@ 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: ''});
}
return Promise.resolve({stdout: ''});
});
try {
Expand All @@ -185,9 +253,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=package.json -p2 --3way --directory=
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=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
Expand Down
23 changes: 20 additions & 3 deletions packages/cli/src/commands/upgrade/upgrade.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,24 @@ 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',
]);
try {
try {
const excludes = filesToExclude.map(e => `--exclude=${e}`);
const excludes = filesToExclude.map(
e => `--exclude=${path.join(relativePathFromRoot, e)}`,
);
await execa('git', [
'apply',
'--check',
tmpPatchFile,
...excludes,
'-p2',
'--3way',
`--directory=${relativePathFromRoot}`,
]);
logger.info('Applying diff...');
} catch (error) {
Expand All @@ -160,8 +168,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) {
Expand Down

0 comments on commit 4d8cfe4

Please sign in to comment.