e2e-test: update to use new run utils from cli-common
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
|
||||
import { Command } from 'commander';
|
||||
import { run } from './run';
|
||||
import { runCommand } from './runCommand';
|
||||
|
||||
export function registerCommands(program: Command) {
|
||||
program
|
||||
.command('run')
|
||||
.option('--keep', 'Do not remove the temporary dir after tests complete')
|
||||
.description('Run e2e tests')
|
||||
.action(run);
|
||||
.action(runCommand);
|
||||
}
|
||||
|
||||
+20
-24
@@ -22,18 +22,12 @@ import killTree from 'tree-kill';
|
||||
import { resolve as resolvePath, join as joinPath } from 'path';
|
||||
import path from 'path';
|
||||
|
||||
import {
|
||||
spawnPiped,
|
||||
runPlain,
|
||||
waitFor,
|
||||
waitForExit,
|
||||
print,
|
||||
} from '../lib/helpers';
|
||||
import { waitFor, print } from '../lib/helpers';
|
||||
|
||||
import mysql from 'mysql2/promise';
|
||||
import pgtools from 'pgtools';
|
||||
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { findPaths, runOutput, run } from '@backstage/cli-common';
|
||||
import { OptionValues } from 'commander';
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
@@ -46,7 +40,7 @@ const templatePackagePaths = [
|
||||
'packages/create-app/templates/default-app/packages/backend/package.json.hbs',
|
||||
];
|
||||
|
||||
export async function run(opts: OptionValues) {
|
||||
export async function runCommand(opts: OptionValues) {
|
||||
const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-'));
|
||||
print(`CLI E2E test root: ${rootDir}\n`);
|
||||
|
||||
@@ -67,7 +61,7 @@ export async function run(opts: OptionValues) {
|
||||
await createPlugin({ appDir, pluginId, select: 'backend-plugin' });
|
||||
|
||||
print(`Running 'yarn test:e2e' in newly created app with new plugin`);
|
||||
await runPlain(['yarn', 'test:e2e'], {
|
||||
await runOutput(['yarn', 'test:e2e'], {
|
||||
cwd: appDir,
|
||||
env: { ...process.env, CI: undefined },
|
||||
});
|
||||
@@ -75,13 +69,13 @@ export async function run(opts: OptionValues) {
|
||||
await switchToReact17(appDir);
|
||||
|
||||
print(`Running 'yarn install' to install React 17`);
|
||||
await runPlain(['yarn', 'install'], { cwd: appDir });
|
||||
await runOutput(['yarn', 'install'], { cwd: appDir });
|
||||
|
||||
print(`Running 'yarn tsc' with React 17`);
|
||||
await runPlain(['yarn', 'tsc'], { cwd: appDir });
|
||||
await runOutput(['yarn', 'tsc'], { cwd: appDir });
|
||||
|
||||
print(`Running 'yarn test:e2e' with React 17`);
|
||||
await runPlain(['yarn', 'test:e2e'], {
|
||||
await runOutput(['yarn', 'test:e2e'], {
|
||||
cwd: appDir,
|
||||
env: { ...process.env, CI: undefined },
|
||||
});
|
||||
@@ -190,7 +184,7 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) {
|
||||
appendDeps(require('@backstage/create-app/package.json'));
|
||||
|
||||
print(`Preparing workspace`);
|
||||
await runPlain([
|
||||
await runOutput([
|
||||
'yarn',
|
||||
'backstage-cli',
|
||||
'build-workspace',
|
||||
@@ -209,7 +203,7 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) {
|
||||
}
|
||||
|
||||
print('Installing workspace dependencies');
|
||||
await runPlain(['yarn', 'workspaces', 'focus', '--all', '--production'], {
|
||||
await runOutput(['yarn', 'workspaces', 'focus', '--all', '--production'], {
|
||||
cwd: workspaceDir,
|
||||
});
|
||||
|
||||
@@ -257,7 +251,7 @@ async function createApp(
|
||||
workspaceDir: string,
|
||||
rootDir: string,
|
||||
) {
|
||||
const child = spawnPiped(
|
||||
const child = run(
|
||||
[
|
||||
'node',
|
||||
resolvePath(workspaceDir, 'packages/create-app/bin/backstage-create-app'),
|
||||
@@ -265,6 +259,7 @@ async function createApp(
|
||||
],
|
||||
{
|
||||
cwd: rootDir,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -278,7 +273,7 @@ async function createApp(
|
||||
child.stdin?.write(`${appName}\n`);
|
||||
|
||||
print('Waiting for app create script to be done');
|
||||
await waitForExit(child);
|
||||
await child.waitForExit();
|
||||
|
||||
const appDir = resolvePath(rootDir, appName);
|
||||
|
||||
@@ -318,7 +313,7 @@ async function createApp(
|
||||
'test:all',
|
||||
]) {
|
||||
print(`Running 'yarn ${cmd}' in newly created app`);
|
||||
await runPlain(['yarn', cmd], { cwd: appDir });
|
||||
await runOutput(['yarn', cmd], { cwd: appDir });
|
||||
}
|
||||
|
||||
return appDir;
|
||||
@@ -378,10 +373,11 @@ async function createPlugin(options: {
|
||||
select: string;
|
||||
}) {
|
||||
const { appDir, pluginId, select } = options;
|
||||
const child = spawnPiped(
|
||||
const child = run(
|
||||
['yarn', 'new', '--select', select, '--option', `pluginId=${pluginId}`],
|
||||
{
|
||||
cwd: appDir,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -392,7 +388,7 @@ async function createPlugin(options: {
|
||||
});
|
||||
|
||||
print('Waiting for plugin create script to be done');
|
||||
await waitForExit(child);
|
||||
await child.waitForExit();
|
||||
|
||||
const pluginDir = resolvePath(
|
||||
appDir,
|
||||
@@ -401,11 +397,11 @@ async function createPlugin(options: {
|
||||
);
|
||||
|
||||
print(`Running 'yarn tsc' in root for newly created plugin`);
|
||||
await runPlain(['yarn', 'tsc'], { cwd: appDir });
|
||||
await runOutput(['yarn', 'tsc'], { cwd: appDir });
|
||||
|
||||
for (const cmd of [['lint'], ['test', '--no-watch']]) {
|
||||
print(`Running 'yarn ${cmd.join(' ')}' in newly created plugin`);
|
||||
await runPlain(['yarn', ...cmd], { cwd: pluginDir });
|
||||
await runOutput(['yarn', ...cmd], { cwd: pluginDir });
|
||||
}
|
||||
} finally {
|
||||
child.kill();
|
||||
@@ -494,7 +490,7 @@ async function dropClientDatabases(client: string) {
|
||||
* Start serving the newly created backend and make sure that all db migrations works correctly
|
||||
*/
|
||||
async function testBackendStart(appDir: string, ...args: string[]) {
|
||||
const child = spawnPiped(['yarn', 'workspace', 'backend', 'start', ...args], {
|
||||
const child = run(['yarn', 'workspace', 'backend', 'start', ...args], {
|
||||
cwd: appDir,
|
||||
// Windows does not like piping stdin here, the child process will hang when requiring the 'process' module
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
@@ -586,7 +582,7 @@ async function testBackendStart(appDir: string, ...args: string[]) {
|
||||
}
|
||||
|
||||
try {
|
||||
await waitForExit(child);
|
||||
await child.waitForExit();
|
||||
} catch (error) {
|
||||
if (!successful) {
|
||||
throw new Error(`Backend failed to startup: ${stderr}`);
|
||||
@@ -18,7 +18,6 @@ import { program } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
import { registerCommands } from './commands';
|
||||
import { version } from '../package.json';
|
||||
import { exitWithError } from './lib/helpers';
|
||||
|
||||
async function main(argv: string[]) {
|
||||
program.name('e2e-test').version(version);
|
||||
@@ -36,4 +35,12 @@ async function main(argv: string[]) {
|
||||
program.parse(argv);
|
||||
}
|
||||
|
||||
main(process.argv).catch(exitWithError);
|
||||
main(process.argv).catch(err => {
|
||||
process.stdout.write(`${err.name}: ${err.stack || err.message}\n`);
|
||||
|
||||
if (typeof err.code === 'number') {
|
||||
process.exit(err.code);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,77 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import {
|
||||
spawn,
|
||||
execFile as execFileCb,
|
||||
SpawnOptions,
|
||||
ChildProcess,
|
||||
} from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const execFile = promisify(execFileCb);
|
||||
|
||||
export function spawnPiped(cmd: string[], options?: SpawnOptions) {
|
||||
function pipeWithPrefix(stream: NodeJS.WriteStream, prefix = '') {
|
||||
return (data: Buffer) => {
|
||||
const prefixedMsg = data
|
||||
.toString('utf8')
|
||||
.trimEnd()
|
||||
.replace(/^/gm, prefix);
|
||||
stream.write(`${prefixedMsg}\n`, 'utf8');
|
||||
};
|
||||
}
|
||||
|
||||
const child = spawn(cmd[0], cmd.slice(1), {
|
||||
stdio: 'pipe',
|
||||
shell: true,
|
||||
...options,
|
||||
});
|
||||
child.on('error', exitWithError);
|
||||
|
||||
const logPrefix = cmd.map(s => s.replace(/.+\//, '')).join(' ');
|
||||
child.stdout?.on(
|
||||
'data',
|
||||
pipeWithPrefix(process.stdout, `[${logPrefix}].out: `),
|
||||
);
|
||||
child.stderr?.on(
|
||||
'data',
|
||||
pipeWithPrefix(process.stderr, `[${logPrefix}].err: `),
|
||||
);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
export async function runPlain(cmd: string[], options?: SpawnOptions) {
|
||||
try {
|
||||
const { stdout } = await execFile(cmd[0], cmd.slice(1), {
|
||||
...options,
|
||||
shell: true,
|
||||
});
|
||||
return stdout.trim();
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
if (error.stdout) {
|
||||
process.stdout.write(error.stdout as Buffer);
|
||||
}
|
||||
if (error.stderr) {
|
||||
process.stderr.write(error.stderr as Buffer);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function exitWithError(err: Error & { code?: unknown }) {
|
||||
process.stdout.write(`${err.name}: ${err.stack || err.message}\n`);
|
||||
|
||||
if (typeof err.code === 'number') {
|
||||
process.exit(err.code);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for fn() to be true
|
||||
* Checks every 100ms
|
||||
@@ -108,22 +37,6 @@ export function waitFor(fn: () => boolean, maxSeconds: number = 120) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function waitForExit(child: ChildProcess) {
|
||||
if (child.exitCode !== null) {
|
||||
throw new Error(`Child already exited with code ${child.exitCode}`);
|
||||
}
|
||||
await new Promise<void>((resolve, reject) =>
|
||||
child.once('exit', code => {
|
||||
if (code) {
|
||||
reject(new Error(`Child exited with code ${code}`));
|
||||
} else {
|
||||
print('Child finished');
|
||||
resolve();
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function print(msg: string) {
|
||||
return process.stdout.write(`${msg}\n`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user