From 1b79344219ed079399c3d1c1aa765c17f86578ff Mon Sep 17 00:00:00 2001 From: Stefanos Mousafeiris Date: Mon, 29 Apr 2024 17:07:38 +0300 Subject: [PATCH] chore(starter): Test CLI more thoroughly for dep installation failure (#1207) We had an issue with some peer deps not being marked as optional that started causing the npm install to fail (see https://github.com/electric-sql/electric/pull/1205). Despite that because the starter cli does not return a non zero exit code if it fails at the dep installation phase, some tests were passing - I've modified them to check explicitly that the dep installation error is not shown. Until we upgrade the electric-sql package with the optional peer dep flag, I've added the `--legacy-peer-deps` to `npm install` calls to work around this issue. I will roll this back immediately after release after confirming that the individual examples work correctly as well. --- .changeset/lazy-rocks-switch.md | 5 ++ .../starter/e2e/create-project.e2e.test.js | 63 +++++++++---------- examples/starter/e2e/test-utils.js | 6 +- examples/starter/src/file-utils.ts | 16 +++-- 4 files changed, 48 insertions(+), 42 deletions(-) create mode 100644 .changeset/lazy-rocks-switch.md diff --git a/.changeset/lazy-rocks-switch.md b/.changeset/lazy-rocks-switch.md new file mode 100644 index 0000000000..20bc4a84b2 --- /dev/null +++ b/.changeset/lazy-rocks-switch.md @@ -0,0 +1,5 @@ +--- +"create-electric-app": patch +--- + +Install with `--legacy-peer-deps` to circumvent temporary issue with `electric-sql` package. diff --git a/examples/starter/e2e/create-project.e2e.test.js b/examples/starter/e2e/create-project.e2e.test.js index e0b6fc6ba6..13eb834369 100644 --- a/examples/starter/e2e/create-project.e2e.test.js +++ b/examples/starter/e2e/create-project.e2e.test.js @@ -19,6 +19,19 @@ const testAppDisplayName = 'Test App' const testAppDir = path.join(tempDir, testAppName) const envFilePath = path.join(testAppDir, '.env.local') +async function runCli(t, cliArgs) { + return await t.notThrowsAsync(() => + runCommand( + `npx create-electric-app ${testAppName} ${cliArgs}`, + tempDir, + [], + (output) => { + t.notRegex(output, /Could not install project dependencies./) + }, + ), + ) +} + async function assertPackageJson(t) { const packageJsonPath = path.join(testAppDir, 'package.json') @@ -62,34 +75,19 @@ test.serial.afterEach.always(async () => { }) test.serial('should create React project', async (t) => { - await t.notThrowsAsync(() => - runCommand(`npx create-electric-app ${testAppName}`, tempDir), - ) - + await runCli(t, '') await assertPackageJson(t) await assertEnvFile(t) }) test.serial('should create Vue.js project', async (t) => { - await t.notThrowsAsync(() => - runCommand( - `npx create-electric-app ${testAppName} --template vue`, - tempDir, - ), - ) - + await runCli(t, '--template vue') await assertPackageJson(t) await assertEnvFile(t) }) test.serial('should create Expo project', async (t) => { - await t.notThrowsAsync(() => - runCommand( - `npx create-electric-app ${testAppName} --template expo`, - tempDir, - ), - ) - + await runCli(t, '--template expo') await assertPackageJson(t) await assertEnvFile(t) @@ -101,12 +99,7 @@ test.serial('should create Expo project', async (t) => { }) test.serial('should create React Native project', async (t) => { - await t.notThrowsAsync(() => - runCommand( - `npx create-electric-app ${testAppName} --template react-native`, - tempDir, - ), - ) + await runCli(t, '--template react-native') await assertPackageJson(t) await assertEnvFile(t) @@ -128,23 +121,23 @@ test.serial('should create React Native project', async (t) => { test.serial('should set environment variables for project', async (t) => { const electricPort = 1234 const electricProxyPort = 12345 - await t.notThrowsAsync(() => - runCommand( - `npx create-electric-app ${testAppName} --electric-port ${electricPort} --electric-proxy-port ${electricProxyPort}`, - tempDir, - ), + await runCli( + t, + `--electric-port ${electricPort} --electric-proxy-port ${electricProxyPort}`, ) await assertEnvFile(t, electricPort, electricProxyPort) }) test.serial('should be able to use interactive prompt', async (t) => { await t.notThrowsAsync(() => - runCommand(`npx create-electric-app`, tempDir, [ - testAppName, - 'react', - '1234', - '12345', - ]), + runCommand( + `npx create-electric-app`, + tempDir, + [testAppName, 'react', '1234', '12345'], + (output) => { + t.notRegex(output, /Could not install project dependencies./) + }, + ), ) await assertPackageJson(t) diff --git a/examples/starter/e2e/test-utils.js b/examples/starter/e2e/test-utils.js index 5552ed70db..e7445f420d 100644 --- a/examples/starter/e2e/test-utils.js +++ b/examples/starter/e2e/test-utils.js @@ -1,7 +1,7 @@ import { spawn } from 'child_process' import * as fs from 'fs/promises' -export function runCommand(command, cwd, inputArgs = []) { +export function runCommand(command, cwd, inputArgs = [], outputListener) { const inputs = [...inputArgs] return new Promise((res, rej) => { const proc = spawn(command, [], { @@ -18,6 +18,9 @@ export function runCommand(command, cwd, inputArgs = []) { let timer = null proc.stdin.setEncoding('utf-8') proc.stdout.on('data', (data) => { + if (outputListener) { + outputListener(Buffer.from(data).toString()) + } if (inputs.length > 0) { if (timer === null) { console.log('Received:', Buffer.from(data).toString()) @@ -42,6 +45,7 @@ export function runCommand(command, cwd, inputArgs = []) { res() } else { const errStr = Buffer.concat(errors).toString() + console.error(errStr) rej(errStr) } }) diff --git a/examples/starter/src/file-utils.ts b/examples/starter/src/file-utils.ts index 24afb7cde1..bb6b92f7d6 100644 --- a/examples/starter/src/file-utils.ts +++ b/examples/starter/src/file-utils.ts @@ -212,11 +212,15 @@ export async function installDependencies(projectDir: string): Promise { // Run `npm install` in the project directory to install the dependencies // Also run `npm upgrade` to replace `electric-sql: latest` by `electric-sql: x.y.z` // where `x.y.z` corresponds to the latest version. - const proc = spawn('npm install && npm upgrade --caret electric-sql', [], { - stdio: ['ignore', 'ignore', 'pipe'], - cwd: projectDir, - shell: true, - }) + const proc = spawn( + 'npm install --legacy-peer-deps && npm upgrade --legacy-peer-deps --caret electric-sql', + [], + { + stdio: ['ignore', 'ignore', 'pipe'], + cwd: projectDir, + shell: true, + }, + ) let errors: Uint8Array[] = [] proc.stderr.on('data', (data) => { @@ -247,7 +251,7 @@ export async function regenerateReactNativePlatformProjects( // recreate a react native project from scratch await new Promise((res, rej) => { const proc = spawn( - `npm install -D react-native-eject && npx react-native eject`, + `npm install -D --legacy-peer-deps react-native-eject && npx react-native eject`, [], { stdio: ['ignore', 'ignore', 'pipe'],