Skip to content

Commit d90860d

Browse files
feat(qwik-nx): extract e2e logic to a separate generator (#50)
1 parent e504fa2 commit d90860d

File tree

10 files changed

+189
-52
lines changed

10 files changed

+189
-52
lines changed

packages/qwik-nx/generators.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
"schema": "./src/generators/setup-tailwind/schema.json",
4545
"description": "Set up Tailwind configuration for a project.",
4646
"aliases": ["tailwind", "tw", "t"]
47+
},
48+
"e2e-project": {
49+
"factory": "./src/generators/e2e-project/generator",
50+
"schema": "./src/generators/e2e-project/schema.json",
51+
"description": "Create an E2E app for a Qwik app"
4752
}
4853
}
4954
}

packages/qwik-nx/src/generators/application/generator.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import { addStyledModuleDependencies } from '../../utils/add-styled-dependencies
1515
import { configureEslint } from '../../utils/configure-eslint';
1616
import setupTailwindGenerator from '../setup-tailwind/setup-tailwind';
1717
import { SetupTailwindOptions } from './../setup-tailwind/schema.d';
18-
import { addE2eProject } from './lib/add-e2e-project';
1918
import { NormalizedSchema, QwikAppGeneratorSchema } from './schema';
2019
import { getQwikApplicationProjectTargets } from './utils/get-qwik-application-project-params';
2120
import { normalizeOptions } from './utils/normalize-options';
21+
import { addE2eProject } from '../e2e-project/generator';
2222

