* feat(testing): add generator to aid in the migration to cypress 10 cypress 10 introduces a new configuration format and new layout that requires update settings and files for e2e projects * feat(testing): cypress component tests for react/next initial work for cypress component tests for react and next * feat(testing): add support for v10 e2e cypress projects create the correct files for cypress projects >v10 and reorganize tests based on version to allow easier parsing of tests * feat(testing): add utils for modifying cypress v10 config provide ts transformers to take in an existing cypress config and update/add properties within the given configuration * fix(testing): fix tests affected by the cypress v10 changes update tests to assert the correct files/folders/file contents due to the cypress changes in v10 * cleanup(testing): move cypress component testing plugins into the respective packages move the plugins into out of cypress plugins into the specific vertical plugin to prevent issues with circular refs * cleanup(testing): bump cypress version bump to latest cypress v10 release * docs(testing): update docs for cypress 10 update cypress docs to include info about component testing and migration to cypress v10 * fix(repo): revert cypress version bump keep v9 of cypress installed for nx repo until v10 release * fix(testing): update cypress gen tsconfig and infer targets with converter * fix(testing): make sure tests use the cypress v10 (for the intermediate) * fix(testing): update target name after feedback * fix(testing): support multiple target w/custom configs for cypress v10 migration * fix(testing): refactor cy component tests into seperate verticals * feat(testing): create storybook cypress preset * fix(testing): clean up cy v10 migration * fix(testing): don't branch for cypress executor testingType * fix(testing): move cy comp test generator to next * fix(testing): bump cypress deps * fix(testing): clean up cy component testing generators * fix(testing): update cy component testing docs * fix(testign): dep check. runtime plugin pulls from @nrwl/react * fix(testing): move e2e into verticals * fix(testing): address PR feedback * fix(testing): clean up unit tests * feat(angular): support migrating angular cli workspaces using cypress v10 * chore(testing): update e2e tests * fix(testing): address pr feedback * chore(testing): remove cypress component testing for next.js * fix(testing): address pr feedback Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
168 lines
4.3 KiB
TypeScript
168 lines
4.3 KiB
TypeScript
import {
|
|
convertNxGenerator,
|
|
generateFiles,
|
|
getProjects,
|
|
joinPathFragments,
|
|
Tree,
|
|
} from '@nrwl/devkit';
|
|
import { basename, join } from 'path';
|
|
import * as ts from 'typescript';
|
|
import {
|
|
findExportDeclarationsForJsx,
|
|
getComponentNode,
|
|
getComponentPropsInterface,
|
|
} from '../../utils/ast-utils';
|
|
|
|
export interface CreateComponentSpecFileSchema {
|
|
project: string;
|
|
componentPath: string;
|
|
js?: boolean;
|
|
cypressProject?: string;
|
|
}
|
|
|
|
export function componentCypressGenerator(
|
|
host: Tree,
|
|
schema: CreateComponentSpecFileSchema
|
|
) {
|
|
createComponentSpecFile(host, schema);
|
|
}
|
|
|
|
// TODO: candidate to refactor with the angular component story
|
|
export function getArgsDefaultValue(property: ts.SyntaxKind): string {
|
|
const typeNameToDefault: Record<number, any> = {
|
|
[ts.SyntaxKind.StringKeyword]: '',
|
|
[ts.SyntaxKind.NumberKeyword]: 0,
|
|
[ts.SyntaxKind.BooleanKeyword]: false,
|
|
};
|
|
|
|
const resolvedValue = typeNameToDefault[property];
|
|
if (typeof resolvedValue === undefined) {
|
|
return '';
|
|
} else if (typeof resolvedValue === 'string') {
|
|
return resolvedValue.replace(/\s/g, '+');
|
|
} else {
|
|
return resolvedValue;
|
|
}
|
|
}
|
|
|
|
export function createComponentSpecFile(
|
|
tree: Tree,
|
|
{ project, componentPath, js, cypressProject }: CreateComponentSpecFileSchema
|
|
) {
|
|
const e2eProjectName = cypressProject || `${project}-e2e`;
|
|
const projects = getProjects(tree);
|
|
const e2eProject = projects.get(e2eProjectName);
|
|
// cypress >= v10 will have a cypress.config.ts < v10 will have a cypress.json
|
|
const isCypressV10 = tree.exists(join(e2eProject.root, 'cypress.config.ts'));
|
|
|
|
const e2eLibIntegrationFolderPath = join(
|
|
e2eProject.sourceRoot,
|
|
isCypressV10 ? 'e2e' : 'integration'
|
|
);
|
|
|
|
const proj = projects.get(project);
|
|
const componentFilePath = joinPathFragments(proj.sourceRoot, componentPath);
|
|
const componentName = componentFilePath
|
|
.slice(componentFilePath.lastIndexOf('/') + 1)
|
|
.replace('.tsx', '')
|
|
.replace('.jsx', '')
|
|
.replace('.js', '');
|
|
|
|
const contents = tree.read(componentFilePath, 'utf-8');
|
|
if (contents === null) {
|
|
throw new Error(`Failed to read ${componentFilePath}`);
|
|
}
|
|
|
|
const sourceFile = ts.createSourceFile(
|
|
componentFilePath,
|
|
contents,
|
|
ts.ScriptTarget.Latest,
|
|
true
|
|
);
|
|
|
|
const cmpDeclaration = getComponentNode(sourceFile);
|
|
if (!cmpDeclaration) {
|
|
const componentNodes = findExportDeclarationsForJsx(sourceFile);
|
|
if (componentNodes?.length) {
|
|
componentNodes.forEach((declaration) => {
|
|
findPropsAndGenerateFileForCypress(
|
|
tree,
|
|
sourceFile,
|
|
declaration,
|
|
e2eLibIntegrationFolderPath,
|
|
componentName,
|
|
project,
|
|
js,
|
|
true
|
|
);
|
|
});
|
|
} else {
|
|
throw new Error(
|
|
`Could not find any React component in file ${componentFilePath}`
|
|
);
|
|
}
|
|
} else {
|
|
findPropsAndGenerateFileForCypress(
|
|
tree,
|
|
sourceFile,
|
|
cmpDeclaration,
|
|
e2eLibIntegrationFolderPath,
|
|
componentName,
|
|
project,
|
|
js
|
|
);
|
|
}
|
|
}
|
|
|
|
function findPropsAndGenerateFileForCypress(
|
|
tree: Tree,
|
|
sourceFile: ts.SourceFile,
|
|
cmpDeclaration: ts.Node,
|
|
e2eLibIntegrationFolderPath: string,
|
|
componentName: string,
|
|
project: string,
|
|
js: boolean,
|
|
fromNodeArray?: boolean
|
|
) {
|
|
const propsInterface = getComponentPropsInterface(sourceFile, cmpDeclaration);
|
|
|
|
let props: {
|
|
name: string;
|
|
defaultValue: any;
|
|
}[] = [];
|
|
|
|
if (propsInterface) {
|
|
props = propsInterface.members.map((member: ts.PropertySignature) => {
|
|
return {
|
|
name: (member.name as ts.Identifier).text,
|
|
defaultValue: getArgsDefaultValue(member.type.kind),
|
|
};
|
|
});
|
|
}
|
|
|
|
const isCypressV10 = basename(e2eLibIntegrationFolderPath) === 'e2e';
|
|
const cyFilePrefix = isCypressV10 ? 'cy' : 'spec';
|
|
|
|
generateFiles(
|
|
tree,
|
|
joinPathFragments(__dirname, './files'),
|
|
`${e2eLibIntegrationFolderPath}/${
|
|
fromNodeArray
|
|
? componentName + '--' + (cmpDeclaration as any).name.text
|
|
: componentName
|
|
}`,
|
|
{
|
|
projectName: project,
|
|
componentName,
|
|
componentSelector: (cmpDeclaration as any).name.text,
|
|
props,
|
|
fileExt: js ? `${cyFilePrefix}.js` : `${cyFilePrefix}.ts`,
|
|
}
|
|
);
|
|
}
|
|
|
|
export default componentCypressGenerator;
|
|
export const componentCypressSchematic = convertNxGenerator(
|
|
componentCypressGenerator
|
|
);
|