diff --git a/packages/create-qawolf/package.json b/packages/create-qawolf/package.json index 32b15f94c..6a54b6207 100644 --- a/packages/create-qawolf/package.json +++ b/packages/create-qawolf/package.json @@ -8,7 +8,8 @@ "types": "./build/index.d.ts", "files": [ "build", - "src" + "src", + "static" ], "engines": { "node": ">=10.15.0" @@ -27,7 +28,6 @@ "glob": "^7.1.6", "inquirer": "^7.1.0", "kleur": "^3.0.3", - "playwright-ci": "^1.0.0", "tslib": "^2.0.0" }, "devDependencies": { diff --git a/packages/create-qawolf/src/cli.ts b/packages/create-qawolf/src/cli.ts index e737f92c6..1c69ce5fa 100644 --- a/packages/create-qawolf/src/cli.ts +++ b/packages/create-qawolf/src/cli.ts @@ -1,5 +1,7 @@ -import { prompt } from 'inquirer'; +import { outputFile, pathExists, readFileSync } from 'fs-extra'; +import inquirer from 'inquirer'; import { bold, cyan } from 'kleur'; +import { join, resolve } from 'path'; import { getPackageJsonPath } from './packageJson'; import { Packages } from './types'; @@ -21,7 +23,7 @@ export const logInstallDependencies = ( ): void => { console.log(cyan(`Installing dependencies`)); - Object.keys(packages).forEach(name => { + Object.keys(packages).forEach((name) => { const version = packages[name]; console.log( cyan( @@ -32,20 +34,18 @@ export const logInstallDependencies = ( }; export const logUseTypeScript = (useTypeScript: boolean): void => { - console.log( - cyan( - `TypeScript ${useTypeScript ? '✔️' : '✖️'} tsconfig.json ${ - useTypeScript ? 'found' : 'not found' - }`, - ), - ); + const message = useTypeScript + ? 'useTypeScript: true (tsconfig.json found)' + : 'useTypeScript: false (tsconfig.json not found)'; + + console.log(cyan(message)); }; export const promptRootDir = async (): Promise => { // create a line break before our CLI prompt console.log(''); - const { rootDir } = await prompt<{ rootDir: string }>({ + const { rootDir } = await inquirer.prompt<{ rootDir: string }>({ default: '.qawolf', message: 'rootDir: Directory to create tests in', name: 'rootDir', @@ -54,3 +54,51 @@ export const promptRootDir = async (): Promise => { return rootDir; }; + +export const promptConfirmOverwrite = async ( + path: string, +): Promise => { + const answers = await inquirer.prompt([ + { + default: false, + message: `"${path}" already exists, overwrite it?`, + name: 'overwrite', + type: 'confirm', + }, + ]); + + return answers.overwrite; +}; + +export const promptOverwrite = async (path: string): Promise => { + const exists = await pathExists(path); + if (!exists) return true; + + return promptConfirmOverwrite(path); +}; + +export const promptGithubActions = async (): Promise => { + const answers = await inquirer.prompt([ + { + default: false, + message: `Set up CI with GitHub Actions?`, + name: 'setup', + type: 'confirm', + }, + ]); + + if (!answers.setup) return; + + const outputPath = join(process.cwd(), '.github/workflows/qawolf.yml'); + const shouldWrite = await promptOverwrite(outputPath); + + const template = readFileSync( + resolve(__dirname, `../static/github.yml`), + 'utf8', + ); + + if (shouldWrite) { + await outputFile(outputPath, template, 'utf8'); + console.log(`Saved GitHub Actions template to ${outputPath}`); + } +}; diff --git a/packages/create-qawolf/src/config.ts b/packages/create-qawolf/src/config.ts index 760f44ab9..818f93837 100644 --- a/packages/create-qawolf/src/config.ts +++ b/packages/create-qawolf/src/config.ts @@ -1,7 +1,7 @@ import { writeFile } from 'fs-extra'; import glob from 'glob'; import { resolve } from 'path'; -import { promptOverwrite } from 'playwright-ci'; +import { promptOverwrite } from './cli'; type ConfigOptions = { rootDir: string; diff --git a/packages/create-qawolf/src/create.ts b/packages/create-qawolf/src/create.ts new file mode 100644 index 000000000..80ad33f7a --- /dev/null +++ b/packages/create-qawolf/src/create.ts @@ -0,0 +1,39 @@ +#!/usr/bin/env node +import { + logError, + logInstallDependencies, + logUseTypeScript, + promptGithubActions, + promptRootDir, +} from './cli'; +import { detectTypeScript, detectYarn, writeConfig } from './config'; +import { + addDevDependencies, + installDependencies, + readPackageJson, +} from './packageJson'; + +export const create = async (): Promise => { + try { + // run this first to ensure package.json + await readPackageJson(); + + const rootDir = await promptRootDir(); + + const useTypeScript = await detectTypeScript(); + logUseTypeScript(useTypeScript); + + await promptGithubActions(); + + await writeConfig({ rootDir, useTypeScript }); + + const packages = await addDevDependencies(useTypeScript); + + const isYarn = detectYarn(); + logInstallDependencies(packages, isYarn); + installDependencies(isYarn); + } catch (error) { + logError(error); + process.exit(1); + } +}; diff --git a/packages/create-qawolf/src/index.ts b/packages/create-qawolf/src/index.ts index 0e9345e0c..54f8a0505 100644 --- a/packages/create-qawolf/src/index.ts +++ b/packages/create-qawolf/src/index.ts @@ -1,39 +1,9 @@ #!/usr/bin/env node -import { install as installCi } from 'playwright-ci'; -import { - logError, - logInstallDependencies, - logUseTypeScript, - promptRootDir, -} from './cli'; -import { detectTypeScript, detectYarn, writeConfig } from './config'; -import { - addDevDependencies, - installDependencies, - readPackageJson, -} from './packageJson'; +import { create } from './create'; -(async (): Promise => { - try { - // run this first to ensure package.json - await readPackageJson(); +const isCLI = !module.parent; +if (isCLI) { + create(); +} - const rootDir = await promptRootDir(); - - const useTypeScript = await detectTypeScript(); - logUseTypeScript(useTypeScript); - - await installCi(true); - - await writeConfig({ rootDir, useTypeScript }); - - const packages = await addDevDependencies(useTypeScript); - - const isYarn = detectYarn(); - logInstallDependencies(packages, isYarn); - installDependencies(isYarn); - } catch (error) { - logError(error); - process.exit(1); - } -})(); +export { promptOverwrite } from './cli'; diff --git a/packages/create-qawolf/static/github.yml b/packages/create-qawolf/static/github.yml new file mode 100644 index 000000000..072f0d656 --- /dev/null +++ b/packages/create-qawolf/static/github.yml @@ -0,0 +1,47 @@ +name: qawolf +on: + push: + # test every branch + # edit below if you only want certain branches tested + branches: "*" + # schedule: + # # test on schedule using cron syntax + # - cron: "0 * * * *" # every hour +jobs: + test: + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-node@v1 + + - uses: microsoft/playwright-github-action@v1 + + - uses: actions/cache@v1 + with: + path: ~/.npm + key: $\{{ runner.os }}-node-$\{{ hashFiles('**/package-lock.json') }} + restore-keys: | + $\{{ runner.os }}-node- + + - run: npm install + + # - name: Start local server + # run: npm run start & npx wait-on http://localhost:3000 + + - run: npx qawolf test --headless + env: + # configure tests with environment variables + FFMPEG_PATH: /usr/bin/ffmpeg # for recording video + QAW_ARTIFACT_PATH: $\{{ github.workspace }}/artifacts + # you can also use GitHub secrets for environment variables + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets + # LOGIN_PASSWORD: $\{{ secrets.PASSWORD }} + + - name: Upload Artifacts + if: always() + uses: actions/upload-artifact@master + with: + name: qawolf + path: $\{{ github.workspace }}/artifacts \ No newline at end of file diff --git a/packages/create-qawolf/test/prompt.test.ts b/packages/create-qawolf/test/prompt.test.ts new file mode 100644 index 000000000..29bf0587a --- /dev/null +++ b/packages/create-qawolf/test/prompt.test.ts @@ -0,0 +1,40 @@ +import * as fsExtra from 'fs-extra'; +import * as prompt from '../src/cli'; + +const { promptOverwrite } = prompt; + +jest.mock('fs-extra'); + +/* eslint-disable @typescript-eslint/no-explicit-any */ +describe('promptOverwrite', () => { + afterAll(() => jest.restoreAllMocks()); + + it('returns true if path does not exist', async () => { + jest.spyOn(fsExtra, 'pathExists').mockReturnValue(false as any); + + const shouldSave = await promptOverwrite('myTest.test.js'); + expect(shouldSave).toBe(true); + }); + + it('returns true if path exists but can overwrite', async () => { + jest.spyOn(fsExtra, 'pathExists').mockReturnValue(true as any); + + jest + .spyOn(prompt, 'promptConfirmOverwrite') + .mockReturnValue(new Promise((resolve) => resolve(true))); + + const shouldSave = await promptOverwrite('myTest.test.js'); + expect(shouldSave).toBe(true); + }); + + it('returns false if path exists and cannot overwrite', async () => { + jest.spyOn(fsExtra, 'pathExists').mockReturnValue(true as any); + jest + .spyOn(prompt, 'promptConfirmOverwrite') + .mockReturnValue(new Promise((resolve) => resolve(false))); + + const shouldSave = await promptOverwrite('myTest.test.js'); + expect(shouldSave).toBe(false); + }); +}); +/* eslint-enable @typescript-eslint/no-explicit-any */