UNPKG

@nx/cypress

Version:

The Nx Plugin for Cypress contains executors and generators allowing your workspace to use the powerful Cypress integration testing capabilities.

166 lines (165 loc) • 7.73 kB
"use strict"; 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; }