-
Notifications
You must be signed in to change notification settings - Fork 905
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: setup e2e tests * add flowfixmes * use test.each * add docs to install/uninstall * remove dead code
- Loading branch information
Showing
10 changed files
with
848 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,6 +63,3 @@ unclear-type | |
unsafe-getters-setters | ||
untyped-import | ||
untyped-type-import | ||
|
||
[version] | ||
^0.94.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// @flow | ||
import path from 'path'; | ||
import {run, getTempDirectory, cleanup, writeFiles} from '../helpers'; | ||
|
||
const DIR = getTempDirectory('command-install-test'); | ||
const pkg = 'react-native-config'; | ||
|
||
beforeEach(() => { | ||
cleanup(DIR); | ||
writeFiles(DIR, { | ||
'node_modules/react-native/package.json': '{}', | ||
'package.json': '{}', | ||
}); | ||
}); | ||
afterEach(() => cleanup(DIR)); | ||
|
||
test.each(['yarn', 'npm'])('install module with %s', pm => { | ||
if (pm === 'yarn') { | ||
writeFiles(DIR, {'yarn.lock': ''}); | ||
} | ||
const {stdout, code} = run(DIR, ['install', pkg]); | ||
|
||
expect(stdout).toContain(`Installing "${pkg}"`); | ||
expect(stdout).toContain(`Linking "${pkg}"`); | ||
// TODO – this behavior is a bug, linking should fail/warn without native deps | ||
// to link. Not a high priority since we're changing how link works | ||
expect(stdout).toContain(`Successfully installed and linked "${pkg}"`); | ||
expect(require(path.join(DIR, 'package.json'))).toMatchObject({ | ||
dependencies: { | ||
[pkg]: expect.any(String), | ||
}, | ||
}); | ||
expect(code).toBe(0); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// @flow | ||
import {run, getTempDirectory, cleanup, writeFiles} from '../helpers'; | ||
|
||
const DIR = getTempDirectory('command-uninstall-test'); | ||
const pkg = 'react-native-config'; | ||
|
||
beforeEach(() => { | ||
cleanup(DIR); | ||
writeFiles(DIR, { | ||
'node_modules/react-native/package.json': '{}', | ||
'node_modules/react-native-config/package.json': '{}', | ||
'package.json': `{ | ||
"dependencies": { | ||
"react-native-config": "*" | ||
} | ||
}`, | ||
}); | ||
}); | ||
afterEach(() => cleanup(DIR)); | ||
|
||
test('uninstall fails when package is not defined', () => { | ||
writeFiles(DIR, { | ||
'package.json': `{ | ||
"dependencies": {} | ||
}`, | ||
}); | ||
const {stderr, code} = run(DIR, ['uninstall']); | ||
|
||
expect(stderr).toContain('missing required argument'); | ||
expect(code).toBe(1); | ||
}); | ||
|
||
test('uninstall fails when package is not installed', () => { | ||
writeFiles(DIR, { | ||
'package.json': `{ | ||
"dependencies": {} | ||
}`, | ||
}); | ||
const {stderr, code} = run(DIR, ['uninstall', pkg]); | ||
|
||
expect(stderr).toContain(`Project "${pkg}" is not a react-native library`); | ||
expect(code).toBe(1); | ||
}); | ||
|
||
test.each(['yarn', 'npm'])('uninstall module with %s', pm => { | ||
if (pm === 'yarn') { | ||
writeFiles(DIR, {'yarn.lock': ''}); | ||
} | ||
const {stdout, code} = run(DIR, ['uninstall', pkg]); | ||
|
||
expect(stdout).toContain(`Unlinking "${pkg}"`); | ||
expect(stdout).toContain(`Uninstalling "${pkg}"`); | ||
expect(stdout).toContain(`Successfully uninstalled and unlinked "${pkg}"`); | ||
expect(code).toBe(0); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// @flow | ||
import fs from 'fs'; | ||
import os from 'os'; | ||
import path from 'path'; | ||
import {createDirectory} from 'jest-util'; | ||
import rimraf from 'rimraf'; | ||
import execa from 'execa'; | ||
import {Writable} from 'readable-stream'; | ||
|
||
const CLI_PATH = path.resolve(__dirname, '../packages/cli/build/bin.js'); | ||
|
||
type RunOptions = { | ||
nodeOptions?: string, | ||
nodePath?: string, | ||
timeout?: number, // kill the process after X milliseconds | ||
}; | ||
|
||
export function run( | ||
dir: string, | ||
args?: Array<string>, | ||
options: RunOptions = {}, | ||
) { | ||
return spawnCli(dir, args, options); | ||
} | ||
|
||
// Runs cli until a given output is achieved, then kills it with `SIGTERM` | ||
export async function runUntil( | ||
dir: string, | ||
args: Array<string> | void, | ||
text: string, | ||
options: RunOptions = {}, | ||
) { | ||
const spawnPromise = spawnCliAsync(dir, args, {timeout: 30000, ...options}); | ||
|
||
spawnPromise.stderr.pipe( | ||
new Writable({ | ||
write(chunk, _encoding, callback) { | ||
const output = chunk.toString('utf8'); | ||
|
||
if (output.includes(text)) { | ||
spawnPromise.kill(); | ||
} | ||
|
||
callback(); | ||
}, | ||
}), | ||
); | ||
|
||
return spawnPromise; | ||
} | ||
|
||
export const makeTemplate = ( | ||
str: string, | ||
): ((values?: Array<any>) => string) => (values?: Array<any>) => | ||
str.replace(/\$(\d+)/g, (_match, number) => { | ||
if (!Array.isArray(values)) { | ||
throw new Error('Array of values must be passed to the template.'); | ||
} | ||
return values[number - 1]; | ||
}); | ||
|
||
export const cleanup = (directory: string) => rimraf.sync(directory); | ||
|
||
/** | ||
* Creates a nested directory with files and their contents | ||
* writeFiles( | ||
* '/home/tmp', | ||
* { | ||
* 'package.json': '{}', | ||
* 'dir/file.js': 'module.exports = "x";', | ||
* } | ||
* ); | ||
*/ | ||
export const writeFiles = ( | ||
directory: string, | ||
files: {[filename: string]: string}, | ||
) => { | ||
createDirectory(directory); | ||
Object.keys(files).forEach(fileOrPath => { | ||
const dirname = path.dirname(fileOrPath); | ||
|
||
if (dirname !== '/') { | ||
createDirectory(path.join(directory, dirname)); | ||
} | ||
fs.writeFileSync( | ||
path.resolve(directory, ...fileOrPath.split('/')), | ||
files[fileOrPath], | ||
); | ||
}); | ||
}; | ||
|
||
export const copyDir = (src: string, dest: string) => { | ||
const srcStat = fs.lstatSync(src); | ||
if (srcStat.isDirectory()) { | ||
if (!fs.existsSync(dest)) { | ||
fs.mkdirSync(dest); | ||
} | ||
fs.readdirSync(src).map(filePath => | ||
copyDir(path.join(src, filePath), path.join(dest, filePath)), | ||
); | ||
} else { | ||
fs.writeFileSync(dest, fs.readFileSync(src)); | ||
} | ||
}; | ||
|
||
export const getTempDirectory = (name: string) => | ||
path.resolve(os.tmpdir(), name); | ||
|
||
function spawnCli(dir: string, args?: Array<string>, options: RunOptions = {}) { | ||
const {spawnArgs, spawnOptions} = getCliArguments({dir, args, options}); | ||
|
||
return execa.sync(process.execPath, spawnArgs, spawnOptions); | ||
} | ||
|
||
function spawnCliAsync( | ||
dir: string, | ||
args?: Array<string>, | ||
options: RunOptions = {}, | ||
) { | ||
const {spawnArgs, spawnOptions} = getCliArguments({dir, args, options}); | ||
|
||
return execa(process.execPath, spawnArgs, spawnOptions); | ||
} | ||
|
||
function getCliArguments({dir, args, options}) { | ||
const isRelative = !path.isAbsolute(dir); | ||
|
||
if (isRelative) { | ||
dir = path.resolve(__dirname, dir); | ||
} | ||
|
||
const env = Object.assign({}, process.env, {FORCE_COLOR: '0'}); | ||
|
||
if (options.nodeOptions) { | ||
env.NODE_OPTIONS = options.nodeOptions; | ||
} | ||
if (options.nodePath) { | ||
env.NODE_PATH = options.nodePath; | ||
} | ||
|
||
const spawnArgs = [CLI_PATH, ...(args || [])]; | ||
const spawnOptions = { | ||
cwd: dir, | ||
env, | ||
reject: false, | ||
timeout: options.timeout || 0, | ||
}; | ||
return {spawnArgs, spawnOptions}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module.exports = { | ||
testEnvironment: 'node', | ||
testPathIgnorePatterns: ['<rootDir>/(?:.+?)/__tests__/'], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// flow-typed signature: 613ee1ec7d728b6a312fcff21a7b2669 | ||
// flow-typed version: 3163f7a6e3/execa_v1.0.x/flow_>=v0.75.x | ||
|
||
declare module 'execa' { | ||
|
||
declare type StdIoOption = | ||
| 'pipe' | ||
| 'ipc' | ||
| 'ignore' | ||
| 'inherit' | ||
| stream$Stream | ||
| number; | ||
|
||
declare type CommonOptions = {| | ||
argv0?: string, | ||
cleanup?: boolean, | ||
cwd?: string, | ||
detached?: boolean, | ||
encoding?: string, | ||
env?: {[string]: string}, | ||
extendEnv?: boolean, | ||
gid?: number, | ||
killSignal?: string | number, | ||
localDir?: string, | ||
maxBuffer?: number, | ||
preferLocal?: boolean, | ||
reject?: boolean, | ||
shell?: boolean | string, | ||
stderr?: ?StdIoOption, | ||
stdin?: ?StdIoOption, | ||
stdio?: 'pipe' | 'ignore' | 'inherit' | $ReadOnlyArray<?StdIoOption>, | ||
stdout?: ?StdIoOption, | ||
stripEof?: boolean, | ||
timeout?: number, | ||
uid?: number, | ||
windowsVerbatimArguments?: boolean, | ||
|}; | ||
|
||
declare type SyncOptions = {| | ||
...CommonOptions, | ||
input?: string | Buffer, | ||
|}; | ||
|
||
declare type Options = {| | ||
...CommonOptions, | ||
input?: string | Buffer | stream$Readable, | ||
|}; | ||
|
||
declare type SyncResult = {| | ||
stdout: string, | ||
stderr: string, | ||
code: number, | ||
failed: boolean, | ||
signal: ?string, | ||
cmd: string, | ||
timedOut: boolean, | ||
|}; | ||
|
||
declare type Result = {| | ||
...SyncResult, | ||
killed: boolean, | ||
|}; | ||
|
||
declare interface ThenableChildProcess extends child_process$ChildProcess { | ||
then<R, E>( | ||
onfulfilled?: ?((value: Result) => R | Promise<R>), | ||
onrejected?: ?((reason: ExecaError) => E | Promise<E>), | ||
): Promise<R | E>; | ||
|
||
catch<E>( | ||
onrejected?: ?((reason: ExecaError) => E | Promise<E>) | ||
): Promise<Result | E>; | ||
} | ||
|
||
declare interface ExecaError extends ErrnoError { | ||
stdout: string; | ||
stderr: string; | ||
failed: boolean; | ||
signal: ?string; | ||
cmd: string; | ||
timedOut: boolean; | ||
} | ||
|
||
declare interface Execa { | ||
(file: string, args?: $ReadOnlyArray<string>, options?: $ReadOnly<Options>): ThenableChildProcess; | ||
(file: string, options?: $ReadOnly<Options>): ThenableChildProcess; | ||
|
||
stdout(file: string, args?: $ReadOnlyArray<string>, options?: $ReadOnly<Options>): Promise<string>; | ||
stdout(file: string, options?: $ReadOnly<Options>): Promise<string>; | ||
|
||
stderr(file: string, args?: $ReadOnlyArray<string>, options?: $ReadOnly<Options>): Promise<string>; | ||
stderr(file: string, options?: $ReadOnly<Options>): Promise<string>; | ||
|
||
shell(command: string, options?: $ReadOnly<Options>): ThenableChildProcess; | ||
|
||
sync(file: string, args?: $ReadOnlyArray<string>, options?: $ReadOnly<SyncOptions>): SyncResult; | ||
sync(file: string, options?: $ReadOnly<SyncOptions>): SyncResult; | ||
|
||
shellSync(command: string, options?: $ReadOnly<Options>): SyncResult; | ||
} | ||
|
||
declare module.exports: Execa; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.