From 2d7ef68be1ad077feadd7ae5c1f7dca78052a507 Mon Sep 17 00:00:00 2001 From: Tamay Eser Uysal Date: Fri, 24 Jun 2022 17:32:01 +0300 Subject: [PATCH 1/4] feat: implement status report --- package.json | 2 ++ src/CLIUtils.ts | 84 ++++++++++++++++++++++++++++++++++++++++++++++-- test/cli.test.ts | 56 +++++++++++++++++++++++++++----- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index f8d10b9b..7bffd8f0 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,8 @@ } }, "dependencies": { + "cli-table3": "^0.6.2", + "colors": "^1.4.0", "yargs": "17.5.1" } } diff --git a/src/CLIUtils.ts b/src/CLIUtils.ts index 34a72011..a2654926 100644 --- a/src/CLIUtils.ts +++ b/src/CLIUtils.ts @@ -16,11 +16,13 @@ import { parse, createRunner } from '../lib/main.js'; import { readFileSync, readdirSync, lstatSync } from 'fs'; -import { join, isAbsolute, extname } from 'path'; +import { join, isAbsolute, extname, relative } from 'path'; import { pathToFileURL } from 'url'; import { cwd } from 'process'; import { PuppeteerRunnerOwningBrowserExtension } from '../lib/main.js'; import { Browser } from 'puppeteer'; +import Table from 'cli-table3'; +import colors from 'colors'; export function getJSONFilesFromFolder(path: string): string[] { return readdirSync(path) @@ -75,13 +77,67 @@ export function getHeadlessEnvVar(headless?: string) { } } +type Result = { + startedAt: Date; + file: string; + finishedAt: Date; + passed: boolean; + title: string; +}; + +export function createStatusReport(results: Result[]): Table.Table { + const table = new Table({ + head: ['Title', 'Status', 'File', 'Duration'], + chars: { + top: '═', + 'top-mid': '╤', + 'top-left': '╔', + 'top-right': '╗', + bottom: '═', + 'bottom-mid': '╧', + 'bottom-left': '╚', + 'bottom-right': '╝', + left: '║', + 'left-mid': '╟', + mid: '─', + 'mid-mid': '┼', + right: '║', + 'right-mid': '╢', + middle: '│', + }, + style: { + head: ['bold'], + }, + }); + + const resultTextColor = colors.white; + for (const result of results) { + const row: string[] = []; + + const duration = + result.finishedAt?.getTime()! - result.startedAt.getTime() || 0; + const status = result.passed + ? resultTextColor.bgGreen(' Passed ') + : resultTextColor.bgRed(' Failed '); + + row.push(result.title); + row.push(status); + row.push(relative(process.cwd(), result.file)); + row.push(`${duration}ms`); + + table.push(row); + } + + return table; +} + export async function runFiles( files: string[], opts: { log: boolean; headless: boolean | 'chrome'; extension?: string } = { log: false, headless: true, } -): Promise { +): Promise { let Extension = PuppeteerRunnerOwningBrowserExtension; let browser: Browser | undefined; @@ -95,12 +151,24 @@ export async function runFiles( ); Extension = module.default; } + + const results: Result[] = []; for (const file of files) { + const result: Result = { + title: '', + startedAt: new Date(), + finishedAt: new Date(), + file, + passed: true, + }; + opts.log && console.log(`Running ${file}...`); try { const content = readFileSync(file, 'utf-8'); const object = JSON.parse(content); const recording = parse(object); + result['title'] = recording.title; + const { default: puppeteer } = await import('puppeteer'); browser = await puppeteer.launch({ headless: opts.headless, @@ -112,9 +180,19 @@ export async function runFiles( opts.log && console.log(`Finished running ${file}`); } catch (err) { opts.log && console.error(`Error running ${file}`, err); - throw err; + result['passed'] = false; } finally { + result['finishedAt'] = new Date(); + results.push(result); + await browser?.close(); } } + + if (opts.log) { + const statusReport = createStatusReport(results); + console.log(statusReport.toString()); + } + + return results.every((result) => result.passed); } diff --git a/test/cli.test.ts b/test/cli.test.ts index 1f21aa4c..d288b376 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -15,6 +15,7 @@ */ import { + createStatusReport, runFiles, getHeadlessEnvVar, getRecordingPaths, @@ -23,6 +24,8 @@ import { import { assert } from 'chai'; import path from 'path'; import url from 'url'; +import { HorizontalTableRow } from 'cli-table3'; +import colors from 'colors'; const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); @@ -34,14 +37,11 @@ enum Status { async function getStatus( asyncFn: () => Promise ): Promise { - let error = undefined; - try { - await asyncFn(); - } catch (err) { - error = err; - } - - return error ? Status.Error : Status.Success; + const result = await asyncFn(); + + if (!result) return Status.Error; + + return Status.Success; } describe('cli', () => { @@ -114,4 +114,44 @@ describe('cli', () => { assert.isTrue(files.every((file) => file.endsWith('.json'))); }); }); + + describe('createStatusReport', () => { + it('is able to create a successful status report', () => { + const date = new Date(); + const result = { + startedAt: date, + file: path.join(__dirname, 'resources', 'replay-fail.json'), + finishedAt: new Date(date.getTime() + 1000), + passed: true, + title: 'Test run', + }; + const [statusReport] = createStatusReport([result]); + const [title, status, file, duration] = + statusReport as HorizontalTableRow; + + assert.strictEqual(status, colors.white.bgGreen(' Passed ')); + assert.strictEqual(duration, '1000ms'); + assert.isString(file); + assert.strictEqual(title, result.title); + }); + + it('is able to create a failed status report', () => { + const date = new Date(); + const result = { + startedAt: date, + file: path.join(__dirname, 'resources', 'replay-fail.json'), + finishedAt: date, + passed: false, + title: 'Test run', + }; + const [statusReport] = createStatusReport([result]); + const [title, status, file, duration] = + statusReport as HorizontalTableRow; + + assert.strictEqual(status, colors.white.bgRed(' Failed ')); + assert.strictEqual(duration, '0ms'); + assert.isString(file); + assert.strictEqual(title, result.title); + }); + }); }); From 098b989582bf251f6aaae6f67398a5472f2e9409 Mon Sep 17 00:00:00 2001 From: Tamay Eser Uysal Date: Mon, 18 Jul 2022 19:58:20 +0300 Subject: [PATCH 2/4] chore: Use dot property accessor --- src/CLIUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CLIUtils.ts b/src/CLIUtils.ts index a2654926..cae94793 100644 --- a/src/CLIUtils.ts +++ b/src/CLIUtils.ts @@ -167,7 +167,7 @@ export async function runFiles( const content = readFileSync(file, 'utf-8'); const object = JSON.parse(content); const recording = parse(object); - result['title'] = recording.title; + result.title = recording.title; const { default: puppeteer } = await import('puppeteer'); browser = await puppeteer.launch({ @@ -180,9 +180,9 @@ export async function runFiles( opts.log && console.log(`Finished running ${file}`); } catch (err) { opts.log && console.error(`Error running ${file}`, err); - result['passed'] = false; + result.passed = false; } finally { - result['finishedAt'] = new Date(); + result.finishedAt = new Date(); results.push(result); await browser?.close(); From 7f2eecfd0f4ad2e4ce9ee21caa747ec52dd7fd7a Mon Sep 17 00:00:00 2001 From: Tamay Eser Uysal Date: Mon, 18 Jul 2022 20:05:46 +0300 Subject: [PATCH 3/4] chore: restore old test behaviour --- src/CLIUtils.ts | 6 ++++-- test/cli.test.ts | 13 ++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/CLIUtils.ts b/src/CLIUtils.ts index cae94793..172dd755 100644 --- a/src/CLIUtils.ts +++ b/src/CLIUtils.ts @@ -137,7 +137,7 @@ export async function runFiles( log: false, headless: true, } -): Promise { +): Promise { let Extension = PuppeteerRunnerOwningBrowserExtension; let browser: Browser | undefined; @@ -194,5 +194,7 @@ export async function runFiles( console.log(statusReport.toString()); } - return results.every((result) => result.passed); + if (results.every((result) => result.passed)) return; + + throw new Error('Some recordings have failed to run.'); } diff --git a/test/cli.test.ts b/test/cli.test.ts index d288b376..55683fa0 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -37,11 +37,14 @@ enum Status { async function getStatus( asyncFn: () => Promise ): Promise { - const result = await asyncFn(); - - if (!result) return Status.Error; - - return Status.Success; + let error = undefined; + try { + await asyncFn(); + } catch (err) { + error = err; + } + + return error ? Status.Error : Status.Success; } describe('cli', () => { From 3add140a0f7cb76809733cf3005792348f3ab8ba Mon Sep 17 00:00:00 2001 From: Tamay Eser Uysal Date: Mon, 18 Jul 2022 20:10:36 +0300 Subject: [PATCH 4/4] chore: reword `passed` to `success` --- src/CLIUtils.ts | 14 +++++++------- test/cli.test.ts | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/CLIUtils.ts b/src/CLIUtils.ts index 172dd755..92cf2c3e 100644 --- a/src/CLIUtils.ts +++ b/src/CLIUtils.ts @@ -81,7 +81,7 @@ type Result = { startedAt: Date; file: string; finishedAt: Date; - passed: boolean; + success: boolean; title: string; }; @@ -116,9 +116,9 @@ export function createStatusReport(results: Result[]): Table.Table { const duration = result.finishedAt?.getTime()! - result.startedAt.getTime() || 0; - const status = result.passed - ? resultTextColor.bgGreen(' Passed ') - : resultTextColor.bgRed(' Failed '); + const status = result.success + ? resultTextColor.bgGreen(' Success ') + : resultTextColor.bgRed(' Failure '); row.push(result.title); row.push(status); @@ -159,7 +159,7 @@ export async function runFiles( startedAt: new Date(), finishedAt: new Date(), file, - passed: true, + success: true, }; opts.log && console.log(`Running ${file}...`); @@ -180,7 +180,7 @@ export async function runFiles( opts.log && console.log(`Finished running ${file}`); } catch (err) { opts.log && console.error(`Error running ${file}`, err); - result.passed = false; + result.success = false; } finally { result.finishedAt = new Date(); results.push(result); @@ -194,7 +194,7 @@ export async function runFiles( console.log(statusReport.toString()); } - if (results.every((result) => result.passed)) return; + if (results.every((result) => result.success)) return; throw new Error('Some recordings have failed to run.'); } diff --git a/test/cli.test.ts b/test/cli.test.ts index 55683fa0..9cf6db3c 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -125,14 +125,14 @@ describe('cli', () => { startedAt: date, file: path.join(__dirname, 'resources', 'replay-fail.json'), finishedAt: new Date(date.getTime() + 1000), - passed: true, + success: true, title: 'Test run', }; const [statusReport] = createStatusReport([result]); const [title, status, file, duration] = statusReport as HorizontalTableRow; - assert.strictEqual(status, colors.white.bgGreen(' Passed ')); + assert.strictEqual(status, colors.white.bgGreen(' Success ')); assert.strictEqual(duration, '1000ms'); assert.isString(file); assert.strictEqual(title, result.title); @@ -144,14 +144,14 @@ describe('cli', () => { startedAt: date, file: path.join(__dirname, 'resources', 'replay-fail.json'), finishedAt: date, - passed: false, + success: false, title: 'Test run', }; const [statusReport] = createStatusReport([result]); const [title, status, file, duration] = statusReport as HorizontalTableRow; - assert.strictEqual(status, colors.white.bgRed(' Failed ')); + assert.strictEqual(status, colors.white.bgRed(' Failure ')); assert.strictEqual(duration, '0ms'); assert.isString(file); assert.strictEqual(title, result.title);