From e66d1c7de54c2e0e1336bee5f4c8bc7c34a5d624 Mon Sep 17 00:00:00 2001 From: Caleb Ukle Date: Fri, 14 Oct 2022 09:45:16 -0500 Subject: [PATCH] fix(testing): run cypress install if cypress is not installed --- .../executors/cypress/cypress.impl.spec.ts | 28 ++++++++++++++++++- .../src/executors/cypress/cypress.impl.ts | 17 +++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/cypress/src/executors/cypress/cypress.impl.spec.ts b/packages/cypress/src/executors/cypress/cypress.impl.spec.ts index f55a705bbd0d0f..0495beb5f88c1d 100644 --- a/packages/cypress/src/executors/cypress/cypress.impl.spec.ts +++ b/packages/cypress/src/executors/cypress/cypress.impl.spec.ts @@ -1,5 +1,5 @@ import { getTempTailwindPath } from '../../utils/ct-helpers'; -import { stripIndents } from '@nrwl/devkit'; +import { getPackageManagerCommand, stripIndents } from '@nrwl/devkit'; import * as path from 'path'; import { installedCypressVersion } from '../../utils/cypress-version'; import cypressExecutor, { CypressExecutorOptions } from './cypress.impl'; @@ -9,6 +9,12 @@ let devkit = require('@nrwl/devkit'); jest.mock('../../utils/cypress-version'); jest.mock('../../utils/ct-helpers'); +jest.mock('child_process', () => { + return { + ...jest.requireActual('child_process'), + execSync: jest.fn(), + }; +}); const Cypress = require('cypress'); describe('Cypress builder', () => { @@ -33,6 +39,9 @@ describe('Cypress builder', () => { (devkit as any).readTargetOptions = jest.fn().mockReturnValue({ watch: true, }); + (devkit as any).getPackageManagerCommand = jest.fn().mockReturnValue({ + exec: 'npx', + }); let runExecutor: any; let mockGetTailwindPath: jest.Mock> = getTempTailwindPath as any; @@ -411,6 +420,23 @@ A generator to migrate from v8 to v10 is provided. See https://nx.dev/cypress/v1 ); }); + it('should call cypress verify', async () => { + const cp = require('child_process'); + const execSpy = jest.spyOn(cp, 'execSync'); + await cypressExecutor(cypressOptions, mockContext); + expect(execSpy).toHaveBeenCalledWith('npx cypress verify'); + }); + + it('should make sure cypress is installed', async () => { + const cp = require('child_process'); + const execSpy = jest.spyOn(cp, 'execSync').mockImplementationOnce(() => { + throw new Error('blah'); + }); + await cypressExecutor(cypressOptions, mockContext); + expect(execSpy).toHaveBeenNthCalledWith(1, 'npx cypress verify'); + expect(execSpy).toHaveBeenNthCalledWith(2, 'npx cypress install'); + }); + describe('Component Testing', () => { beforeEach(() => { mockGetTailwindPath.mockReturnValue(undefined); diff --git a/packages/cypress/src/executors/cypress/cypress.impl.ts b/packages/cypress/src/executors/cypress/cypress.impl.ts index fd3ef2f3b9a810..965711e0d74296 100644 --- a/packages/cypress/src/executors/cypress/cypress.impl.ts +++ b/packages/cypress/src/executors/cypress/cypress.impl.ts @@ -1,11 +1,13 @@ import { ExecutorContext, + getPackageManagerCommand, logger, parseTargetString, readTargetOptions, runExecutor, stripIndents, } from '@nrwl/devkit'; +import { execSync } from 'child_process'; import 'dotenv/config'; import { existsSync, unlinkSync } from 'fs'; import { basename, dirname, join } from 'path'; @@ -49,6 +51,7 @@ export default async function cypressExecutor( options: CypressExecutorOptions, context: ExecutorContext ) { + assertCypressInstalled(); options = normalizeOptions(options, context); // this is used by cypress component testing presets to build the executor contexts with the correct configuration options. process.env.NX_CYPRESS_TARGET_CONFIGURATION = context.configurationName; @@ -144,6 +147,20 @@ A generator to migrate from v8 to v10 is provided. See https://nx.dev/cypress/v1 } } +function assertCypressInstalled() { + const pm = getPackageManagerCommand(); + try { + execSync(`${pm.exec} cypress verify`); + } catch (e) { + if (e.stdout) { + logger.debug(e.stdout.toString()); + } + + logger.info(`NX Attempting to install Cypress.`); + execSync(`${pm.exec} cypress install`); + } +} + async function* startDevServer( opts: CypressExecutorOptions, context: ExecutorContext