chore(create-playwright): UX follow-ups (#8897)
This commit is contained in:
parent
95e1cfa0c1
commit
299a55c562
|
|
@ -19,11 +19,9 @@ const config = {
|
||||||
|
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'Desktop Chromium',
|
name: 'Desktop Chrome',
|
||||||
use: {
|
use: {
|
||||||
browserName: 'chromium',
|
...devices['Desktop Chrome'],
|
||||||
// Test against Chrome Beta channel.
|
|
||||||
channel: 'chrome-beta',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,9 @@ const config: PlaywrightTestConfig = {
|
||||||
|
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'Chrome Stable',
|
name: 'Desktop Chrome',
|
||||||
use: {
|
use: {
|
||||||
browserName: 'chromium',
|
...devices['Desktop Chrome'],
|
||||||
// Test against Chrome Stable channel.
|
|
||||||
channel: 'chrome',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "create-playwright",
|
"name": "create-playwright",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"description": "Getting started with writing end-to-end tests with Playwright.",
|
"description": "Getting started with writing end-to-end tests with Playwright.",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import fs from 'fs';
|
||||||
import { prompt } from 'enquirer';
|
import { prompt } from 'enquirer';
|
||||||
import colors from 'ansi-colors';
|
import colors from 'ansi-colors';
|
||||||
|
|
||||||
import { executeCommands, createFiles, determinePackageManager, executeTemplate, determineRootDir } from './utils';
|
import { executeCommands, createFiles, determinePackageManager, executeTemplate, determineRootDir, Command, languagetoFileExtension } from './utils';
|
||||||
|
|
||||||
export type PromptOptions = {
|
export type PromptOptions = {
|
||||||
testDir: string,
|
testDir: string,
|
||||||
|
|
@ -27,6 +27,8 @@ export type PromptOptions = {
|
||||||
language: 'JavaScript' | 'TypeScript'
|
language: 'JavaScript' | 'TypeScript'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PACKAGE_JSON_TEST_SCRIPT_CMD = 'test:e2e';
|
||||||
|
|
||||||
class Generator {
|
class Generator {
|
||||||
packageManager: 'npm' | 'yarn';
|
packageManager: 'npm' | 'yarn';
|
||||||
constructor(private readonly rootDir: string) {
|
constructor(private readonly rootDir: string) {
|
||||||
|
|
@ -37,12 +39,12 @@ class Generator {
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
this._printIntro();
|
this._printIntro();
|
||||||
const questions = await this._askQuestions();
|
const answers = await this._askQuestions();
|
||||||
const { files, commands } = await this._identifyChanges(questions);
|
const { files, commands } = await this._identifyChanges(answers);
|
||||||
executeCommands(this.rootDir, commands);
|
executeCommands(this.rootDir, commands);
|
||||||
await createFiles(this.rootDir, files);
|
await createFiles(this.rootDir, files);
|
||||||
await this._patchPackageJSON();
|
await this._patchPackageJSON();
|
||||||
this._printOutro();
|
this._printOutro(answers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _printIntro() {
|
private _printIntro() {
|
||||||
|
|
@ -66,28 +68,28 @@ class Generator {
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'testDir',
|
name: 'testDir',
|
||||||
message: 'Where to put your integration tests?',
|
message: 'Where to put your end-to-end tests?',
|
||||||
initial: 'e2e'
|
initial: 'e2e'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
name: 'installGitHubActions',
|
name: 'installGitHubActions',
|
||||||
message: 'Add GitHub Actions workflow?',
|
message: 'Add a GitHub Actions workflow?',
|
||||||
initial: true,
|
initial: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _identifyChanges(options: PromptOptions) {
|
private async _identifyChanges(answers: PromptOptions) {
|
||||||
const commands: string[] = [];
|
const commands: Command[] = [];
|
||||||
const files = new Map<string, string>();
|
const files = new Map<string, string>();
|
||||||
const fileExtension = options.language === 'JavaScript' ? 'js' : 'ts';
|
const fileExtension = languagetoFileExtension(answers.language);
|
||||||
|
|
||||||
files.set(`playwright.config.${fileExtension}`, executeTemplate(this._readAsset(`playwright.config.${fileExtension}`), {
|
files.set(`playwright.config.${fileExtension}`, executeTemplate(this._readAsset(`playwright.config.${fileExtension}`), {
|
||||||
testDir: options.testDir,
|
testDir: answers.testDir,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (options.installGitHubActions) {
|
if (answers.installGitHubActions) {
|
||||||
const githubActionsScript = executeTemplate(this._readAsset('github-actions.yml'), {
|
const githubActionsScript = executeTemplate(this._readAsset('github-actions.yml'), {
|
||||||
installDepsCommand: this.packageManager === 'npm' ? 'npm ci' : 'yarn',
|
installDepsCommand: this.packageManager === 'npm' ? 'npm ci' : 'yarn',
|
||||||
runTestsCommand: commandToRunTests(this.packageManager),
|
runTestsCommand: commandToRunTests(this.packageManager),
|
||||||
|
|
@ -95,21 +97,40 @@ class Generator {
|
||||||
files.set('.github/workflows/playwright.yml', githubActionsScript);
|
files.set('.github/workflows/playwright.yml', githubActionsScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
files.set(path.join(options.testDir, `example.spec.${fileExtension}`), this._readAsset(`example.spec.${fileExtension}`));
|
files.set(path.join(answers.testDir, `example.spec.${fileExtension}`), this._readAsset(`example.spec.${fileExtension}`));
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(this.rootDir, 'package.json')))
|
if (!fs.existsSync(path.join(this.rootDir, 'package.json'))) {
|
||||||
commands.push(this.packageManager === 'yarn' ? 'yarn init -y' : 'npm init -y');
|
commands.push({
|
||||||
|
name: `Initializing ${this.packageManager === 'yarn' ? 'Yarn' : 'NPM'} project`,
|
||||||
|
command: this.packageManager === 'yarn' ? 'yarn init -y' : 'npm init -y',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (this.packageManager === 'yarn')
|
commands.push({
|
||||||
commands.push('yarn add --dev @playwright/test');
|
name: 'Installing Playwright Test',
|
||||||
else
|
command: this.packageManager === 'yarn' ? 'yarn add --dev @playwright/test' : 'npm install --save-dev @playwright/test',
|
||||||
commands.push('npm install --save-dev @playwright/test');
|
});
|
||||||
|
|
||||||
commands.push('npx playwright install --with-deps');
|
commands.push({
|
||||||
|
name: 'Downloading browsers',
|
||||||
|
command: 'npx playwright install --with-deps',
|
||||||
|
});
|
||||||
|
|
||||||
|
files.set('.gitignore', this._buildGitIgnore());
|
||||||
|
|
||||||
return { files, commands };
|
return { files, commands };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _buildGitIgnore(): string {
|
||||||
|
let gitIgnore = '';
|
||||||
|
if (fs.existsSync(path.join(this.rootDir, '.gitignore')))
|
||||||
|
gitIgnore = fs.readFileSync(path.join(this.rootDir, '.gitignore'), 'utf-8').trimEnd() + '\n';
|
||||||
|
if (!gitIgnore.includes('node_modules'))
|
||||||
|
gitIgnore += 'node_modules/\n';
|
||||||
|
gitIgnore += 'test-results/\n';
|
||||||
|
return gitIgnore;
|
||||||
|
}
|
||||||
|
|
||||||
private _readAsset(asset: string): string {
|
private _readAsset(asset: string): string {
|
||||||
const assetsDir = path.join(__dirname, '..', 'assets');
|
const assetsDir = path.join(__dirname, '..', 'assets');
|
||||||
return fs.readFileSync(path.join(assetsDir, asset), 'utf-8');
|
return fs.readFileSync(path.join(assetsDir, asset), 'utf-8');
|
||||||
|
|
@ -119,26 +140,47 @@ class Generator {
|
||||||
const packageJSON = JSON.parse(fs.readFileSync(path.join(this.rootDir, 'package.json'), 'utf-8'));
|
const packageJSON = JSON.parse(fs.readFileSync(path.join(this.rootDir, 'package.json'), 'utf-8'));
|
||||||
if (!packageJSON.scripts)
|
if (!packageJSON.scripts)
|
||||||
packageJSON.scripts = {};
|
packageJSON.scripts = {};
|
||||||
packageJSON.scripts['playwright-tests'] = `playwright test`;
|
if (packageJSON.scripts['test']?.includes('no test specified'))
|
||||||
|
delete packageJSON.scripts['test'];
|
||||||
|
packageJSON.scripts[PACKAGE_JSON_TEST_SCRIPT_CMD] = `playwright test`;
|
||||||
|
|
||||||
const files = new Map<string, string>();
|
const files = new Map<string, string>();
|
||||||
files.set('package.json', JSON.stringify(packageJSON, null, 2));
|
files.set('package.json', JSON.stringify(packageJSON, null, 2) + '\n'); // NPM keeps a trailing new-line
|
||||||
await createFiles(this.rootDir, files, true);
|
await createFiles(this.rootDir, files, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _printOutro() {
|
private _printOutro(answers: PromptOptions) {
|
||||||
console.log(colors.green('✔'), colors.bold('Successfully initialized your Playwright Test project!'));
|
console.log(colors.green('✔ Success!') + ' ' + colors.bold(`Created a Playwright Test project at ${this.rootDir}`));
|
||||||
const pathToNavigate = path.relative(process.cwd(), this.rootDir);
|
const pathToNavigate = path.relative(process.cwd(), this.rootDir);
|
||||||
const prefix = pathToNavigate !== '' ? `- cd ${pathToNavigate}\n` : '';
|
const prefix = pathToNavigate !== '' ? ` cd ${pathToNavigate}\n` : '';
|
||||||
console.log(colors.bold('🎭 Try it out with:\n') + colors.greenBright(prefix + '- ' + commandToRunTests(this.packageManager)));
|
console.log(`Inside that directory, you can run several commands:
|
||||||
console.log('Visit https://playwright.dev/docs/intro for more information');
|
|
||||||
|
${colors.cyan(commandToRunTests(this.packageManager))}
|
||||||
|
Runs the end-to-end tests.
|
||||||
|
|
||||||
|
${colors.cyan(commandToRunTests(this.packageManager) + ' -- --project=Desktop Chrome')}
|
||||||
|
Runs the tests only on Desktop Chrome.
|
||||||
|
|
||||||
|
${colors.cyan(commandToRunTests(this.packageManager) + ` -- ${answers.testDir}${path.sep}example.spec.${languagetoFileExtension(answers.language)}`)}
|
||||||
|
Runs the tests of a specific file.
|
||||||
|
|
||||||
|
${colors.cyan((this.packageManager === 'npm' ? 'npx' : 'yarn') + ' playwright debug ' + commandToRunTests(this.packageManager))}
|
||||||
|
Runs the tests in debug mode.
|
||||||
|
|
||||||
|
We suggest that you begin by typing:
|
||||||
|
|
||||||
|
${colors.cyan(prefix + ' ' + commandToRunTests(this.packageManager))}
|
||||||
|
|
||||||
|
Visit https://playwright.dev/docs/intro for more information. ✨
|
||||||
|
|
||||||
|
Happy hacking! 🎭`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function commandToRunTests(packageManager: 'npm' | 'yarn') {
|
export function commandToRunTests(packageManager: 'npm' | 'yarn') {
|
||||||
if (packageManager === 'yarn')
|
if (packageManager === 'yarn')
|
||||||
return 'yarn playwright-tests';
|
return `yarn ${PACKAGE_JSON_TEST_SCRIPT_CMD}`;
|
||||||
return 'npm run playwright-tests';
|
return `npm run ${PACKAGE_JSON_TEST_SCRIPT_CMD}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,14 @@ import path from 'path';
|
||||||
import { prompt } from 'enquirer';
|
import { prompt } from 'enquirer';
|
||||||
import colors from 'ansi-colors';
|
import colors from 'ansi-colors';
|
||||||
|
|
||||||
|
export type Command = {
|
||||||
|
command: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
export function executeCommands(cwd: string, commands: string[]) {
|
export function executeCommands(cwd: string, commands: Command[]) {
|
||||||
for (const command of commands) {
|
for (const { command, name } of commands) {
|
||||||
console.log('Running:', command);
|
console.log(`${name} (${command})…`);
|
||||||
execSync(command, {
|
execSync(command, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
cwd,
|
cwd,
|
||||||
|
|
@ -72,3 +76,7 @@ export function executeTemplate(input: string, args: Record<string, string>): st
|
||||||
input = input.replace(`{{${key}}}`, args[key]);
|
input = input.replace(`{{${key}}}`, args[key]);
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function languagetoFileExtension(language: 'JavaScript' | 'TypeScript'): 'js' | 'ts' {
|
||||||
|
return language === 'JavaScript' ? 'js' : 'ts';
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,14 +84,15 @@ for (const packageManager of ['npm', 'yarn'] as ('npm'|'yarn')[]) {
|
||||||
const playwrightConfigContent = fs.readFileSync(path.join(dir, 'playwright.config.ts'), 'utf8');
|
const playwrightConfigContent = fs.readFileSync(path.join(dir, 'playwright.config.ts'), 'utf8');
|
||||||
expect(playwrightConfigContent).toContain('e2e');
|
expect(playwrightConfigContent).toContain('e2e');
|
||||||
expect(fs.existsSync(path.join(dir, '.github/workflows/playwright.yml'))).toBeTruthy();
|
expect(fs.existsSync(path.join(dir, '.github/workflows/playwright.yml'))).toBeTruthy();
|
||||||
|
expect(fs.existsSync(path.join(dir, '.gitignore'))).toBeTruthy();
|
||||||
if (packageManager === 'npm') {
|
if (packageManager === 'npm') {
|
||||||
expect(stdout).toContain('Running: npm init -y');
|
expect(stdout).toContain('Initializing NPM project (npm init -y)…');
|
||||||
expect(stdout).toContain('Running: npm install --save-dev @playwright/test');
|
expect(stdout).toContain('Installing Playwright Test (npm install --save-dev @playwright/test)…');
|
||||||
} else {
|
} else {
|
||||||
expect(stdout).toContain('Running: yarn init -y');
|
expect(stdout).toContain('Initializing Yarn project (yarn init -y)…');
|
||||||
expect(stdout).toContain('Running: yarn add --dev @playwright/test');
|
expect(stdout).toContain('Installing Playwright Test (yarn add --dev @playwright/test)…');
|
||||||
}
|
}
|
||||||
expect(stdout).toContain('Running: npx playwright install --with-deps');
|
expect(stdout).toContain('npx playwright install --with-deps');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should generate a project in a given directory', async ({ run }) => {
|
test('should generate a project in a given directory', async ({ run }) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue