Skip to content

Commit

Permalink
feat(*): Improve progress display (pretty reporter)
Browse files Browse the repository at this point in the history
  • Loading branch information
wadackel committed Dec 16, 2020
1 parent 7778bf8 commit dc4e9e1
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 56 deletions.
14 changes: 14 additions & 0 deletions packages/core/src/acot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ export class Acot implements Core {
store,
onTestStart: this._handleTestStart,
onTestComplete: this._handleTestComplete,
onTestcaseStart: this._handleTestcaseStart,
onTestcaseComplete: this._handleTestcaseComplete,
}),
);
}
Expand Down Expand Up @@ -224,4 +226,16 @@ export class Acot implements Core {
) => {
await this._emitter.emit('test:complete', args);
};

private _handleTestcaseStart = async (
...args: CoreEventMap['testcase:start']
) => {
await this._emitter.emit('testcase:start', args);
};

private _handleTestcaseComplete = async (
...args: CoreEventMap['testcase:complete']
) => {
await this._emitter.emit('testcase:complete', args);
};
}
44 changes: 28 additions & 16 deletions packages/core/src/tester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export type TesterConfig = {
readyTimeout: number;
onTestStart: (...args: CoreEventMap['test:start']) => Promise<void>;
onTestComplete: (...args: CoreEventMap['test:complete']) => Promise<void>;
onTestcaseStart: (...args: CoreEventMap['testcase:start']) => Promise<void>;
onTestcaseComplete: (
...args: CoreEventMap['testcase:complete']
) => Promise<void>;
};

