diff --git a/packages/cypress/src/executors/cypress/cypress.impl.spec.ts b/packages/cypress/src/executors/cypress/cypress.impl.spec.ts index f55a705bbd0d0..c61ea7f8ee6a5 100644 --- a/packages/cypress/src/executors/cypress/cypress.impl.spec.ts +++ b/packages/cypress/src/executors/cypress/cypress.impl.spec.ts @@ -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 fd3ef2f3b9a81..965711e0d7429 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