UNPKG

flagpole

Version:

Simple and fast DOM integration and REST API testing framework.

345 lines (312 loc) 9.83 kB
import { Flagpole } from "./index"; import { Scenario } from "./scenario"; import { iLogLine, LogLineType, HeadingLine, DecorationLine, CommentLine, LineBreak, CustomLine, ConsoleColor, SubheadingLine, LogLine, HorizontalRule } from "./consoleline"; import { URL } from 'url'; import { FlagpoleOutput } from './flagpole'; /** * A suite contains many scenarios */ export class Suite { public scenarios: Array<Scenario> = []; protected title: string; protected baseUrl: URL | null = null; protected start: number; protected waitToExecute: boolean = false; protected callback: Function | null = null; protected _verifySslCert: boolean = true; constructor(title: string) { this.title = title; this.start = Date.now(); } public verifySslCert(verify: boolean): Suite { this._verifySslCert = verify; return this; } /** * * @param {Function} callback * @returns {Suite} */ public onDone(callback: Function): Suite { this.callback = callback; return this } /** * By default tell scenarios in this suite not to run until specifically told to by execute() * * @param {boolean} bool * @returns {Suite} */ public wait(bool: boolean = true): Suite { this.waitToExecute = bool; return this; } /** * Have all of the scenarios in this suite completed? * * @returns {boolean} */ public isDone(): boolean { let isDone: boolean = this.scenarios.every(function(scenario) { return scenario.isDone(); }); if (isDone && this.callback) { this.callback(this); } return isDone; } /** * How long has this been running? * * @returns {number} */ public getDuration(): number { return Date.now() - this.start; } /** * Print all logs to console * * @returns {Suite} */ public print(): Suite { if (Flagpole.logOutput) { this.getLines().forEach(function (line: iLogLine) { if (line.type != LogLineType.Decoration) { line.print(); } }); } else { if (Flagpole.getOutput() == FlagpoleOutput.html) { console.log(this.toHTML()); } else if (Flagpole.getOutput() == FlagpoleOutput.json) { console.log(JSON.stringify(this.toJson(), null, 2)); } else { this.getLines().forEach(function (line: iLogLine) { line.print(); }); } } return this; } public getLines(): iLogLine[] { let lines: iLogLine[] = []; lines.push(new HorizontalRule('=')); lines.push(new HeadingLine(this.title)); lines.push(new HorizontalRule('=')); lines.push(new CommentLine('Base URL: ' + this.baseUrl)); lines.push(new CommentLine('Environment: ' + Flagpole.getEnvironment())); lines.push(new CommentLine('Took ' + this.getDuration() + 'ms')); let color: ConsoleColor = this.passed() ? ConsoleColor.FgGreen : ConsoleColor.FgRed; lines.push(new CustomLine(' » Passed? ' + (this.passed() ? 'Yes' : 'No'), color)); lines.push(new LineBreak()); this.scenarios.forEach(function (scenario) { scenario.getLog().forEach(function (line: iLogLine) { lines.push(line); }); lines.push(new LineBreak()); }); return lines; } public toConsoleString(): string { let text: string = ''; this.getLines().forEach(function (line: iLogLine) { text += line.toConsoleString() + "\n"; }); return text; } public toString(): string { let text: string = ''; this.getLines().forEach(function (line: iLogLine) { text += line.toString() + "\n"; }); return text; } /** * Get JSON output * * @returns {any} */ public toJson(): any { let out: any = { title: this.title, baseUrl: this.baseUrl, duration: this.getDuration(), scenarios: [] }; this.scenarios.forEach(function(scenario, index) { out.scenarios[index] = { done: scenario.isDone(), failCount: 0, passCount: 0, log: [] }; scenario.getLog().forEach(function(line: iLogLine) { out.scenarios[index].log.push(line.toJson()); if (line.type == LogLineType.Pass) { out.scenarios[index].passCount++; } else if (line.type == LogLineType.Fail) { out.scenarios[index].failCount++; } }); }); return out; } public toHTML(): string { let html: string = ''; html += '<article class="suite">' + "\n"; html += new HeadingLine(this.getTitle()).toHTML() + "\n"; html += "<aside>\n"; html += "<ul>\n"; html += new CommentLine('Duartion: ' + this.getDuration() + 'ms').toHTML(); html += new CommentLine('Base URL: ' + this.baseUrl).toHTML(); html += new CommentLine('Environment: ' + Flagpole.getEnvironment()).toHTML(); html += "</ul>\n"; html += "</aside>\n"; this.scenarios.forEach(function (scenario: Scenario) { html += '<section class="scenario">' + "\n"; html += new SubheadingLine(scenario.getTitle()).toHTML() + "\n"; html += "<ul>\n"; scenario.getLog().forEach(function (line: iLogLine) { if ( line.type == LogLineType.Pass || line.type == LogLineType.Fail || line.type == LogLineType.Comment ) { html += line.toHTML(); } }); html += "</ul>\n"; html += "</section>\n"; }); html += "</article>\n"; return html; } public getTitle(): string { return this.title; } /** * Create a new scenario for this suite * * @param {string} title * @param {[string]} tags * @returns {Scenario} * @constructor */ public Scenario(title: string): Scenario { let suite: Suite = this; let scenario: Scenario = new Scenario(this, title, function() { if (suite.isDone()) { if (Flagpole.automaticallyPrintToConsole) { suite.print(); } process.exit( suite.passed() ? 0 : 1 ); } }); scenario.verifySslCert(this._verifySslCert); if (this.waitToExecute) { scenario.wait(); } this.scenarios.push(scenario); return scenario; } public Json(title: string): Scenario { return this.Scenario(title).json(); } public Image(title: string): Scenario { return this.Scenario(title).image(); } public Html(title: string): Scenario { return this.Scenario(title).html(); } public Stylesheet(title: string): Scenario { return this.Scenario(title).stylesheet(); } public Script(title: string): Scenario { return this.Scenario(title).script(); } public Resource(title: string): Scenario { return this.Scenario(title).resource(); } /** * Set the base url, which is typically the domain. All scenarios will run relative to it * * @param {string} url * @returns {Suite} */ public base(url: string | any[]): Suite { let baseUrl: string = ''; if (typeof url == 'string') { baseUrl = url; } else if (Object.keys(url).length > 0) { let env = Flagpole.getEnvironment() || 'dev'; baseUrl = url[env]; // If env didn't match one, just pick the first one if (!baseUrl) { baseUrl = url[Object.keys(url)[0]]; } } if (baseUrl.length > 0) { this.baseUrl = new URL(baseUrl); } else { throw Error('Invalid base url.'); } return this; } /** * Used by scenario to build its url * * @param {string} path * @returns {string} */ public buildUrl(path: string): string { if (this.baseUrl === null) { return path; } else if (path.startsWith('http://') || path.startsWith('https://') || path.startsWith('data:')) { return path; } else if (path.startsWith('/')) { return this.baseUrl.protocol + '//' + this.baseUrl.host + path; } return (new URL(path, this.baseUrl.href)).href; } /** * If suite was told to wait, this will tell each scenario in it to run * * @returns {Suite} */ public execute(): Suite { this.scenarios.forEach(function(scenario) { scenario.execute(); }); return this; } /** * Did every scenario in this suite pass? * * @returns {boolean} */ public passed(): boolean { return this.scenarios.every(function(scenario) { return scenario.passed(); }); } /** * Did any scenario in this suite fail? * * @returns {boolean} */ public failed(): boolean { return this.scenarios.some(function(scenario) { return scenario.failed(); }); } }