Skip to content
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
38 changes: 38 additions & 0 deletions tools/generators/cypress-component-configuration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# cypress-component-configuration

Workspace Generator for setting up Cypress component testing for a project

<!-- toc -->

- [Usage](#usage)
- [Examples](#examples)
- [Options](#options)
- [`project`](#project)

<!-- tocstop -->

## Usage

```sh
yarn nx workspace-generator cypress-component-configuration ...
```

Show what will be generated without writing to disk:

```sh
yarn nx workspace-generator cypress-component-configuration --dry-run
```

### Examples

```sh
yarn nx workspace-generator cypress-component-configuration
```

## Options

#### `project`

Type: `string`

The name of the project to add cypress component testing to
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { baseConfig } from '@fluentui/scripts-cypress';

export default baseConfig;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends":"./tsconfig.json",
"compilerOptions":{
"isolatedModules":false,
"types":["node", "cypress", "cypress-storybook/cypress", "cypress-real-events"],
"lib":["ES2019", "dom"]
},
"include":["**/*.cy.ts", "**/*.cy.tsx"]
}
142 changes: 142 additions & 0 deletions tools/generators/cypress-component-configuration/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {
addProjectConfiguration,
joinPathFragments,
NxJsonConfiguration,
readJson,
readNxJson,
serializeJson,
Tree,
} from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';

import generator from './index';

describe(`cypress-component-configuration`, () => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};

let tree: Tree;
beforeEach(() => {
jest.spyOn(console, 'log').mockImplementation(noop);
jest.spyOn(console, 'info').mockImplementation(noop);
jest.spyOn(console, 'warn').mockImplementation(noop);

tree = createTreeWithEmptyV1Workspace();
});

it(`should not create component testing for application`, async () => {
const project = '@proj/app-one';
tree = setupDummyPackage(tree, { name: project, projectType: 'application' });

await generator(tree, { project });

expect(tree.exists('apps/app-one/cypress.config.ts')).toBe(false);
});

it(`should setup cypress component testing for existing project`, async () => {
const project = '@proj/one';
tree = setupDummyPackage(tree, { name: project });

await generator(tree, { project });

expect(tree.read('packages/one/cypress.config.ts', 'utf-8')).toMatchInlineSnapshot(`
"import { baseConfig } from '@fluentui/scripts-cypress';

export default baseConfig;
"
`);
expect(readJson(tree, 'packages/one/tsconfig.json').references).toEqual(
expect.arrayContaining([
{
path: './tsconfig.cy.json',
},
]),
);
expect(readJson(tree, 'packages/one/tsconfig.lib.json').exclude).toEqual(
expect.arrayContaining(['**/*.cy.ts', '**/*.cy.tsx']),
);
expect(readJson(tree, 'packages/one/tsconfig.cy.json')).toMatchInlineSnapshot(`
Object {
"compilerOptions": Object {
"isolatedModules": false,
"lib": Array [
"ES2019",
"dom",
],
"types": Array [
"node",
"cypress",
"cypress-storybook/cypress",
"cypress-real-events",
],
},
"extends": "./tsconfig.json",
"include": Array [
"**/*.cy.ts",
"**/*.cy.tsx",
],
}
`);

expect(readJson(tree, 'packages/one/package.json').scripts).toEqual(
expect.objectContaining({
e2e: 'cypress run --component',
'e2e:local': 'cypress open --component',
}),
);
});
});

function setupDummyPackage(tree: Tree, options: { name: string; projectType?: 'application' | 'library' }) {
const { name: pkgName, projectType = 'library' } = options;

const workspaceConfig = readNxJson(tree) ?? {};
const normalizedPkgName = getNormalizedPkgName({ pkgName, workspaceConfig });
const paths = {
root: `${projectType === 'application' ? 'apps' : 'packages'}/${normalizedPkgName}`,
};

const templates = {
packageJson: {
name: pkgName,
version: '0.0.1',
typings: 'lib/index.d.ts',
main: 'lib-commonjs/index.js',
scripts: {},
dependencies: {},
devDependencies: {},
},
tsconfigs: {
root: {
references: [
{
path: './tsconfig.lib.json',
},
],
},
lib: {
extends: './tsconfig.json',
compilerOptions: {},
exclude: ['./src/testing/**', '**/*.spec.ts', '**/*.spec.tsx', '**/*.test.ts', '**/*.test.tsx'],
},
},
};

tree.write(`${paths.root}/package.json`, serializeJson(templates.packageJson));
tree.write(`${paths.root}/tsconfig.json`, serializeJson(templates.tsconfigs.root));
tree.write(`${paths.root}/tsconfig.lib.json`, serializeJson(templates.tsconfigs.lib));
tree.write(`${paths.root}/src/index.ts`, `export const greet = 'hello' `);

addProjectConfiguration(tree, pkgName, {
root: paths.root,
sourceRoot: joinPathFragments(paths.root, 'src'),
projectType,
targets: {},
});

return tree;
}

function getNormalizedPkgName(options: { pkgName: string; workspaceConfig: NxJsonConfiguration }) {
return options.pkgName.replace(`@${options.workspaceConfig.npmScope}/`, '');
}
35 changes: 35 additions & 0 deletions tools/generators/cypress-component-configuration/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Tree, formatFiles, names } from '@nrwl/devkit';