export class Tester {
Expand Down Expand Up @@ -74,9 +78,7 @@ export class Tester {
await series(
immutable.map((args) => async () => {
await this._execute(browser, page, tracker, args);

// Cleanup the page state.
await page.focus('body');
await this._cleanupPage(page);
}),
);
} catch (e) {
Expand Down Expand Up @@ -220,28 +222,41 @@ export class Tester {
return page;
}

private async _cleanupPage(page: Page): Promise<void> {
await page.focus('body');
}

private async _execute(
browser: Browser,
page: Page,
tracker: TimingTracker,
[id, rule, options]: TesterRuleGroup,
): Promise<void> {
const {
url,
workingDir,
onTestcaseStart,
onTestcaseComplete,
} = this._config;

await onTestcaseStart(url, id);

await tracker.track(id, async () => {
const results: TestcaseResult[] = [];

const context = createRuleContext({
process: browser.id(),
status: options[0] as Exclude<Status, 'off'>,
rule: id,
url: this._config.url,
url,
tags: rule.meta?.tags ?? [],
workingDir: this._config.workingDir,
workingDir,
results,
page,
options,
});

const addErrorResult = (e: unknown) => {
const handleUnexpectedError = (e: unknown) => {
debug('Unexpected error occurred:', e);

results.push(
Expand All @@ -259,7 +274,7 @@ export class Tester {

switch (rule.type) {
case 'global': {
await rule.test(context).catch((e) => addErrorResult(e));
await rule.test(context).catch(handleUnexpectedError);
break;
}

Expand All @@ -269,16 +284,11 @@ export class Tester {

await Promise.all(
nodes.map((node) =>
rule.test(context, node).catch((e) => addErrorResult(e)),
rule.test(context, node).catch(handleUnexpectedError),
),
);
} catch (e) {
debug(
'Not found elements (rule="%s", url="%s")',
id,
this._config.url,
e,
);
debug('Not found elements (rule="%s", url="%s")', id, url, e);
}
break;
}
Expand All @@ -305,16 +315,18 @@ export class Tester {
);
}

await onTestcaseComplete(url, id, results);

this._results.push(...results);
});
}

private async _waitForReady(page: Page): Promise<void> {
const { readyTimeout: timeout } = this._config;
const { url, readyTimeout: timeout } = this._config;

try {
await Promise.all([
page.goto(this._config.url, {
page.goto(url, {
waitUntil: 'networkidle2',
timeout,
}),
Expand Down
82 changes: 53 additions & 29 deletions packages/reporter/src/reporters/pretty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import boxen from 'boxen';
import table from 'text-table';
import ora from 'ora';
import indent from 'indent-string';
import type { TestResult } from '@acot/types';
import type { RuleId, TestcaseResult } from '@acot/types';
import logUpdate from 'log-update';
import logSymbols from 'log-symbols';
import plur from 'plur';
Expand All @@ -16,13 +16,42 @@ import { createReporterFactory } from '../factory';

const readFile = promisify(fs.readFile);

const count = (s: string, n: number) => `${n} ${plur(s, n)}`;
const setCount = (arr: string[], s: string, n: number) => {
if (n > 0) {
arr.push(`${n} ${plur(s, n)}`);
}
};

const summarize = (results: TestcaseResult[]) => {
const stat = results.reduce(
(acc, cur) => {
acc.pass += cur.status === 'pass' ? 1 : 0;
acc.pass += cur.status === 'error' ? 1 : 0;
acc.pass += cur.status === 'warn' ? 1 : 0;
return acc;
},
{
pass: 0,
error: 0,
warn: 0,
},
);

const msg: string[] = [];

setCount(msg, 'pass', stat.pass);
setCount(msg, 'error', stat.error);
setCount(msg, 'warning', stat.warn);

return msg.join(', ');
};

const stringLength = (s: string) => stripAnsi(s).length;

class AuditProgress {
protected _current = 0;
protected _urls: string[];
protected _current = 0;
protected _total = 0;
protected _stream: NodeJS.WritableStream;
protected _timer: NodeJS.Timeout | null = null;
protected _spinner: ora.Ora;
Expand All @@ -37,23 +66,20 @@ class AuditProgress {
}

public isComplete() {
return this._current >= this._urls.length;
return this._current >= this._total;
}

public start() {
this._loop();
}

public increment(result: TestResult) {
const summary = [
count('error', result.errorCount),
count('warning', result.warningCount),
count('pass', result.passCount),
].join(', ');

this._text = `${result.url} (${summary})`;
public add(total: number) {
this._total += total;
}

public increment(url: string, id: RuleId, results: TestcaseResult[]) {
this._current++;
this._text = `${url} - ${id} (${summarize(results)})`;

if (this.isComplete()) {
this.done();
Expand All @@ -74,8 +100,7 @@ class AuditProgress {
}

private _render() {
const total = this._urls.length;
const progress = this._current / total;
const progress = this._current / this._total;
const percent = `${Math.floor(progress * 100)}`.padStart(3);
const format = {
size: 30,
Expand All @@ -92,7 +117,7 @@ class AuditProgress {
const spinner = this.isComplete() ? '' : `${this._spinner.frame()}`;

let output = [
chalk.bold(`Running on ${total} URLs:`),
chalk.bold(`Running on ${this._urls.length} URLs:`),
chalk`{cyan ${percent}%} ${bar} ${spinner}{gray ${this._text}}`,
].join('\n');

Expand All @@ -111,22 +136,17 @@ class AuditProgress {
}

class AuditLinearProgress extends AuditProgress {
public increment(result: TestResult) {
public increment(url: string, id: RuleId, results: TestcaseResult[]) {
this._current++;

const total = this._urls.length;
const current = this._current.toString().padStart(`${total}`.length, '0');

const summary = [
count('error', result.errorCount),
count('warning', result.warningCount),
count('pass', result.passCount),
].join(', ');
const current = this._current
.toString()
.padStart(`${this._total}`.length, '0');

const output = [
`[${current}/${total}]`,
chalk.bold(result.url),
chalk`{gray (${summary})}`,
chalk.gray(`[${current}/${this._total}]`),
chalk.bold(url),
chalk.gray(`${id} (${summarize(results)})`),
].join(' ');

this._stream.write(`${output}\n`);
Expand Down Expand Up @@ -215,8 +235,12 @@ export default createReporterFactory(
progress!.start();
});

runner.on('test:complete', ([, , result]) => {
progress!.increment(result);
runner.on('test:start', ([, ids]) => {
progress!.add(ids.length);
});

runner.on('testcase:complete', ([url, id, results]) => {
progress!.increment(url, id, results);
});

runner.on('audit:complete', async ([summary]) => {
Expand Down
32 changes: 24 additions & 8 deletions packages/runner/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,23 @@ export class BaseRunner implements Runner {
this._core.on('audit:complete', this._handleAuditComplete);
this._core.on('test:start', this._handleTestStart);
this._core.on('test:complete', this._handleTestComplete);
this._core.on('testcase:start', this._handleTestcaseStart);
this._core.on('testcase:complete', this._handleTestcaseComplete);
this._core.on('close:start', this._handleCloseStart);
this._core.on('close:complete', this._handleCloseComplete);
}

private _unbindEvents() {
this._core.on('launch:start', this._handleLaunchStart);
this._core.on('launch:complete', this._handleLaunchComplete);
this._core.on('audit:start', this._handleAuditStart);
this._core.on('audit:complete', this._handleAuditComplete);
this._core.on('test:start', this._handleTestStart);
this._core.on('test:complete', this._handleTestComplete);
this._core.on('close:start', this._handleCloseStart);
this._core.on('close:complete', this._handleCloseComplete);
this._core.off('launch:start', this._handleLaunchStart);
this._core.off('launch:complete', this._handleLaunchComplete);
this._core.off('audit:start', this._handleAuditStart);
this._core.off('audit:complete', this._handleAuditComplete);
this._core.off('test:start', this._handleTestStart);
this._core.off('test:complete', this._handleTestComplete);
this._core.off('testcase:start', this._handleTestcaseStart);
this._core.off('testcase:complete', this._handleTestcaseComplete);
this._core.off('close:start', this._handleCloseStart);
this._core.off('close:complete', this._handleCloseComplete);
}

private async _close(): Promise<void> {
Expand Down Expand Up @@ -209,6 +213,18 @@ export class BaseRunner implements Runner {
await this._emitter.emit('test:complete', args);
};

private _handleTestcaseStart = async (
args: RunnerEventMap['testcase:start'],
) => {
await this._emitter.emit('testcase:start', args);
};

private _handleTestcaseComplete = async (
args: RunnerEventMap['testcase:complete'],
) => {
await this._emitter.emit('testcase:complete', args);
};

private _handleCloseStart = async (args: RunnerEventMap['close:start']) => {
await this._emitter.emit('close:start', args);
};
Expand Down
9 changes: 6 additions & 3 deletions packages/types/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import type { Summary } from './summary';
import type { NormalizedRuleConfig } from './config';
import type { Preset } from './preset';
import type { EventMethodFactory } from './event';
import type { TestResult } from './result';
import type { TestcaseResult, TestResult } from './result';
import type { RuleId } from './rule';

export type TestDescriptor = {
headers?: Record<string, string>;
Expand All @@ -15,8 +16,10 @@ export type CoreEventMap = {
'launch:complete': [urls: string[]];
'audit:start': [];
'audit:complete': [summary: Summary];
'test:start': [url: string, ids: string[]];
'test:complete': [url: string, ids: string[], result: TestResult];
'test:start': [url: string, ids: RuleId[]];
'test:complete': [url: string, ids: RuleId[], result: TestResult];
'testcase:start': [url: string, id: RuleId];
'testcase:complete': [url: string, id: RuleId, results: TestcaseResult[]];
'close:start': [];
'close:complete': [];
};
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export type RunnerEventMap = {
'audit:complete': CoreEventMap['audit:complete'];
'test:start': CoreEventMap['test:start'];
'test:complete': CoreEventMap['test:complete'];
'testcase:start': CoreEventMap['testcase:start'];
'testcase:complete': CoreEventMap['testcase:complete'];
'close:start': CoreEventMap['close:start'];
'close:complete': CoreEventMap['close:complete'];
'cleanup:start': [];
Expand Down

0 comments on commit dc4e9e1

Please sign in to comment.