diff --git a/CHANGELOG.md b/CHANGELOG.md
index f263e7c8..5a4e8b26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,54 @@
+
+
+# 3.0.0 (2017-12-01)
+
+### Features
+* **cli:** Added a boat-load of new CLI configuration options. Every option available
+ in the `angular-playground.json` file is now also available as a CLI argument
+ (except @angular/cli arguments). [Read more about it in our docs](http://angularplayground.it/docs/api/configuration).
+ ([9dc1066](https://github.com/SoCreate/angular-playground/commit/9dc1066))
+* **new docs:** Speaking of docs, check out our newly-designed
+ [docs page](http://angularplayground.it/docs/getting-started/introduction).
+* **new error checking utility:** A new CLI option has been introduced that will run and visit
+ all sandbox scenarios in headless chrome, surfacing any errors that appear in the
+ console. [Never forget to mock a dependency again!](http://angularplayground.it/docs/how-to/run-the-test-suite)
+ ([6074586](https://github.com/SoCreate/angular-playground/commit/6074586))
+* **report formats for builds:** Used in conjunction with the checking utility, you can now
+ generate a JSON report that your build system can read for error reporting. Read all
+ about it [here](http://angularplayground.it/docs/api/reporter-formats).
+ ([7e0f5a8](https://github.com/SoCreate/angular-playground/commit/7e0f5a8))
+
+* **command bar shows all components as default:** Got the Playground running but don't know where
+ to begin? We'll help you out by showing all of your available scenarios.
+ ([51680fd](https://github.com/SoCreate/angular-playground/commit/51680fd)
+
+### Breaking Changes
+* **no default configuration argument**: The CLI no longer supports a default configuration file argument.
+ **Note:** `angular-playground` with no arguments will still default to using the
+ `angular-playground.json` file as expected.
+ ([9dc1066](https://github.com/SoCreate/angular-playground/commit/9dc1066))
+
+ Before:
+ ```
+ angular-playground my-configuration-file.json
+ ```
+ After:
+ ```
+ angular-playground --config my-configuration-file.json
+ ```
+
+* **new cli argument style**: CLI arguments now match typical npm style: `--argument` for full name, `-A` for abbreviation.
+
+ Before:
+ ```
+ -no-watch -no-serve
+ ```
+
+ After:
+ ```
+ --no-watch --no-serve
+ ```
+
# 2.3.0 (2017-11-13)
diff --git a/package-lock.json b/package-lock.json
index 2ff8ea78..5f81808a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "angular-playground",
- "version": "2.3.0",
+ "version": "3.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -186,10 +186,9 @@
"dev": true
},
"async": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz",
- "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==",
- "dev": true,
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
+ "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
"requires": {
"lodash": "4.17.4"
},
@@ -197,8 +196,7 @@
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
- "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
- "dev": true
+ "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
}
}
},
@@ -998,7 +996,7 @@
"integrity": "sha1-oUXyFveaDabJxrF7zkfhmQGM2Dg=",
"dev": true,
"requires": {
- "async": "2.5.0",
+ "async": "2.6.0",
"clone": "1.0.2",
"es6-templates": "0.2.3",
"extend": "3.0.1",
diff --git a/package.json b/package.json
index a795bfed..4d948821 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "angular-playground",
- "version": "2.3.0",
+ "version": "3.0.0",
"description": "A drop in app module for working on Angular components in isolation (aka Scenario Driven Development).",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
@@ -45,7 +45,10 @@
"zone.js": ">=0.8.14"
},
"dependencies": {
- "node-watch": "^0.4.1"
+ "async": "^2.6.0",
+ "node-watch": "^0.4.1",
+ "ts-node": "^3.3.0",
+ "puppeteer": "^0.13.0"
},
"devDependencies": {
"@angular/common": "^5.0.0",
@@ -63,9 +66,7 @@
"glob": "^7.1.2",
"gulp": "^3.9.1",
"gulp-inline-ng2-template": "^4.0.0",
- "puppeteer": "^0.13.0",
"rxjs": "^5.5.2",
- "ts-node": "^3.3.0",
"tslint": "5.3.2",
"typescript": "2.4.2",
"zone.js": "0.8.4"
diff --git a/src/cli/cli.ts b/src/cli/cli.ts
index cbb54877..514184e3 100644
--- a/src/cli/cli.ts
+++ b/src/cli/cli.ts
@@ -5,6 +5,7 @@ import { startWatch } from './start-watch';
import { runAngularCli } from './run-angular-cli';
import { Configuration } from './shared/configuration';
import { verifySandboxes } from './verify-sandboxes';
+import { findFirstFreePort } from './shared/find-port';
(async () => {
await run();
@@ -13,9 +14,9 @@ import { verifySandboxes } from './verify-sandboxes';
async function run() {
const rawArgs = process.argv.slice(2);
const config = new Configuration(rawArgs);
+ let sandboxPort, playgroundConfig;
- let configFile = path.resolve(config.configFilePath);
- let playgroundConfig;
+ let configFile = path.resolve(config.flags.config.value);
try {
playgroundConfig = require(configFile.replace(/.json$/, ''));
} catch (e) {
@@ -23,18 +24,26 @@ async function run() {
process.exit(1);
}
+ // Parity between command line arguments and configuration file
+ config.applyConfigurationFile(playgroundConfig);
const sandboxesPath = await build(playgroundConfig.sourceRoot);
- config.port = playgroundConfig.angularCli.port ? playgroundConfig.angularCli.port : 4201;
- if (config.runWatch) {
- startWatch(playgroundConfig, () => build(playgroundConfig.sourceRoot));
+ if (config.flags.checkErrors.value) {
+ // get port dynamically
+ const port = await findFirstFreePort('127.0.0.1', 7000, 9000);
+ sandboxPort = port;
+ config.flags.angularCli.port.value = port;
}
- if (config.runAngularCliServe && playgroundConfig.angularCli) {
- runAngularCli(playgroundConfig.angularCli);
+ if (!config.flags.noWatch.value) {
+ startWatch(config.flags.sourceRoot.value, () => build(config.flags.sourceRoot.value));
}
- if (config.runCheckErrors) {
- verifySandboxes(config, sandboxesPath);
+ if (!config.flags.noServe.value && playgroundConfig.angularCli) {
+ runAngularCli(config, playgroundConfig.angularCli);
+ }
+
+ if (config.flags.checkErrors.value) {
+ verifySandboxes(config, sandboxesPath, sandboxPort);
}
}
diff --git a/src/cli/reporters/json-reporter.ts b/src/cli/reporters/json-reporter.ts
new file mode 100644
index 00000000..25b98e6c
--- /dev/null
+++ b/src/cli/reporters/json-reporter.ts
@@ -0,0 +1,43 @@
+class JSONStats {
+ suites = 1;
+ passes: number;
+ pending = 0;
+ duration = 0;
+ time = 0;
+
+ constructor(
+ public tests: number,
+ public failures: number,
+ public start = 0,
+ public end = 0
+ ) {
+ this.passes = this.tests - this.failures;
+ }
+
+}
+
+export class JSONReporter {
+ constructor (
+ public errors: any[],
+ public scenarioNames: string[]
+ ) {}
+
+ getJson() {
+ return JSON.stringify({
+ stats: new JSONStats(this.scenarioNames.length, this.errors.length),
+ failures: this.errors.map(failure => {
+ if (!failure) return;
+ return {
+ title: failure.scenario,
+ err: {
+ message: failure.descriptions[0]
+ }
+ };
+ }),
+ passes: this.scenarioNames.map(pass => {
+ return { title: pass };
+ }),
+ skips: []
+ }, null, 2);
+ }
+}
diff --git a/src/cli/run-angular-cli.ts b/src/cli/run-angular-cli.ts
index 34113a47..1387d27d 100644
--- a/src/cli/run-angular-cli.ts
+++ b/src/cli/run-angular-cli.ts
@@ -1,24 +1,19 @@
+import { Configuration } from './shared/configuration';
+
const fs = require('fs');
const path = require('path');
const childProcess = require('child_process');
-export const runAngularCli = (angularCliConfig) => {
- let port = angularCliConfig.port ? angularCliConfig.port : 4201;
- let cliName = '@angular/cli';
- try {
- fs.accessSync(path.resolve('node_modules/@angular/cli/bin/ng'));
- } catch (e) {
- cliName = 'angular-cl';
- }
- let cliPath = `node_modules/${cliName}/bin/ng`;
- let args = [cliPath, 'serve', '-no-progress'];
+export const runAngularCli = (config: Configuration, angularCliConfig: any) => {
+ const cliConfig = config.flags.angularCli;
+ let args = [cliConfig.cmdPath.value, 'serve', '-no-progress'];
args.push('--port');
- args.push(port.toString());
- if (angularCliConfig.appName) {
- args.push(`-a=${angularCliConfig.appName}`);
+ args.push(cliConfig.port.value.toString());
+ if (cliConfig.appName.value) {
+ args.push(`-a=${cliConfig.appName.value}`);
}
- if (angularCliConfig.environment) {
- args.push(`-e=${angularCliConfig.environment}`);
+ if (cliConfig.environment.value) {
+ args.push(`-e=${cliConfig.environment.value}`);
}
if (angularCliConfig.args) {
args = args.concat(angularCliConfig.args);
diff --git a/src/cli/shared/configuration.ts b/src/cli/shared/configuration.ts
index d05d64fc..b8c36a1e 100644
--- a/src/cli/shared/configuration.ts
+++ b/src/cli/shared/configuration.ts
@@ -1,82 +1,106 @@
+import { REPORT_TYPE } from './error-reporter';
+
+class Flag {
+ constructor(
+ public aliases: string[],
+ public value: any,
+ public required = false
+ ) {}
+}
+
/**
* Configuration object used to parse and assign command line arguments
*/
export class Configuration {
- private supportedFlags = {
- noWatch: '--no-watch',
- noServe: '--no-serve',
- checkErrs: '--check-errors',
- randomScenario: '--random-scenario'
- };
-
- private supportedArguments = {
- config: '--config'
+ flags: any = {
+ noWatch: new Flag(['--no-watch'], false),
+ noServe: new Flag(['--no-serve'], false),
+ checkErrors: new Flag(['--check-errors'], false),
+ randomScenario: new Flag(['--random-scenario'], false),
+ sourceRoot: new Flag(['--src', '-S'], './src', true),
+ config: new Flag(['--config', '-C'], 'angular-playground.json'),
+ timeout: new Flag(['--timeout'], 90),
+ reportPath: new Flag(['--report-path', '-R'], './sandbox.report.json'),
+ reportType: new Flag(['--report-type'], REPORT_TYPE.LOG),
+ angularCli: {
+ appName: new Flag(['--ng-cli-app'], 'playground'),
+ environment: new Flag(['--ng-cli-env'], null),
+ port: new Flag(['--ng-cli-port'], 4201),
+ cmdPath: new Flag(['--ng-cli-cmd'], 'node_modules/@angular/cli/bin/ng')
+ }
};
- runWatch: boolean;
- runAngularCliServe: boolean;
- runCheckErrors: boolean;
- randomScenario: boolean;
- configFilePath: string;
- port: number;
- timeoutAttempts = 20;
+ // Used to tailor the version of headless chromium ran by puppeteer
chromeArguments = [ '--disable-gpu', '--no-sandbox' ];
constructor(rawArgv: string[]) {
- const { flags, args } = this.getParsedArguments(rawArgv);
- this.configureFlags(flags);
- this.configureArguments(args);
- }
+ // Apply command line arguments
+ rawArgv.forEach((argv, i) => {
+ const matchingFlag = this.findFlag(argv, this.flags);
+ if (!matchingFlag) return;
- get baseUrl(): string {
- return `http://localhost:${this.port}`;
+ if (typeof matchingFlag.value === 'boolean') {
+ matchingFlag.value = true;
+ } else {
+ matchingFlag.value = rawArgv[i + 1];
+ }
+ });
}
- // Boolean flags
- private configureFlags(flags: string[]) {
- this.runWatch = flags.indexOf(this.supportedFlags.noWatch) === -1;
- this.runAngularCliServe = flags.indexOf(this.supportedFlags.noServe) === -1;
- this.runCheckErrors = flags.indexOf(this.supportedFlags.checkErrs) !== -1;
- this.randomScenario = flags.indexOf(this.supportedFlags.randomScenario) !== -1;
- }
+ /**
+ * Override flags and switches with angular playground configuration JSON file
+ * @param playgroundConfig
+ */
+ applyConfigurationFile(playgroundConfig: any) {
+ const applyToFlags = (config: any, flagGroup: any) => {
+ Object.keys(config).forEach(key => {
+ if (!flagGroup.hasOwnProperty(key)) return;
+ const flag = flagGroup[key];
- // Arguments that may have values attached
- private configureArguments(args: string[]) {
- const configIndex = args.indexOf(this.supportedArguments.config);
- if (configIndex !== -1) {
- this.configFilePath = this.getArgValue(configIndex, args);
- } else if (args.length > 0) {
- this.configFilePath = args[0];
- } else {
- this.configFilePath = 'angular-playground.json';
- }
+ if (this.instanceOfFlagGroup(flag)) {
+ applyToFlags(config[key], flag);
+ }
+
+ flag.value = config[key];
+ });
+ };
+
+ applyToFlags(playgroundConfig, this.flags);
}
/**
- * Separates accepted command line arguments from other ts-node arguments
- * @param supportedFlags - Accepted command line flags
- * @param args - Process arguments
+ * Return a flag that contains the provided alias or undefined if none found
+ * @param alias - Alias provided by argv. e.g. --config
+ * @param flagGroup - Grouping of flags to check
*/
- private getParsedArguments(args: string[]): { flags: string[], args: string[] } {
- const flags: string[] = [];
+ private findFlag(alias: string, flagGroup: any): Flag {
+ return this.getValues(flagGroup).find(flag => {
+ if (this.instanceOfFlagGroup(flag)) {
+ return this.findFlag(alias, flag);
+ }
- args = args.reduce((accr, value) => {
- Object.keys(this.supportedFlags)
- .map(key => this.supportedFlags[key])
- .indexOf(value) > -1 ? flags.push(value) : accr.push(value);
- return accr;
- }, []);
+ return flag.aliases.includes(alias);
+ });
+ }
+
+ // Shim for Object.values()
+ private getValues(obj: any): any[] {
+ const vals = [];
+
+ for (const key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ vals.push(obj[key]);
+ }
+ }
- return { flags, args };
+ return vals;
}
/**
- * Gets the value of an argument from list of args (next consecutive argument)
- * e.g. --config ./src/
- * @param startingIndex - Index of argument
- * @param args - list of args
+ * Determines if provided item is a Flag or Flag-group
+ * @param item - Flag or Flag-group
*/
- private getArgValue(startingIndex: number, args: string[]): string {
- return args[startingIndex + 1];
+ private instanceOfFlagGroup(item: any) {
+ return !item.hasOwnProperty('value');
}
}
diff --git a/src/cli/shared/error-reporter.ts b/src/cli/shared/error-reporter.ts
index 3b350b8c..752bc4c9 100644
--- a/src/cli/shared/error-reporter.ts
+++ b/src/cli/shared/error-reporter.ts
@@ -1,25 +1,47 @@
-export enum ReportType {
- Log
-}
+import * as fs from 'fs';
+import { JSONReporter } from '../reporters/json-reporter';
+import { ScenarioSummary } from '../verify-sandboxes';
+
+export const REPORT_TYPE = {
+ LOG: 'log',
+ JSON: 'json'
+};
export class ErrorReporter {
- private _errors: { error: any, scenario: string }[] = [];
+ private _errors: { descriptions: any, scenario: string }[] = [];
- constructor(public type = ReportType.Log) {}
+ constructor(
+ public scenarios: ScenarioSummary[],
+ public filename: string,
+ public type: string
+ ) {}
get errors() {
return this._errors;
}
- addError(error: any, scenario: string) {
- this._errors.push({ error, scenario });
+ redWrap(msg: string): string {
+ return `\x1b[31m${msg}\x1b[0m`;
+ }
+
+ addError(descriptions: any, scenario: string) {
+ this._errors.push({ descriptions, scenario });
}
compileReport() {
switch (this.type) {
- case ReportType.Log:
- console.log('Found errors in the following scenarios:');
- this._errors.forEach(e => console.log(e.scenario));
+ case REPORT_TYPE.LOG:
+ console.error(`${this.redWrap('ERROR:')} in the following scenarios`);
+ this._errors.forEach(e => {
+ console.log(e.scenario);
+ console.log(e.descriptions);
+ });
+ break;
+ case REPORT_TYPE.JSON:
+ const scenarioNames = this.scenarios.map(s => `${s.name}: ${s.description}`);
+ const results = new JSONReporter(this.errors, scenarioNames);
+ fs.writeFileSync(this.filename, results.getJson());
+ break;
}
}
diff --git a/src/cli/shared/find-port.ts b/src/cli/shared/find-port.ts
new file mode 100644
index 00000000..36cb914e
--- /dev/null
+++ b/src/cli/shared/find-port.ts
@@ -0,0 +1,63 @@
+import * as net from 'net';
+// Legacy import
+const detect = require('async/detect');
+
+/**
+ * Function that detects the first port not in use in a given range
+ * e.g.
+ * findFirstFreePort('127.0.0.1', 8000, 8030, (port) => {
+ * console.log(port)
+ * });
+ *
+ * @param host - Host to check ports
+ * @param start - Starting point for range
+ * @param end - Ending point for range
+ * @param callback - Callback on result
+ */
+export async function findFirstFreePort(host: string, start: number, end: number) {
+ const ports = [];
+ for (let i = start; i < end; i++) {
+ ports.push(i);
+ }
+
+ const probe = (port: number, cb: Function) => {
+ let calledOnce = false;
+ let connected = false;
+
+ const server = net.createServer().listen(port, host);
+ const timeoutRef = setTimeout(() => {
+ calledOnce = true;
+ cb(port, false);
+ }, 2000);
+
+ // Active timeout won't require node event loop to remain active
+ timeoutRef.unref();
+
+ server.on('listening', () => {
+ clearTimeout(timeoutRef);
+ if (server) server.close();
+ if (!calledOnce) {
+ calledOnce = true;
+ cb(port, true);
+ }
+ });
+
+ server.on('error', (err: any) => {
+ clearTimeout(timeoutRef);
+ let result = true;
+ if (err.code === 'EADDRINUSE' || err.code === 'EACCES') {
+ result = false;
+ }
+ if (!calledOnce) {
+ calledOnce = true;
+ cb(port, result);
+ }
+ });
+ };
+
+ return new Promise(resolve => {
+ detect(ports, probe, (port: number) => {
+ resolve(port);
+ });
+ });
+}
diff --git a/src/cli/start-watch.ts b/src/cli/start-watch.ts
index 94b16a88..c4088f94 100644
--- a/src/cli/start-watch.ts
+++ b/src/cli/start-watch.ts
@@ -1,7 +1,7 @@
const path = require('path');
const watch = require('node-watch');
-export const startWatch = (config, cb) => {
+export const startWatch = (sourceRoot, cb) => {
let filter = (fn) => {
return (filename) => {
if (!/node_modules/.test(filename) && /\.sandbox.ts$/.test(filename)) {
@@ -9,5 +9,5 @@ export const startWatch = (config, cb) => {
}
};
};
- watch([path.resolve(config.sourceRoot)], filter(cb));
+ watch([path.resolve(sourceRoot)], filter(cb));
};
diff --git a/src/cli/verify-sandboxes.ts b/src/cli/verify-sandboxes.ts
index 399a7f5d..7aeb10c9 100644
--- a/src/cli/verify-sandboxes.ts
+++ b/src/cli/verify-sandboxes.ts
@@ -1,13 +1,15 @@
import * as puppeteer from 'puppeteer';
import * as process from 'process';
import * as path from 'path';
-import { ErrorReporter, ReportType } from './shared/error-reporter';
+import { ErrorReporter, REPORT_TYPE } from './shared/error-reporter';
import { Configuration } from './shared/configuration';
// ts-node required for runtime typescript compilation of sandboxes.ts
require('ts-node/register');
+// Legacy import
+const asyncMap = require('async/map');
-interface ScenarioSummary {
+export interface ScenarioSummary {
url: string;
name: string;
description: string;
@@ -15,31 +17,35 @@ interface ScenarioSummary {
let browser: any;
let currentScenario = '';
-const reporter = new ErrorReporter();
+let reporter: ErrorReporter;
+let hostUrl = '';
// Ensure Chromium instances are destroyed on err
process.on('unhandledRejection', () => {
if (browser) browser.close();
});
-export async function verifySandboxes(configuration: Configuration, sandboxesPath: string) {
- await main(configuration, sandboxesPath);
+export async function verifySandboxes(configuration: Configuration, sandboxesPath: string, port: number) {
+ hostUrl = `http://localhost:${port}`;
+ await main(configuration, sandboxesPath, port);
}
/////////////////////////////////
-async function main (configuration: Configuration, sandboxesPath: string) {
- let timeoutAttempts = configuration.timeoutAttempts;
+async function main (configuration: Configuration, sandboxesPath: string, port: number) {
+ const timeoutAttempts = configuration.flags.timeout.value;
browser = await puppeteer.launch({
headless: true,
handleSIGINT: false,
args: configuration.chromeArguments
});
- const scenarios = getSandboxMetadata(configuration.baseUrl, configuration.randomScenario, sandboxesPath);
+ const scenarios = getSandboxMetadata(hostUrl, configuration.flags.randomScenario.value, sandboxesPath);
+ reporter = new ErrorReporter(scenarios, configuration.flags.reportPath.value, configuration.flags.reportType.value);
console.log(`Retrieved ${scenarios.length} scenarios.\n`);
for (let i = 0; i < scenarios.length; i++) {
- await openScenarioInNewPage(scenarios[i], configuration.timeoutAttempts);
+ console.log(`Checking: ${scenarios[i].name}: ${scenarios[i].description}`);
+ await openScenarioInNewPage(scenarios[i], timeoutAttempts);
}
browser.close();
@@ -61,7 +67,7 @@ async function main (configuration: Configuration, sandboxesPath: string) {
async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts: number) {
if (timeoutAttempts === 0) {
await browser.close();
- process.exit(1);
+ throw new Error('Unable to connect to Playground.');
}
const page = await browser.newPage();
@@ -69,12 +75,10 @@ async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts:
currentScenario = scenario.name;
try {
- console.log(`Checking: ${currentScenario}: ${scenario.description}`);
await page.goto(scenario.url);
} catch (e) {
await page.close();
- await delay(5000);
- console.log(`Attempting to connect. (Attempts Remaining: ${timeoutAttempts})`);
+ await delay(1000);
await openScenarioInNewPage(scenario, timeoutAttempts - 1);
}
}
@@ -130,10 +134,15 @@ function loadSandboxMenuItems(path: string): any[] {
* Callback when Chromium page encounters a console error
* @param msg - Error message
*/
-function onConsoleErr(msg: any) {
+async function onConsoleErr(msg: any) {
if (msg.type === 'error') {
- console.error(`ERROR Found in ${currentScenario}`);
- reporter.addError(msg, currentScenario);
+ console.error(`${reporter.redWrap('ERROR:')} in ${currentScenario}`);
+ const descriptions = msg.args
+ .map(a => a._remoteObject)
+ .filter(o => o.type === 'object')
+ .map(o => o.description);
+ descriptions.map(d => console.error(d));
+ reporter.addError(descriptions, currentScenario);
}
}