Skip to content

Commit

Permalink
feat(testing): add playwright configuration generator
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Jul 7, 2023
1 parent 25e5f2b commit 67218b1
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 145 deletions.
154 changes: 18 additions & 136 deletions e2e/playwright/src/playwright.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
cleanupProject,
newProject,
uniq,
createFile,
runCLI,
packageInstall,
} from '@nx/e2e/utils';
Expand All @@ -19,9 +18,9 @@ describe('Playwright E2E Test runner', () => {
it(
'should test example app',
() => {
//TODO: remove when generators are setup.Need to have basic workspace deps setup
runCLI(`g @nx/js:init`);
addSampleProject();
//TODO: remove when generators are setup. Need to have basic workspace deps setup
runCLI(`g @nx/js:lib demo-e2e --unitTestRunner none --bundler none`);
runCLI(`g @nx/playwright:configuration demo-e2e`);

// NOTE: playwright throws errors if it detects running inside jest process. tmp remove and restore the env var for playwright to run
const id = process.env.JEST_WORKER_ID;
Expand All @@ -34,140 +33,23 @@ describe('Playwright E2E Test runner', () => {
},
TEN_MINS_MS
);
});

// TODO: remove this when there are project generators
function addSampleProject() {
createFile(
'apps/demo-e2e/src/example.spec.ts',
`
import { test, expect } from '@playwright/test';
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
});
test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Click the get started link.
await page.getByRole('link', { name: 'Get started' }).click();
// Expects the URL to contain intro.
await expect(page).toHaveURL(/.*intro/);
});
`
);
createFile(
'apps/demo-e2e/playwright.config.ts',
`
import { defineConfig, devices } from '@playwright/test';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './src',
outputDir: '../../dist/playwright/apps/demo-e2e/output',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [
[
'html',
{
outputFolder:
'../../dist/playwright/apps/demo-e2e/playwright-report',
},
],
],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like await page.goto('/'). */
// baseURL: 'http://127.0.0.1:3000',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
it(
'should test example app with js',
() => {
//TODO: remove when generators are setup. Need to have basic workspace deps setup
runCLI(`g @nx/js:lib demo-e2e --unitTestRunner none --bundler none --js`);
runCLI(`g @nx/playwright:configuration demo-e2e --js`);

{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
// NOTE: playwright throws errors if it detects running inside jest process. tmp remove and restore the env var for playwright to run
const id = process.env.JEST_WORKER_ID;
delete process.env.JEST_WORKER_ID;
const results = runCLI(`e2e demo-e2e`);
expect(results).toContain('6 passed');
expect(results).toContain('Successfully ran target e2e for project');

{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
process.env.JEST_WORKER_ID = id;
},
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});
`
);
createFile(
'apps/demo-e2e/project.json',
JSON.stringify(
{
name: 'demo-e2e',
root: 'apps/demo-e2e',
sourceRoot: 'apps/demo-e2e/src',
targets: {
e2e: {
executor: '@nx/playwright:playwright',
options: {},
},
},
},
null,
2
)
TEN_MINS_MS
);
}
});
18 changes: 18 additions & 0 deletions packages/playwright/generators.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Nx Playwright",
"version": "0.1",
"schematics": {
"configuration": {
"factory": "./src/generators/configuration/generator#configurationSchematic",
"schema": "./src/generators/configuration/schema.json",
"description": "Add Nx Playwright configuration to your project"
}
},
"generators": {
"configuration": {
"factory": "./src/generators/configuration/generator",
"schema": "./src/generators/configuration/schema.json",
"description": "Add Nx Playwright configuration to your project"
}
}
}
3 changes: 2 additions & 1 deletion packages/playwright/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
"optional": true
}
},
"executors": "./executors.json"
"executors": "./executors.json",
"generators": "./generators.json"
}
18 changes: 10 additions & 8 deletions packages/playwright/src/executors/playwright/playwright.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fork } from 'child_process';
import { ExecutorContext } from '@nx/devkit';
import { ExecutorContext, names } from '@nx/devkit';

export async function playwrightExecutor(
options: PlaywrightExecutorSchema,
Expand Down Expand Up @@ -28,14 +28,17 @@ function createArgs(opts: PlaywrightExecutorSchema): string[] {

for (const key in opts) {
const value = opts[key];
const keyInKebobCase = names(key).fileName;

if (Array.isArray(value)) {
args.push(`--${key}=${value.map((v) => v.trim()).join(',')}`);
} else if (typeof value === 'boolean' && value) {
args.push(`--${key}`);
args.push(`--${keyInKebobCase}=${value.map((v) => v.trim()).join(',')}`);
} else if (typeof value === 'boolean') {
if (value) {
args.push(`--${keyInKebobCase}`);
}
} else {
args.push(`--${keyInKebobCase}=${value}`);
}

args.push(`--${key}=${value}`);
}

return args;
Expand All @@ -45,8 +48,7 @@ function runPlaywright(args: string[], cwd: string) {
try {
const cli = require.resolve('@playwright/test/cli');

return fork(cli, {
execArgv: ['test', ...args],
return fork(cli, ['test', ...args], {
stdio: 'inherit',
cwd,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { defineConfig, devices } from '@playwright/test';

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://127.0.0.1:3000',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},

/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},

{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},

{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},

/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },

/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],

/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { test, expect } from '@playwright/test';

test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');

// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
});

test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');

// Click the get started link.
await page.getByRole('link', { name: 'Get started' }).click();

// Expects the URL to contain intro.
await expect(page).toHaveURL(/.*intro/);
});
20 changes: 20 additions & 0 deletions packages/playwright/src/generators/configuration/generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { Tree, readProjectConfiguration } from '@nx/devkit';

import { configurationGenerator } from './generator';
import { ConfigurationGeneratorSchema } from './schema';

describe('configuration generator', () => {
let tree: Tree;
const options: ConfigurationGeneratorSchema = { name: 'test' };

beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});

it('should run successfully', async () => {
await configurationGenerator(tree, options);
const config = readProjectConfiguration(tree, 'test');
expect(config).toBeDefined();
});
});
Loading

0 comments on commit 67218b1

Please sign in to comment.