2323
function addFiles(tree: Tree, options: NormalizedSchema) {
2424
const templateOptions = {
@@ -77,8 +77,15 @@ export async function appGenerator(
7777

7878
tasks.push(addCommonQwikDependencies(tree));
7979

80-
const e2eProjectTask = await addE2eProject(tree, normalizedOptions);
81-
tasks.push(e2eProjectTask);
80+
if (normalizedOptions.e2eTestRunner !== 'none') {
81+
const e2eProjectTask = await addE2eProject(tree, {
82+
project: normalizedOptions.projectName,
83+
directory: normalizedOptions.directory,
84+
e2eTestRunner: normalizedOptions.e2eTestRunner,
85+
skipFormat: true,
86+
});
87+
tasks.push(e2eProjectTask);
88+
}
8289

8390
if (!options.skipFormat) {
8491
await formatFiles(tree);

packages/qwik-nx/src/generators/application/lib/add-e2e-project.ts

Lines changed: 0 additions & 45 deletions
This file was deleted.

packages/qwik-nx/src/generators/application/schema.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,4 @@ export interface NormalizedSchema extends QwikAppGeneratorSchema {
2222
setupVitest: boolean;
2323
parsedTags: string[];
2424
styleExtension: Exclude<QwikAppGeneratorSchema['style'], 'none'> | null;
25-
e2eProjectName: string;
2625
}

packages/qwik-nx/src/generators/application/utils/normalize-options.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ export const normalizeOptions = (
3737

3838
const styleExtension = options.style !== 'none' ? options.style : null;
3939

40-
const e2eProjectName = `${names(options.name).fileName}-e2e`;
41-
4240
return {
4341
...options,
4442
projectName: appProjectName,
@@ -48,6 +46,5 @@ export const normalizeOptions = (
4846
styleExtension,
4947
setupVitest: options.unitTestRunner === 'vitest',
5048
parsedTags,
51-
e2eProjectName,
5249
};
5350
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
2+
import { Tree, readProjectConfiguration } from '@nrwl/devkit';
3+
import { appGenerator } from './../application/generator';
4+
5+
import generator from './generator';
6+
import { E2eProjectGeneratorSchema } from './schema';
7+
8+
// eslint-disable-next-line @typescript-eslint/no-var-requires
9+
const devkit = require('@nrwl/devkit');
10+
11+
describe('e2e project', () => {
12+
let appTree: Tree;
13+
const defaultOptions: Omit<E2eProjectGeneratorSchema, 'e2eTestRunner'> = {
14+
project: 'myapp',
15+
};
16+
17+
jest.spyOn(devkit, 'ensurePackage').mockReturnValue(Promise.resolve());
18+
19+
beforeEach(() => {
20+
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
21+
appGenerator(appTree, {
22+
name: 'myapp',
23+
e2eTestRunner: 'none',
24+
});
25+
});
26+
27+
it('--e2eTestRunner playwright', async () => {
28+
await generator(appTree, {
29+
...defaultOptions,
30+
e2eTestRunner: 'playwright',
31+
});
32+
const config = readProjectConfiguration(appTree, 'myapp-e2e');
33+
expect(config).toBeDefined();
34+
expect(config.targets.e2e.executor).toEqual('@nxkit/playwright:test');
35+
expect(appTree.exists('apps/myapp-e2e/playwright.config.ts')).toBeTruthy();
36+
});
37+
38+
it('--e2eTestRunner cypress', async () => {
39+
await generator(appTree, {
40+
...defaultOptions,
41+
e2eTestRunner: 'cypress',
42+
});
43+
const config = readProjectConfiguration(appTree, 'myapp-e2e');
44+
expect(config).toBeDefined();
45+
expect(config.targets.e2e.executor).toEqual('@nrwl/cypress:cypress');
46+
expect(appTree.exists('apps/myapp-e2e/cypress.config.ts')).toBeTruthy();
47+
});
48+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {
2+
ensurePackage,
3+
GeneratorCallback,
4+
readProjectConfiguration,
5+
Tree,
6+
} from '@nrwl/devkit';
7+
import { getInstalledNxVersion } from '../../utils/get-installed-nx-version';
8+
import { nxKitVersion } from '../../utils/versions';
9+
import { E2eProjectGeneratorSchema, NormalizedSchema } from './schema';
10+
import { normalizeOptions } from './utils/normalize-options';
11+
12+
export async function addE2eProject(
13+
tree: Tree,
14+
options: E2eProjectGeneratorSchema
15+
): Promise<GeneratorCallback> {
16+
const projectConfiguration = readProjectConfiguration(tree, options.project);
17+
18+
if (projectConfiguration.projectType !== 'application') {
19+
throw new Error('Cannot setup e2e project for the given frontend project.');
20+
}
21+
22+
const normalizedOptions = normalizeOptions(tree, options);
23+
if (options.e2eTestRunner === 'cypress') {
24+
return addCypress(tree, normalizedOptions);
25+
}
26+
27+
if (options.e2eTestRunner === 'playwright') {
28+
return addPlaywright(tree, normalizedOptions);
29+
}
30+
}
31+
32+
async function addCypress(tree: Tree, options: NormalizedSchema) {
33+
await ensurePackage(tree, '@nrwl/cypress', getInstalledNxVersion(tree));
34+
const { cypressProjectGenerator } = await import('@nrwl/cypress');
35+
36+
return await cypressProjectGenerator(tree, {
37+
...options,
38+
name: options.e2eProjectName,
39+
directory: options.directory,
40+
project: options.project,
41+
bundler: 'vite',
42+
skipFormat: options.skipFormat,
43+
});
44+
}
45+
46+
async function addPlaywright(tree: Tree, options: NormalizedSchema) {
47+
await ensurePackage(tree, '@nxkit/playwright', nxKitVersion);
48+
const { projectGenerator } = await import('@nxkit/playwright');
49+
50+
return await projectGenerator(tree, {
51+
...options,
52+
name: options.e2eProjectName,
53+
directory: options.directory,
54+
frontendProject: options.project,
55+
skipFormat: options.skipFormat,
56+
});
57+
}
58+
59+
export default addE2eProject;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export interface E2eProjectGeneratorSchema {
2+
project: string;
3+
e2eTestRunner: 'playwright' | 'cypress';
4+
directory?: string;
5+
skipFormat?: boolean;
6+
}
7+
8+
export interface NormalizedSchema extends E2eProjectGeneratorSchema {
9+
e2eProjectName: string;
10+
projectRoot: string;
11+
projectDirectory: string;
12+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"cli": "nx",
4+
"$id": "E2eProject",
5+
"title": "Create an E2E app for a Qwik app",
6+
"type": "object",
7+
"properties": {
8+
"project": {
9+
"type": "string",
10+
"description": "",
11+
"$default": {
12+
"$source": "argv",
13+
"index": 0
14+
},
15+
"x-prompt": "The name of the frontend project to test."
16+
},
17+
"e2eTestRunner": {
18+
"type": "string",
19+
"enum": ["playwright", "cypress"],
20+
"description": "Test runner to use for end to end (E2E) tests.",
21+
"default": "playwright"
22+
},
23+
"directory": {
24+
"type": "string",
25+
"description": "A directory where the project is placed"
26+
},
27+
"skipFormat": {
28+
"description": "Skip formatting files.",
29+
"type": "boolean",
30+
"default": false
31+
}
32+
},
33+
"required": ["project", "e2eTestRunner"]
34+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { getWorkspaceLayout, names, Tree } from '@nrwl/devkit';
2+
import { E2eProjectGeneratorSchema, NormalizedSchema } from '../schema';
3+
4+
export function normalizeOptions(
5+
tree: Tree,
6+
options: E2eProjectGeneratorSchema
7+
): NormalizedSchema {
8+
const name = names(options.project + '-e2e').fileName;
9+
const projectDirectory = options.directory
10+
? `${names(options.directory).fileName}/${name}`
11+
: name;
12+
const e2eProjectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
13+
const projectRoot = `${getWorkspaceLayout(tree).libsDir}/${projectDirectory}`;
14+
15+
return {
16+
...options,
17+
e2eProjectName,
18+
projectRoot,
19+
projectDirectory,
20+
};
21+
}

0 commit comments

Comments
 (0)