diff --git a/README.md b/README.md index 9d80ddb..95338f1 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,9 @@ The base URI can also be changed from the run command docker-compose run e2e-tests --baseUri http://dev-env.my.app/ ``` -Finally, tests can be filtered by a regular expression which is matched against the relative path to test files within +#### Filtering tests to run + +Tests can be filtered by a regular expression which is matched against the relative path to test files within the `tests` directory. For example, running from the above `docker-compose.yml`: @@ -67,4 +69,13 @@ For example, running from the above `docker-compose.yml`: docker-compose run e2e-tests --grep ^ProductsCollectionEndpoint/PostRequest_ ``` +#### Compile scenarios in-place + +By default the tests will be compiled in a temporary path. An optional flag +can be set to have the test JSON files generated adjacent to the input files. + +``` +docker-compose run e2e-tests --compileInPlace +``` + [hypertest]: https://testing.hypermedia.app/dsl/ diff --git a/hypertest/entrypoint.ts b/hypertest/entrypoint.ts index c8c57d3..d65be53 100644 --- a/hypertest/entrypoint.ts +++ b/hypertest/entrypoint.ts @@ -14,6 +14,9 @@ if (grep) { if (argv.dir) { args.push('--dir', `${argv.dir}`) } +if (argv.compileInPlace) { + args.push('--compileInPlace') +} const analyser = spawn( 'node', diff --git a/hypertest/package-lock.json b/hypertest/package-lock.json index 6effc4f..e4c36e9 100644 --- a/hypertest/package-lock.json +++ b/hypertest/package-lock.json @@ -131,6 +131,15 @@ "@types/node": "*" } }, + "@types/temp": { + "version": "0.8.34", + "resolved": "https://registry.npmjs.org/@types/temp/-/temp-0.8.34.tgz", + "integrity": "sha512-oLa9c5LHXgS6UimpEVp08De7QvZ+Dfu5bMQuWyMhf92Z26Q10ubEMOWy9OEfUdzW7Y/sDWVHmUaLFtmnX/2j0w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "13.0.3", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", @@ -685,6 +694,11 @@ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, + "copy-dir": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/copy-dir/-/copy-dir-1.2.0.tgz", + "integrity": "sha512-UeaUFUIHqGV4brjQ9bY2mHll/hoxyCak2emgmYG72LkGKABHBdBDKFw0H5nTKt5OzVBkBhkxCwnniJBAE7EGEw==" + }, "core-decorators": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/core-decorators/-/core-decorators-0.20.0.tgz", @@ -2975,7 +2989,6 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -3471,6 +3484,14 @@ } } }, + "temp": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.1.tgz", + "integrity": "sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==", + "requires": { + "rimraf": "~2.6.2" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/hypertest/package.json b/hypertest/package.json index b2d8702..ad3753b 100644 --- a/hypertest/package.json +++ b/hypertest/package.json @@ -11,13 +11,16 @@ "@fcostarodrigo/walk": "^4.0.2", "@hydrofoil/hypertest": "^0.7", "commander": "^4.0.1", + "copy-dir": "^1.2.0", "hydra-validator": "^1.1.1", "hydra-validator-e2e": "^0.8.2", + "temp": "^0.9.1", "typescript": "^3.7.2", "yargs": "^15.0.2" }, "devDependencies": { "@tpluscode/eslint-config": "0.0.3", + "@types/temp": "^0.8.34", "@types/yargs": "^13.0.3", "@typescript-eslint/eslint-plugin": "^2.9.0", "@typescript-eslint/parser": "^2.9.0", diff --git a/hypertest/run.ts b/hypertest/run.ts index 56d0fe9..3158f95 100755 --- a/hypertest/run.ts +++ b/hypertest/run.ts @@ -1,4 +1,6 @@ import { spawn } from 'child_process' +import { mkdir } from 'temp' +import copydir from 'copy-dir' import program from 'commander' import walk from '@fcostarodrigo/walk' @@ -15,6 +17,7 @@ interface Scenario { program.option('--dir ', 'Directory to run tests from', '/tests') program.option('--grep ', 'RegExp to filter the test cases') program.option('--baseUri ', 'Base resource URI') +program.option('--compileInPlace', 'Compile in the same directory', false) program.parse(process.argv) @@ -26,14 +29,39 @@ ${texts.map(text => ` ${text}`).join('\n')} `) } -function parseScenarios() { +function copyScenarios(): Promise { return new Promise((resolve, reject) => { - headerLog('Compiling test scenarios', `Directory used: ${program.dir}`) - const childProcess = spawn('node_modules/.bin/hypertest-compiler', [program.dir], { stdio: 'inherit' }) + if (program.compileInPlace) { + resolve(program.dir) + return + } + + mkdir('', (err, tempDir) => { + if (err) { + reject(err) + return + } + + copydir.sync(program.dir, tempDir) + + resolve(tempDir) + }) + }) +} + +function parseScenarios(dir: string): Promise { + return new Promise((resolve, reject) => { + const headerMessages = ['Compiling test scenarios', `Directory used: ${program.dir}`] + if (dir !== program.dir) { + headerMessages.push(`Using temp directory for compiled output: ${dir}`) + } + + headerLog(...headerMessages) + const childProcess = spawn('node_modules/.bin/hypertest-compiler', [dir], { stdio: 'inherit' }) childProcess.on('exit', code => { if (code === 0) { - resolve() + resolve(dir) } reject(new Error('Failed to compile test scenarios')) @@ -41,11 +69,11 @@ function parseScenarios() { }) } -async function filterScenarios() { +async function filterScenarios(dir: string) { const scenarios: Scenario[] = [] const skipped: string[] = [] - for await (const file of walk(program.dir)) { - const matches = file.match(new RegExp(`${program.dir}/(.+)\\..+\\.hypertest\\.json$`)) + for await (const file of walk(dir)) { + const matches = file.match(new RegExp(`${dir}/(.+)\\..+\\.hypertest\\.json$`)) if (!matches) { continue @@ -116,7 +144,8 @@ function summary(summary: Summary) { process.exit(summary.failures.length) } -parseScenarios() +copyScenarios() + .then(parseScenarios) .then(filterScenarios) .then(runScenarios) .then(summary)