import { getProjectConfig, printUserLogs, UserLog } from '../../utils';

import type { CypressComponentConfigurationGeneratorSchema } from './schema';

import { addFiles } from './lib/add-files';

// eslint-disable-next-line @typescript-eslint/naming-convention
interface _NormalizedSchema extends ReturnType<typeof normalizeOptions> {}

export default async function (tree: Tree, schema: CypressComponentConfigurationGeneratorSchema) {
const userLog: UserLog = [];
const normalizedOptions = normalizeOptions(tree, schema);

if (normalizedOptions.projectConfig.projectType === 'application') {
userLog.push({ type: 'warn', message: 'we dont support cypress component tests for applications' });
printUserLogs(userLog);
return;
}

addFiles(tree, normalizedOptions);

await formatFiles(tree);
}

function normalizeOptions(tree: Tree, options: CypressComponentConfigurationGeneratorSchema) {
const project = getProjectConfig(tree, { packageName: options.project });

return {
...options,
...project,
...names(options.project),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { stripIndents, Tree, updateJson, generateFiles, joinPathFragments } from '@nrwl/devkit';
import * as path from 'path';

import { PackageJson, TsConfig } from '../../../types';
import { getProjectConfig } from '../../../utils';
import { uniqueArray } from './utils';

const templates = {
config: stripIndents`
import { baseConfig } from '@fluentui/scripts-cypress';

export default baseConfig;
`,
tsconfig: {
extends: './tsconfig.json',
compilerOptions: {
isolatedModules: false,
types: ['node', 'cypress', 'cypress-storybook/cypress', 'cypress-real-events'],
lib: ['ES2019', 'dom'],
},
include: ['**/*.cy.ts', '**/*.cy.tsx'],
},
};

type Options = ReturnType<typeof getProjectConfig>;

export function addFiles(tree: Tree, options: Options) {
generateFiles(tree, joinPathFragments(__dirname, '../files'), options.projectConfig.root, { tmpl: '' });

updateJson(tree, options.paths.tsconfig.main, (json: TsConfig) => {
json.references?.push({
path: `./${path.basename(options.paths.tsconfig.cypress)}`,
});

return json;
});

// update lib ts with new exclude globs
updateJson(tree, options.paths.tsconfig.lib, (json: TsConfig) => {
json.exclude = json.exclude || [];
json.exclude.push(...['**/*.cy.ts', '**/*.cy.tsx']);
json.exclude = uniqueArray(json.exclude);

return json;
});

updateJson(tree, options.paths.packageJson, (json: PackageJson) => {
json.scripts = json.scripts ?? {};
json.scripts.e2e = 'cypress run --component';
json.scripts['e2e:local'] = 'cypress open --component';

return json;
});

return tree;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { uniqueArray } from './utils';

describe(`utils`, () => {
it(`should create uniquie array`, () => {
expect(uniqueArray(['a', 'b', 'a', 'c', 'd', 'b'])).toEqual(['a', 'b', 'c', 'd']);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function uniqueArray<T extends unknown>(value: T[]) {
return Array.from(new Set(value));
}
20 changes: 20 additions & 0 deletions tools/generators/cypress-component-configuration/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "http://json-schema.org/schema",
"cli": "nx",
"id": "cypress-component-configuration",
"type": "object",
"title": "Set up Cypress component testing for a project",
"description": "Set up Cypress component test for a project.",
"properties": {
"project": {
"type": "string",
"description": "The name of the project to add cypress component testing to",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What project should we add Cypress component testing to?"
}
},
"required": ["project"]
}
6 changes: 6 additions & 0 deletions tools/generators/cypress-component-configuration/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface CypressComponentConfigurationGeneratorSchema {
/**
* The name of the project to add cypress component testing to
*/
project: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import { preset, task } from '@fluentui/scripts-tasks';

preset();

task('build', 'build:react-components').cached?.();`
task('build', 'build:react-components').cached?.();
Loading