@nx/cypress
Version:
166 lines (165 loc) • 7.73 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CYPRESS_CONFIG_FILE_NAME_PATTERN = void 0;
exports.addDefaultE2EConfig = addDefaultE2EConfig;
exports.addDefaultCTConfig = addDefaultCTConfig;
exports.addMountDefinition = addMountDefinition;
exports.getProjectCypressConfigPath = getProjectCypressConfigPath;
exports.resolveCypressConfigObject = resolveCypressConfigObject;
const devkit_1 = require("@nx/devkit");
const ensure_typescript_1 = require("@nx/js/src/utils/typescript/ensure-typescript");
exports.CYPRESS_CONFIG_FILE_NAME_PATTERN = 'cypress.config.{js,ts,mjs,cjs}';
const TS_QUERY_COMMON_JS_EXPORT_SELECTOR = 'BinaryExpression:has(Identifier[name="module"]):has(Identifier[name="exports"])';
const TS_QUERY_EXPORT_CONFIG_PREFIX = `:matches(ExportAssignment, ${TS_QUERY_COMMON_JS_EXPORT_SELECTOR}) `;
async function addDefaultE2EConfig(cyConfigContents, options, baseUrl) {
if (!cyConfigContents) {
throw new Error('The passed in cypress config file is empty!');
}
const { tsquery } = await Promise.resolve().then(() => require('@phenomnomnominal/tsquery'));
const isCommonJS = tsquery.query(cyConfigContents, TS_QUERY_COMMON_JS_EXPORT_SELECTOR).length >
0;
const testingTypeConfig = tsquery.query(cyConfigContents, `${TS_QUERY_EXPORT_CONFIG_PREFIX} PropertyAssignment:has(Identifier[name="e2e"])`);
let updatedConfigContents = cyConfigContents;
if (testingTypeConfig.length === 0) {
const configValue = `nxE2EPreset(__filename, ${JSON.stringify(options, null, 2)
.split('\n')
.join('\n ')})`;
updatedConfigContents = tsquery.replace(cyConfigContents, `${TS_QUERY_EXPORT_CONFIG_PREFIX} ObjectLiteralExpression:first-child`, (node) => {
let baseUrlContents = baseUrl ? `,\n baseUrl: '${baseUrl}'` : '';
if (node.properties.length > 0) {
return `{
${node.properties.map((p) => p.getText()).join(',\n')},
e2e: {
...${configValue}${baseUrlContents}
}
}`;
}
return `{
e2e: {
...${configValue}${baseUrlContents}
}
}`;
});
return isCommonJS
? `const { nxE2EPreset } = require('@nx/cypress/plugins/cypress-preset');
${updatedConfigContents}`
: `import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
${updatedConfigContents}`;
}
return updatedConfigContents;
}
/**
* Adds the nxComponentTestingPreset to the cypress config file
* Make sure after calling this the correct import statement is addeda
* to bring in the nxComponentTestingPreset function
**/
async function addDefaultCTConfig(cyConfigContents, options = {}) {
if (!cyConfigContents) {
throw new Error('The passed in cypress config file is empty!');
}
const { tsquery } = await Promise.resolve().then(() => require('@phenomnomnominal/tsquery'));
const testingTypeConfig = tsquery.query(cyConfigContents, `${TS_QUERY_EXPORT_CONFIG_PREFIX} PropertyAssignment:has(Identifier[name="component"])`);
let updatedConfigContents = cyConfigContents;
if (testingTypeConfig.length === 0) {
let configValue = 'nxComponentTestingPreset(__filename)';
if (options) {
if (options.bundler !== 'vite') {
// vite is the default bundler, so we don't need to set it
delete options.bundler;
}
if (Object.keys(options).length) {
configValue = `nxComponentTestingPreset(__filename, ${JSON.stringify(options)})`;
}
}
updatedConfigContents = tsquery.replace(cyConfigContents, `${TS_QUERY_EXPORT_CONFIG_PREFIX} ObjectLiteralExpression:first-child`, (node) => {
if (node.properties.length > 0) {
return `{
${node.properties.map((p) => p.getText()).join(',\n')},
component: ${configValue}
}`;
}
return `{
component: ${configValue}
}`;
});
}
return updatedConfigContents;
}
/**
* Adds the mount command for Cypress
* Make sure after calling this the correct import statement is added
* to bring in the correct mount from cypress.
**/
async function addMountDefinition(cmpCommandFileContents) {
if (!cmpCommandFileContents) {
throw new Error('The passed in cypress component file is empty!');
}
const { tsquery } = await Promise.resolve().then(() => require('@phenomnomnominal/tsquery'));
const hasMountCommand = tsquery.query(cmpCommandFileContents, 'CallExpression StringLiteral[value="mount"]')?.length > 0;
if (hasMountCommand) {
return cmpCommandFileContents;
}
const mountCommand = `Cypress.Commands.add('mount', mount);`;
const updatedInterface = tsquery.replace(cmpCommandFileContents, 'InterfaceDeclaration', (node) => {
return `interface ${node.name.getText()}${node.typeParameters
? `<${node.typeParameters.map((p) => p.getText()).join(', ')}>`
: ''} {
${node.members.map((m) => m.getText()).join('\n ')}
mount: typeof mount;
}`;
});
return `${updatedInterface}\n${mountCommand}`;
}
function getProjectCypressConfigPath(tree, projectRoot) {
const cypressConfigPaths = (0, devkit_1.glob)(tree, [
(0, devkit_1.joinPathFragments)(projectRoot, exports.CYPRESS_CONFIG_FILE_NAME_PATTERN),
]);
if (cypressConfigPaths.length === 0) {
throw new Error(`Could not find a cypress config file in ${projectRoot}.`);
}
return cypressConfigPaths[0];
}
function resolveCypressConfigObject(cypressConfigContents) {
const ts = (0, ensure_typescript_1.ensureTypescript)();
const { tsquery } = (require('@phenomnomnominal/tsquery'));
const sourceFile = tsquery.ast(cypressConfigContents);
const exportDefaultStatement = sourceFile.statements.find((statement) => ts.isExportAssignment(statement));
if (exportDefaultStatement) {
return resolveCypressConfigObjectFromExportExpression(exportDefaultStatement.expression, sourceFile);
}
const moduleExportsStatement = sourceFile.statements.find((statement) => ts.isExpressionStatement(statement) &&
ts.isBinaryExpression(statement.expression) &&
statement.expression.left.getText() === 'module.exports');
if (moduleExportsStatement) {
return resolveCypressConfigObjectFromExportExpression(moduleExportsStatement.expression.right, sourceFile);
}
return null;
}
function resolveCypressConfigObjectFromExportExpression(exportExpression, sourceFile) {
const ts = (0, ensure_typescript_1.ensureTypescript)();
if (ts.isObjectLiteralExpression(exportExpression)) {
return exportExpression;
}
if (ts.isIdentifier(exportExpression)) {
// try to locate the identifier in the source file
const variableStatements = sourceFile.statements.filter((statement) => ts.isVariableStatement(statement));
for (const variableStatement of variableStatements) {
for (const declaration of variableStatement.declarationList
.declarations) {
if (ts.isIdentifier(declaration.name) &&
declaration.name.getText() === exportExpression.getText() &&
ts.isObjectLiteralExpression(declaration.initializer)) {
return declaration.initializer;
}
}
}
return null;
}
if (ts.isCallExpression(exportExpression) &&
ts.isIdentifier(exportExpression.expression) &&
exportExpression.expression.getText() === 'defineConfig' &&
ts.isObjectLiteralExpression(exportExpression.arguments[0])) {
return exportExpression.arguments[0];
}
return null;
}