feat(testing): Cypress 12 Support (#14058)
This commit is contained in:
parent
5970246b51
commit
0bc93ee83d
@ -35,6 +35,12 @@
|
|||||||
"version": "15.1.0-beta.0",
|
"version": "15.1.0-beta.0",
|
||||||
"description": "Update to Cypress v11. This migration will only update if the workspace is already on v10. https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/",
|
"description": "Update to Cypress v11. This migration will only update if the workspace is already on v10. https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/",
|
||||||
"factory": "./src/migrations/update-15-1-0/cypress-11"
|
"factory": "./src/migrations/update-15-1-0/cypress-11"
|
||||||
|
},
|
||||||
|
"update-to-cypress-12": {
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "15.5.0-beta.0",
|
||||||
|
"description": "Update to Cypress v12. Cypress 12 contains a handful of breaking changes that might causes tests to start failing that nx cannot directly fix. Read more Cypress 12 changes: https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-12-0.This migration will only run if you are already using Cypress v11.",
|
||||||
|
"factory": "./src/migrations/update-15-5-0/update-to-cypress-12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageJsonUpdates": {}
|
"packageJsonUpdates": {}
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
"semver": "7.3.4"
|
"semver": "7.3.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"cypress": ">= 3 < 12"
|
"cypress": ">= 3 < 13"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"cypress": {
|
"cypress": {
|
||||||
|
|||||||
@ -120,7 +120,7 @@ https://nx.dev/cypress/v10-migration-guide
|
|||||||
);
|
);
|
||||||
|
|
||||||
updateJson(tree, 'package.json', (json) => {
|
updateJson(tree, 'package.json', (json) => {
|
||||||
json.devDependencies['cypress'] = cypressVersion;
|
json.devDependencies['cypress'] = '^11.2.0';
|
||||||
return json;
|
return json;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,87 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Cypress 12 Migration should migrate to cy 12 1`] = `
|
||||||
|
"describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
cy.server()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
cy.route(/api/, () => {
|
||||||
|
return {
|
||||||
|
'test': 'We’ll',
|
||||||
|
}
|
||||||
|
}).as('getApi')
|
||||||
|
|
||||||
|
cy.visit('/index.html')
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const xhr = new win.XMLHttpRequest
|
||||||
|
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||||
|
xhr.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||||
|
* You should use .then() to chain commands instead.
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||||
|
**/
|
||||||
|
cy.should(($s) => {
|
||||||
|
cy.get('@table').find('tr').should('have.length', 3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Cypress 12 Migration should migrate to cy 12 2`] = `
|
||||||
|
"describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
cy.server()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
cy.route(/api/, () => {
|
||||||
|
return {
|
||||||
|
'test': 'We’ll',
|
||||||
|
}
|
||||||
|
}).as('getApi')
|
||||||
|
|
||||||
|
cy.visit('/index.html')
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const xhr = new win.XMLHttpRequest
|
||||||
|
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||||
|
xhr.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||||
|
* You should use .then() to chain commands instead.
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||||
|
**/
|
||||||
|
cy.should(($s) => {
|
||||||
|
cy.get('@table').find('tr').should('have.length', 3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})"
|
||||||
|
`;
|
||||||
38
packages/cypress/src/migrations/update-15-5-0/helpers.ts
Normal file
38
packages/cypress/src/migrations/update-15-5-0/helpers.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import type { Node } from 'typescript';
|
||||||
|
|
||||||
|
export function isAlreadyCommented(node: Node) {
|
||||||
|
return node.getFullText().includes('TODO(@nrwl/cypress)');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BANNED_COMMANDS = [
|
||||||
|
'as',
|
||||||
|
'children',
|
||||||
|
'closest',
|
||||||
|
'contains',
|
||||||
|
'debug',
|
||||||
|
'document',
|
||||||
|
'eq',
|
||||||
|
'filter',
|
||||||
|
'find',
|
||||||
|
'first',
|
||||||
|
'focused',
|
||||||
|
'get',
|
||||||
|
'hash',
|
||||||
|
'its',
|
||||||
|
'last',
|
||||||
|
'location',
|
||||||
|
'next',
|
||||||
|
'nextAll',
|
||||||
|
'not',
|
||||||
|
'parent',
|
||||||
|
'parents',
|
||||||
|
'parentsUntil',
|
||||||
|
'prev',
|
||||||
|
'prevUntil',
|
||||||
|
'root',
|
||||||
|
'shadow',
|
||||||
|
'siblings',
|
||||||
|
'title',
|
||||||
|
'url',
|
||||||
|
'window',
|
||||||
|
];
|
||||||
@ -0,0 +1,717 @@
|
|||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
|
stripIndents,
|
||||||
|
Tree,
|
||||||
|
readJson,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import {
|
||||||
|
shouldNotOverrideCommands,
|
||||||
|
shouldNotUseCyInShouldCB,
|
||||||
|
shouldUseCyIntercept,
|
||||||
|
shouldUseCySession,
|
||||||
|
turnOffTestIsolation,
|
||||||
|
updateToCypress12,
|
||||||
|
} from './update-to-cypress-12';
|
||||||
|
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||||
|
jest.mock('../../utils/cypress-version');
|
||||||
|
|
||||||
|
describe('Cypress 12 Migration', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
let mockInstalledCypressVersion: jest.Mock<
|
||||||
|
ReturnType<typeof installedCypressVersion>
|
||||||
|
> = installedCypressVersion as never;
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should migrate to cy 12', () => {
|
||||||
|
mockInstalledCypressVersion.mockReturnValue(11);
|
||||||
|
addCypressProject(tree, 'my-app-e2e');
|
||||||
|
addCypressProject(tree, 'my-other-app-e2e');
|
||||||
|
updateToCypress12(tree);
|
||||||
|
assertMigration(tree, 'my-app-e2e');
|
||||||
|
assertMigration(tree, 'my-other-app-e2e');
|
||||||
|
const pkgJson = readJson(tree, 'package.json');
|
||||||
|
expect(pkgJson.devDependencies['cypress']).toEqual('^12.2.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not migrate if cypress version is < 11', () => {
|
||||||
|
mockInstalledCypressVersion.mockReturnValue(10);
|
||||||
|
addCypressProject(tree, 'my-app-e2e');
|
||||||
|
updateToCypress12(tree);
|
||||||
|
expect(tree.read('apps/my-app-e2e/cypress.config.ts', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: nxE2EPreset(__filename)
|
||||||
|
})"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('nest cypress commands in should callback', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
tree.write(
|
||||||
|
'should-callback.ts',
|
||||||
|
`describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
cy.server()
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
cy.should((b) => {
|
||||||
|
const a = 123;
|
||||||
|
// I'm not doing nested cy stuff
|
||||||
|
});
|
||||||
|
cy.should(($s) => {
|
||||||
|
cy.task("");
|
||||||
|
})
|
||||||
|
cy.should(function($el) {
|
||||||
|
cy.task("");
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should comment', () => {
|
||||||
|
shouldNotUseCyInShouldCB(tree, 'should-callback.ts');
|
||||||
|
expect(tree.read('should-callback.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
cy.server()
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
cy.should((b) => {
|
||||||
|
const a = 123;
|
||||||
|
// I'm not doing nested cy stuff
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||||
|
* You should use .then() to chain commands instead.
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||||
|
**/
|
||||||
|
cy.should(($s) => {
|
||||||
|
cy.task(\\"\\");
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||||
|
* You should use .then() to chain commands instead.
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||||
|
**/
|
||||||
|
cy.should(function($el) {
|
||||||
|
cy.task(\\"\\");
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be idempotent', () => {
|
||||||
|
const expected = `describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
cy.server()
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
cy.should((b) => {
|
||||||
|
const a = 123;
|
||||||
|
// I'm not doing nested cy stuff
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||||
|
* You should use .then() to chain commands instead.
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||||
|
**/
|
||||||
|
cy.should(($s) => {
|
||||||
|
cy.task("");
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||||
|
* You should use .then() to chain commands instead.
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||||
|
**/
|
||||||
|
cy.should(function($el) {
|
||||||
|
cy.task("");
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
`;
|
||||||
|
shouldNotUseCyInShouldCB(tree, 'should-callback.ts');
|
||||||
|
expect(tree.read('should-callback.ts', 'utf-8')).toEqual(expected);
|
||||||
|
shouldNotUseCyInShouldCB(tree, 'should-callback.ts');
|
||||||
|
expect(tree.read('should-callback.ts', 'utf-8')).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('banned Cypres.Commands.overwrite', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
tree.write(
|
||||||
|
'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('find', () => {});
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should comment', () => {
|
||||||
|
shouldNotOverrideCommands(tree, 'commands.ts');
|
||||||
|
expect(tree.read('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(@nrwl/cypress): This command can no longer be overridden
|
||||||
|
* Consider using a different name like 'custom_find'
|
||||||
|
* More info: https://docs.cypress.io/guides/references/migration-guide#Cypress-Commands-overwrite
|
||||||
|
**/
|
||||||
|
Cypress.Commands.overwrite('find', () => {});
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be idempotent', () => {
|
||||||
|
const expected = `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(@nrwl/cypress): This command can no longer be overridden
|
||||||
|
* Consider using a different name like 'custom_find'
|
||||||
|
* More info: https://docs.cypress.io/guides/references/migration-guide#Cypress-Commands-overwrite
|
||||||
|
**/
|
||||||
|
Cypress.Commands.overwrite('find', () => {});
|
||||||
|
`;
|
||||||
|
|
||||||
|
shouldNotOverrideCommands(tree, 'commands.ts');
|
||||||
|
expect(tree.read('commands.ts', 'utf-8')).toEqual(expected);
|
||||||
|
shouldNotOverrideCommands(tree, 'commands.ts');
|
||||||
|
expect(tree.read('commands.ts', 'utf-8')).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('api removal', () => {
|
||||||
|
it('should be idempotent', () => {
|
||||||
|
tree.write(
|
||||||
|
'my-cool-test.cy.ts',
|
||||||
|
`
|
||||||
|
describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
cy.server()
|
||||||
|
cy.route(/api/, () => {
|
||||||
|
return {
|
||||||
|
'test': 'We’ll',
|
||||||
|
}
|
||||||
|
}).as('getApi')
|
||||||
|
|
||||||
|
cy.visit('/index.html')
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const xhr = new win.XMLHttpRequest
|
||||||
|
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||||
|
xhr.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
`
|
||||||
|
);
|
||||||
|
const expected = stripIndents`describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
cy.server()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
cy.route(/api/, () => {
|
||||||
|
return {
|
||||||
|
'test': 'We’ll',
|
||||||
|
}
|
||||||
|
}).as('getApi')
|
||||||
|
|
||||||
|
cy.visit('/index.html')
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const xhr = new win.XMLHttpRequest
|
||||||
|
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||||
|
xhr.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
})
|
||||||
|
})`;
|
||||||
|
shouldUseCyIntercept(tree, 'my-cool-test.cy.ts');
|
||||||
|
shouldUseCySession(tree, 'my-cool-test.cy.ts');
|
||||||
|
expect(stripIndents`${tree.read('my-cool-test.cy.ts', 'utf-8')}`).toEqual(
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
|
||||||
|
shouldUseCyIntercept(tree, 'my-cool-test.cy.ts');
|
||||||
|
shouldUseCySession(tree, 'my-cool-test.cy.ts');
|
||||||
|
expect(stripIndents`${tree.read('my-cool-test.cy.ts', 'utf-8')}`).toEqual(
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('comment on cy.route,cy.server, & Cypress.Server.defaults usage', () => {
|
||||||
|
tree.write(
|
||||||
|
'my-cool-test.cy.ts',
|
||||||
|
`
|
||||||
|
describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
cy.server()
|
||||||
|
cy.route(/api/, () => {
|
||||||
|
return {
|
||||||
|
'test': 'We’ll',
|
||||||
|
}
|
||||||
|
}).as('getApi')
|
||||||
|
|
||||||
|
cy.visit('/index.html')
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const xhr = new win.XMLHttpRequest
|
||||||
|
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||||
|
xhr.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
`
|
||||||
|
);
|
||||||
|
shouldUseCyIntercept(tree, 'my-cool-test.cy.ts');
|
||||||
|
expect(tree.read('my-cool-test.cy.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
cy.server()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
cy.route(/api/, () => {
|
||||||
|
return {
|
||||||
|
'test': 'We’ll',
|
||||||
|
}
|
||||||
|
}).as('getApi')
|
||||||
|
|
||||||
|
cy.visit('/index.html')
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const xhr = new win.XMLHttpRequest
|
||||||
|
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||||
|
xhr.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('comment on Cypress.Cookies.defaults & Cypress.Cookies.preserveOnce', () => {
|
||||||
|
tree.write(
|
||||||
|
'my-cool-test.cy.ts',
|
||||||
|
`
|
||||||
|
describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
cy.server()
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
`
|
||||||
|
);
|
||||||
|
shouldUseCySession(tree, 'my-cool-test.cy.ts');
|
||||||
|
expect(tree.read('my-cool-test.cy.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
cy.server()
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('testIsolation', () => {
|
||||||
|
it('should be idempotent', () => {
|
||||||
|
const content = `
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
...nxE2EPreset(__filename),
|
||||||
|
testIsolation: true,
|
||||||
|
})
|
||||||
|
`;
|
||||||
|
tree.write('my-cypress.config.ts', content);
|
||||||
|
turnOffTestIsolation(tree, 'my-cypress.config.ts');
|
||||||
|
|
||||||
|
expect(tree.read('my-cypress.config.ts', 'utf-8')).toEqual(content);
|
||||||
|
});
|
||||||
|
it('should add testIsolation: false to the default e2e config', () => {
|
||||||
|
tree.write(
|
||||||
|
'my-cypress.config.ts',
|
||||||
|
`
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: nxE2EPreset(__filename),
|
||||||
|
})
|
||||||
|
`
|
||||||
|
);
|
||||||
|
turnOffTestIsolation(tree, 'my-cypress.config.ts');
|
||||||
|
expect(tree.read('my-cypress.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
...nxE2EPreset(__filename),
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
||||||
|
* This can cause tests to start breaking where not indended.
|
||||||
|
* You should consider enabling this once you verify tests do not depend on each other
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
||||||
|
**/
|
||||||
|
testIsolation: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add testIsolation: false to inline object e2e config', () => {
|
||||||
|
tree.write(
|
||||||
|
'my-cypress.config.ts',
|
||||||
|
`
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
...nxE2EPreset(__filename),
|
||||||
|
video: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
`
|
||||||
|
);
|
||||||
|
turnOffTestIsolation(tree, 'my-cypress.config.ts');
|
||||||
|
expect(tree.read('my-cypress.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
...nxE2EPreset(__filename),
|
||||||
|
video: false,
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
||||||
|
* This can cause tests to start breaking where not indended.
|
||||||
|
* You should consider enabling this once you verify tests do not depend on each other
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
||||||
|
**/
|
||||||
|
testIsolation: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add testIsolation: false for a variable e2e config', () => {
|
||||||
|
tree.write(
|
||||||
|
'my-cypress.config.ts',
|
||||||
|
`
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
const myConfig = {
|
||||||
|
...nxE2EPreset(__filename),
|
||||||
|
video: false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: myConfig,
|
||||||
|
})
|
||||||
|
`
|
||||||
|
);
|
||||||
|
turnOffTestIsolation(tree, 'my-cypress.config.ts');
|
||||||
|
expect(tree.read('my-cypress.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
const myConfig = {
|
||||||
|
...nxE2EPreset(__filename),
|
||||||
|
video: false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
...myConfig,
|
||||||
|
/**
|
||||||
|
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
||||||
|
* This can cause tests to start breaking where not indended.
|
||||||
|
* You should consider enabling this once you verify tests do not depend on each other
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
||||||
|
**/
|
||||||
|
testIsolation: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function addCypressProject(tree: Tree, name: string) {
|
||||||
|
const targets = {
|
||||||
|
e2e: {
|
||||||
|
executor: '@nrwl/cypress:cypress',
|
||||||
|
options: {
|
||||||
|
tsConfig: `apps/${name}/tsconfig.e2e.json`,
|
||||||
|
testingType: 'e2e',
|
||||||
|
browser: 'chrome',
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
dev: {
|
||||||
|
cypressConfig: `apps/${name}/cypress.config.ts`,
|
||||||
|
devServerTarget: 'client:serve:dev',
|
||||||
|
baseUrl: 'http://localhost:4206',
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
cypressConfig: 'apps/client-e2e/cypress-custom.config.ts',
|
||||||
|
devServerTarget: 'client:serve:watch',
|
||||||
|
baseUrl: 'http://localhost:4204',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultConfiguration: 'dev',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
addProjectConfiguration(tree, name, {
|
||||||
|
root: `apps/${name}`,
|
||||||
|
sourceRoot: `apps/${name}/src`,
|
||||||
|
projectType: 'application',
|
||||||
|
targets,
|
||||||
|
});
|
||||||
|
// testIsolation
|
||||||
|
tree.write(
|
||||||
|
`apps/${name}/cypress.config.ts`,
|
||||||
|
`import { defineConfig } from 'cypress';
|
||||||
|
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: nxE2EPreset(__filename)
|
||||||
|
})`
|
||||||
|
);
|
||||||
|
// test Cypress.Commands.Override
|
||||||
|
tree.write(
|
||||||
|
`apps/${name}/src/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('find', () => {});
|
||||||
|
`
|
||||||
|
);
|
||||||
|
// test .should(() => cy.<cmd>)
|
||||||
|
tree.write(
|
||||||
|
`apps/${name}/src/e2e/callback.spec.ts`,
|
||||||
|
`describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
cy.server()
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
cy.should((b) => {
|
||||||
|
const a = 123;
|
||||||
|
// I'm not doing nested cy stuff
|
||||||
|
});
|
||||||
|
cy.should(($s) => {
|
||||||
|
cy.task("");
|
||||||
|
})
|
||||||
|
cy.should(function($el) {
|
||||||
|
cy.task("");
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})`
|
||||||
|
);
|
||||||
|
tree.write(
|
||||||
|
`apps/${name}/src/e2e/intercept-session.spec.ts`,
|
||||||
|
`describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
cy.server()
|
||||||
|
cy.route(/api/, () => {
|
||||||
|
return {
|
||||||
|
'test': 'We’ll',
|
||||||
|
}
|
||||||
|
}).as('getApi')
|
||||||
|
|
||||||
|
cy.visit('/index.html')
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const xhr = new win.XMLHttpRequest
|
||||||
|
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||||
|
xhr.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
})
|
||||||
|
})`
|
||||||
|
);
|
||||||
|
tree.write(
|
||||||
|
`apps/${name}/src/e2e/combo.spec.ts`,
|
||||||
|
`describe('something', () => {
|
||||||
|
it('should do the thing', () => {
|
||||||
|
Cypress.Cookies.defaults()
|
||||||
|
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||||
|
Cypress.blah.abc()
|
||||||
|
Cypress.Server.defaults({
|
||||||
|
delay: 500,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
cy.server()
|
||||||
|
cy.route(/api/, () => {
|
||||||
|
return {
|
||||||
|
'test': 'We’ll',
|
||||||
|
}
|
||||||
|
}).as('getApi')
|
||||||
|
|
||||||
|
cy.visit('/index.html')
|
||||||
|
cy.window().then((win) => {
|
||||||
|
const xhr = new win.XMLHttpRequest
|
||||||
|
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||||
|
xhr.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.wait('@getApi')
|
||||||
|
.its('url').should('include', 'api/v1')
|
||||||
|
cy.should(($s) => {
|
||||||
|
cy.get('@table').find('tr').should('have.length', 3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertMigration(tree: Tree, name: string) {
|
||||||
|
expect(tree.read(`apps/${name}/cypress.config.ts`, 'utf-8')).toContain(
|
||||||
|
'testIsolation: false'
|
||||||
|
);
|
||||||
|
// command overrides
|
||||||
|
expect(tree.read(`apps/${name}/src/support/commands.ts`, 'utf-8')).toContain(
|
||||||
|
'TODO(@nrwl/cypress): This command can no longer be overridden'
|
||||||
|
);
|
||||||
|
// test .should(() => cy.<cmd>)
|
||||||
|
expect(tree.read(`apps/${name}/src/e2e/callback.spec.ts`, 'utf-8')).toContain(
|
||||||
|
'TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.'
|
||||||
|
);
|
||||||
|
// use cy.intercept, cy.session
|
||||||
|
const interceptSessionSpec = tree.read(
|
||||||
|
`apps/${name}/src/e2e/intercept-session.spec.ts`,
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(interceptSessionSpec).toContain(
|
||||||
|
'// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes'
|
||||||
|
);
|
||||||
|
expect(interceptSessionSpec).toContain(
|
||||||
|
'// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults'
|
||||||
|
);
|
||||||
|
// intercept,session & callback
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/${name}/src/e2e/combo.spec.ts`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
}
|
||||||
@ -0,0 +1,245 @@
|
|||||||
|
import {
|
||||||
|
getProjects,
|
||||||
|
stripIndents,
|
||||||
|
Tree,
|
||||||
|
updateJson,
|
||||||
|
visitNotIgnoredFiles,
|
||||||
|
installPackagesTask,
|
||||||
|
GeneratorCallback,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { forEachExecutorOptions } from '@nrwl/workspace/src/utilities/executor-options-utils';
|
||||||
|
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||||
|
import {
|
||||||
|
CallExpression,
|
||||||
|
isArrowFunction,
|
||||||
|
isCallExpression,
|
||||||
|
isFunctionExpression,
|
||||||
|
isObjectLiteralExpression,
|
||||||
|
PropertyAccessExpression,
|
||||||
|
PropertyAssignment,
|
||||||
|
} from 'typescript';
|
||||||
|
import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
|
||||||
|
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||||
|
import { BANNED_COMMANDS, isAlreadyCommented } from './helpers';
|
||||||
|
|
||||||
|
const JS_TS_FILE_MATCHER = /\.[jt]sx?$/;
|
||||||
|
|
||||||
|
export function updateToCypress12(tree: Tree): GeneratorCallback {
|
||||||
|
if (installedCypressVersion() < 11) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const projects = getProjects(tree);
|
||||||
|
|
||||||
|
forEachExecutorOptions<CypressExecutorOptions>(
|
||||||
|
tree,
|
||||||
|
'@nrwl/cypress:cypress',
|
||||||
|
(options, projectName, targetName, configName) => {
|
||||||
|
if (!(options.cypressConfig && tree.exists(options.cypressConfig))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const projectConfig = projects.get(projectName);
|
||||||
|
turnOffTestIsolation(tree, options.cypressConfig);
|
||||||
|
|
||||||
|
visitNotIgnoredFiles(tree, projectConfig.root, (filePath) => {
|
||||||
|
if (!JS_TS_FILE_MATCHER.test(filePath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shouldUseCyIntercept(tree, filePath);
|
||||||
|
shouldUseCySession(tree, filePath);
|
||||||
|
shouldNotUseCyInShouldCB(tree, filePath);
|
||||||
|
shouldNotOverrideCommands(tree, filePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.warn(stripIndents`Cypress 12 has lots of breaking changes that might subility break your tests.
|
||||||
|
This migration marked known issues that need to be manually migrated,
|
||||||
|
but there can still be runtime based errors that were not detected.
|
||||||
|
Please consult the offical Cypress v12 migration guide for more info on these changes and the next steps.
|
||||||
|
https://docs.cypress.io/guides/references/migration-guide
|
||||||
|
`);
|
||||||
|
|
||||||
|
updateJson(tree, 'package.json', (json) => {
|
||||||
|
json.devDependencies.cypress = '^12.2.0';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
installPackagesTask(tree);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function turnOffTestIsolation(tree: Tree, configPath: string) {
|
||||||
|
const config = tree.read(configPath, 'utf-8');
|
||||||
|
const isTestIsolationSet = tsquery.query<PropertyAssignment>(
|
||||||
|
config,
|
||||||
|
'ExportAssignment ObjectLiteralExpression > PropertyAssignment:has(Identifier[name="testIsolation"])'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isTestIsolationSet.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const testIsolationProperty = `/**
|
||||||
|
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
||||||
|
* This can cause tests to start breaking where not indended.
|
||||||
|
* You should consider enabling this once you verify tests do not depend on each other
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
||||||
|
**/
|
||||||
|
testIsolation: false,`;
|
||||||
|
const updated = tsquery.replace(
|
||||||
|
config,
|
||||||
|
'ExportAssignment ObjectLiteralExpression > PropertyAssignment:has(Identifier[name="e2e"])',
|
||||||
|
(node: PropertyAssignment) => {
|
||||||
|
if (isObjectLiteralExpression(node.initializer)) {
|
||||||
|
const listOfProperties = node.initializer.properties
|
||||||
|
.map((j) => j.getText())
|
||||||
|
.join(',\n ');
|
||||||
|
return `e2e: {
|
||||||
|
${listOfProperties},
|
||||||
|
${testIsolationProperty}
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
return `e2e: {
|
||||||
|
...${node.initializer.getText()},
|
||||||
|
${testIsolationProperty}
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
tree.write(configPath, updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave a comment on all apis that have been removed andsuperseded by cy.intercept
|
||||||
|
* stating they these API are now removed and need to update.
|
||||||
|
* cy.route, cy.server, Cypress.Server.defaults
|
||||||
|
**/
|
||||||
|
export function shouldUseCyIntercept(tree: Tree, filePath: string) {
|
||||||
|
const content = tree.read(filePath, 'utf-8');
|
||||||
|
const markedRemovedCommands = tsquery.replace(
|
||||||
|
content,
|
||||||
|
':matches(PropertyAccessExpression:has(Identifier[name="cy"]):has(Identifier[name="server"], Identifier[name="route"]), PropertyAccessExpression:has(Identifier[name="defaults"]):has(Identifier[name="Cypress"], Identifier[name="Server"]))',
|
||||||
|
(node: PropertyAccessExpression) => {
|
||||||
|
if (isAlreadyCommented(node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const expression = node.expression.getText().trim();
|
||||||
|
// prevent extra chaining i.e. cy.route().as() will return 2 results
|
||||||
|
// cy.route and cy.route().as
|
||||||
|
// only need the first 1 so skip any extra chaining
|
||||||
|
if (expression === 'cy' || expression === 'Cypress.Server') {
|
||||||
|
return `// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||||
|
${node.getText()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
tree.write(filePath, markedRemovedCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave a comment on all apis that have been removed and superseded by cy.session
|
||||||
|
* stating they these API are now removed and need to update.
|
||||||
|
* Cypress.Cookies.defaults & Cypress.Cookies.preserveOnce
|
||||||
|
**/
|
||||||
|
export function shouldUseCySession(tree: Tree, filePath: string) {
|
||||||
|
const content = tree.read(filePath, 'utf-8');
|
||||||
|
const markedRemovedCommands = tsquery.replace(
|
||||||
|
content,
|
||||||
|
':matches(PropertyAccessExpression:has(Identifier[name="defaults"]):has(Identifier[name="Cypress"], Identifier[name="Cookies"]), PropertyAccessExpression:has(Identifier[name="preserveOnce"]):has(Identifier[name="Cypress"], Identifier[name="Cookies"]))',
|
||||||
|
(node: PropertyAccessExpression) => {
|
||||||
|
if (isAlreadyCommented(node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const expression = node.expression.getText().trim();
|
||||||
|
// prevent grabbing other Cypress.<something>.defaults
|
||||||
|
if (expression === 'Cypress.Cookies') {
|
||||||
|
return `// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||||
|
${node.getText()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
tree.write(filePath, markedRemovedCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* leave a comment about nested cy commands in a cy.should callback
|
||||||
|
* */
|
||||||
|
export function shouldNotUseCyInShouldCB(tree: Tree, filePath: string) {
|
||||||
|
const content = tree.read(filePath, 'utf-8');
|
||||||
|
const markedNestedCyCommands = tsquery.replace(
|
||||||
|
content,
|
||||||
|
'CallExpression > PropertyAccessExpression:has(Identifier[name="cy"]):has(Identifier[name="should"])',
|
||||||
|
(node: PropertyAccessExpression) => {
|
||||||
|
if (
|
||||||
|
isAlreadyCommented(node) ||
|
||||||
|
(node.parent && !isCallExpression(node.parent))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parentExpression = node.parent as CallExpression;
|
||||||
|
if (
|
||||||
|
parentExpression?.arguments?.[0] &&
|
||||||
|
(isArrowFunction(parentExpression.arguments[0]) ||
|
||||||
|
isFunctionExpression(parentExpression.arguments[0]))
|
||||||
|
) {
|
||||||
|
const isUsingNestedCyCommand =
|
||||||
|
tsquery.query<PropertyAccessExpression>(
|
||||||
|
parentExpression.arguments[0],
|
||||||
|
'CallExpression > PropertyAccessExpression:has(Identifier[name="cy"])'
|
||||||
|
)?.length > 0;
|
||||||
|
if (isUsingNestedCyCommand) {
|
||||||
|
return `/**
|
||||||
|
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||||
|
* You should use .then() to chain commands instead.
|
||||||
|
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||||
|
**/
|
||||||
|
${node.getText()}`;
|
||||||
|
}
|
||||||
|
return node.getText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
tree.write(filePath, markedNestedCyCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* leave a comment on all usages of overriding built-ins that are now banned
|
||||||
|
* */
|
||||||
|
export function shouldNotOverrideCommands(tree: Tree, filePath: string) {
|
||||||
|
const content = tree.read(filePath, 'utf-8');
|
||||||
|
const markedOverrideUsage = tsquery.replace(
|
||||||
|
content,
|
||||||
|
'PropertyAccessExpression:has(Identifier[name="overwrite"]):has(Identifier[name="Cypress"])',
|
||||||
|
(node: PropertyAccessExpression) => {
|
||||||
|
if (isAlreadyCommented(node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const expression = node.expression.getText().trim();
|
||||||
|
// prevent grabbing other Cypress.<something>.defaults
|
||||||
|
|
||||||
|
if (expression === 'Cypress.Commands') {
|
||||||
|
// get value.
|
||||||
|
const overwriteExpression = node.parent as CallExpression;
|
||||||
|
|
||||||
|
const command = (overwriteExpression.arguments?.[0] as any)?.text; // need string without quotes
|
||||||
|
if (BANNED_COMMANDS.includes(command)) {
|
||||||
|
// overwrite
|
||||||
|
return `/**
|
||||||
|
* TODO(@nrwl/cypress): This command can no longer be overridden
|
||||||
|
* Consider using a different name like 'custom_${command}'
|
||||||
|
* More info: https://docs.cypress.io/guides/references/migration-guide#Cypress-Commands-overwrite
|
||||||
|
**/
|
||||||
|
${node.getText()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
tree.write(filePath, markedOverrideUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default updateToCypress12;
|
||||||
@ -1,8 +1,8 @@
|
|||||||
export const nxVersion = require('../../package.json').version;
|
export const nxVersion = require('../../package.json').version;
|
||||||
export const eslintPluginCypressVersion = '^2.10.3';
|
export const eslintPluginCypressVersion = '^2.10.3';
|
||||||
export const typesNodeVersion = '16.11.7';
|
export const typesNodeVersion = '16.11.7';
|
||||||
export const cypressVersion = '^11.0.0';
|
|
||||||
export const cypressViteDevServerVersion = '^2.2.1';
|
export const cypressViteDevServerVersion = '^2.2.1';
|
||||||
|
export const cypressVersion = '^12.2.0';
|
||||||
export const cypressWebpackVersion = '^2.0.0';
|
export const cypressWebpackVersion = '^2.0.0';
|
||||||
export const webpackHttpPluginVersion = '^5.5.0';
|
export const webpackHttpPluginVersion = '^5.5.0';
|
||||||
export const viteVersion = '^4.0.1';
|
export const viteVersion = '^4.0.1';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user