Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(testing): support cypress v13 #18899

Merged
merged 1 commit into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/generated/packages/cypress/executors/cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@
"type": "boolean",
"description": "If passed, Cypress output will not be printed to stdout. Only output from the configured Mocha reporter will print.",
"default": false
},
"runnerUi": {
"type": "boolean",
"description": "Displays the Cypress Runner UI. Useful for when Test Replay is enabled and you would still like the Cypress Runner UI to be displayed for screenshots and video."
}
},
"additionalProperties": true,
Expand Down
6 changes: 6 additions & 0 deletions packages/cypress/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
"version": "16.4.0-beta.10",
"description": "Remove tsconfig.e2e.json and add settings to project tsconfig.json. tsConfigs executor option is now deprecated. The project level tsconfig.json file should be used instead.",
"implementation": "./src/migrations/update-16-4-0/tsconfig-sourcemaps"
},
"update-16-8-0-cypress-13": {
"cli": "nx",
"version": "16.8.0-beta.4",
"description": "Update to Cypress v13. Most noteable change is video recording is off by default. This migration will only update if the workspace is already on Cypress v12. https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-130",
"implementation": "./src/migrations/update-16-8-0/cypress-13"
}
},
"packageJsonUpdates": {
Expand Down
2 changes: 1 addition & 1 deletion packages/cypress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@nx/linter": "file:../linter"
},
"peerDependencies": {
"cypress": ">= 3 < 13"
"cypress": ">= 3 < 14"
},
"peerDependenciesMeta": {
"cypress": {
Expand Down
2 changes: 0 additions & 2 deletions packages/cypress/plugins/cypress-preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import vitePreprocessor from '../src/plugins/preprocessor-vite';
interface BaseCypressPreset {
videosFolder: string;
screenshotsFolder: string;
video: boolean;
barbados-clemens marked this conversation as resolved.
Show resolved Hide resolved
chromeWebSecurity: boolean;
}

Expand Down Expand Up @@ -47,7 +46,6 @@ export function nxBaseCypressPreset(
return {
videosFolder,
screenshotsFolder,
video: true,
chromeWebSecurity: false,
};
}
Expand Down
2 changes: 2 additions & 0 deletions packages/cypress/src/executors/cypress/cypress.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface CypressExecutorOptions extends Json {
tag?: string;
port?: number | 'cypress-auto';
quiet?: boolean;
runnerUi?: boolean;
}

interface NormalizedCypressExecutorOptions extends CypressExecutorOptions {
Expand Down Expand Up @@ -258,6 +259,7 @@ async function runCypress(
options.tag = opts.tag;
options.exit = opts.exit;
options.headed = opts.headed;
options.runnerUi = opts.runnerUi;

if (opts.headless) {
options.headless = opts.headless;
Expand Down
4 changes: 4 additions & 0 deletions packages/cypress/src/executors/cypress/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@
"type": "boolean",
"description": "If passed, Cypress output will not be printed to stdout. Only output from the configured Mocha reporter will print.",
"default": false
},
"runnerUi": {
"type": "boolean",
"description": "Displays the Cypress Runner UI. Useful for when Test Replay is enabled and you would still like the Cypress Runner UI to be displayed for screenshots and video."
}
},
"additionalProperties": true,
Expand Down
243 changes: 243 additions & 0 deletions packages/cypress/src/migrations/update-16-8-0/cypress-13.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import { Tree, addProjectConfiguration, readJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from 'nx/src/devkit-testing-exports';
import { updateToCypress13 } from './cypress-13';

describe('Cypress 13', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
});

it('should update deps to cypress v13', async () => {
setup(tree, { name: 'my-app' });

await updateToCypress13(tree);
expect(readJson(tree, 'package.json').devDependencies.cypress).toEqual(
'^13.0.0'
);
});

it('should update videoUploadOnPasses from config w/setupNodeEvents', async () => {
setup(tree, { name: 'my-app-video-upload-on-passes' });
await updateToCypress13(tree);
expect(
tree.read('apps/my-app-video-upload-on-passes/cypress.config.ts', 'utf-8')
).toMatchInlineSnapshot(`
"import fs from 'fs';

import { defineConfig } from 'cypress';
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing';

export default defineConfig({
something: 'blah',
// nodeVersion: 'system',
// videoUploadOnPasses: false ,
e2e: {
...nxE2EPreset(__filename),
setupNodeEvents(on, config) {
const a = '';
removePassedSpecs(on);
},
},
component: {
...nxComponentTestingPreset(__filename),
setupNodeEvents: (on, config) => {
const b = '';
removePassedSpecs(on);
},
},
});

/**
* Delete videos for specs that do not contain failing or retried tests.
* This function is to be used in the 'setupNodeEvents' configuration option as a replacement to
* 'videoUploadOnPasses' which has been removed.
*
* https://docs.cypress.io/guides/guides/screenshots-and-videos#Delete-videos-for-specs-without-failing-or-retried-tests
**/
function removePassedSpecs(on) {
on('after:spec', (spec, results) => {
if (results && results.vide) {
const hasFailures = results.tests.some((t) =>
t.attempts.some((a) => a.state === 'failed')
);

if (!hasFailures) {
fs.unlinkSync(results.video);
}
}
});
}
"
`);
});
it('should remove nodeVersion from config', async () => {
setup(tree, { name: 'my-app-node-version' });
await updateToCypress13(tree);
expect(tree.read('apps/my-app-node-version/cypress.config.ts', 'utf-8'))
.toMatchInlineSnapshot(`
"import fs from 'fs';

import { defineConfig } from 'cypress';
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing';

export default defineConfig({
something: 'blah',
// nodeVersion: 'system',
// videoUploadOnPasses: false ,
e2e: {
...nxE2EPreset(__filename),
setupNodeEvents(on, config) {
const a = '';
removePassedSpecs(on);
},
},
component: {
...nxComponentTestingPreset(__filename),
setupNodeEvents: (on, config) => {
const b = '';
removePassedSpecs(on);
},
},
});

/**
* Delete videos for specs that do not contain failing or retried tests.
* This function is to be used in the 'setupNodeEvents' configuration option as a replacement to
* 'videoUploadOnPasses' which has been removed.
*
* https://docs.cypress.io/guides/guides/screenshots-and-videos#Delete-videos-for-specs-without-failing-or-retried-tests
**/
function removePassedSpecs(on) {
on('after:spec', (spec, results) => {
if (results && results.vide) {
const hasFailures = results.tests.some((t) =>
t.attempts.some((a) => a.state === 'failed')
);

if (!hasFailures) {
fs.unlinkSync(results.video);
}
}
});
}
"
`);
});

it('should comment about overriding readFile command', async () => {
setup(tree, { name: 'my-app-read-file' });
const testContent = `describe('something', () => {
it('should do the thing', () => {
cy.readFile('my-data.json').its('name').should('eq', 'Nx');
});
});
`;
tree.write('apps/my-app-read-file/src/something.cy.ts', testContent);

tree.write(
'apps/my-app-read-file/cypress/support/commands.ts',
`declare namespace Cypress {
interface Chainable<Subject> {
login(email: string, password: string): void;
}
}
//
// -- This is a parent command --
Cypress.Commands.add('login', (email, password) => {
console.log('Custom command example: Login', email, password);
});
Cypress.Commands.overwrite('readFile', () => {});

`
);
await updateToCypress13(tree);

expect(
tree.read('apps/my-app-read-file/src/something.cy.ts', 'utf-8')
).toEqual(testContent);
expect(
tree.read('apps/my-app-read-file/cypress/support/commands.ts', 'utf-8')
).toMatchInlineSnapshot(`
"declare namespace Cypress {
interface Chainable<Subject> {
login(email: string, password: string): void;
}
}
//
// -- This is a parent command --
Cypress.Commands.add('login', (email, password) => {
console.log('Custom command example: Login', email, password);
});
/**
* TODO(@nx/cypress): This command can no longer be overridden
* Consider using a different name like 'custom_readFile'
* More info: https://docs.cypress.io/guides/references/migration-guide#readFile-can-no-longer-be-overwritten-with-CypressCommandsoverwrite
**/
Cypress.Commands.overwrite('readFile', () => {});
"
`);
});
});

function setup(tree: Tree, options: { name: string }) {
tree.write(
`apps/${options.name}/cypress.config.ts`,
`
import { defineConfig} from 'cypress';
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing';

export default defineConfig({
something: 'blah',
nodeVersion: 'system',
videoUploadOnPasses: false,
e2e: {
...nxE2EPreset(__filename),
videoUploadOnPasses: false,
nodeVersion: 'bundled',
setupNodeEvents(on, config) {
const a = '';
},
},
component: {
...nxComponentTestingPreset(__filename),
videoUploadOnPasses: false,
nodeVersion: 'something',
setupNodeEvents: (on, config) => {
const b = '';
}
},
})
`
);
tree.write(
'package.json',
JSON.stringify({ devDependencies: { cypress: '^12.16.0' } })
);
addProjectConfiguration(tree, options.name, {
root: `apps/${options.name}`,
sourceRoot: `apps/${options.name}/src`,
targets: {
e2e: {
executor: '@nx/cypress:cypress',
options: {
testingType: 'e2e',
cypressConfig: `apps/${options.name}/cypress.config.ts`,
devServerTarget: 'app:serve',
},
},
'component-test': {
executor: '@nx/cypress:cypress',
options: {
testingType: 'component',
cypressConfig: `apps/${options.name}/ct-cypress.config.ts`,
skipServe: true,
devServerTarget: 'app:build',
},
},
},
});
}
Loading