UNPKG

@ply-ct/ply

Version:

REST API Automated Testing

157 lines (142 loc) 5.86 kB
import * as fs from 'fs'; import * as path from 'path'; import glob from 'glob'; import { Storage } from '../storage'; import { PlyResults, RunResult, SuiteRun, TestRun } from '../runs/model'; import { Request } from '../request'; import { Response } from '../response'; import { Test } from '../test'; import { Outcome } from '../result'; import { fwdSlashes } from '../util'; import { Values } from '../values'; export class Runs { constructor(readonly path: string) {} readRun(name: string, runNumber: number, test: string): TestRun | undefined { const run = this.readRuns(name, runNumber).find((run) => run.test === test); if (run?.request?.submitted && run.response) { // for vscode-ply request editor (run.response as any).submitted = run.request.submitted; } return run; } readRuns(name: string, runNumber: number): TestRun[] { const storage = new Storage(`${this.path}/${name}.${runNumber + 1}.json`); const content = storage.read(); if (content) { return JSON.parse(content); } return []; } writeRun( name: string, test: Test, outcome: Outcome & { request?: Request; response?: Response }, message?: string, values?: Values, runNumber = 0 ) { const testRun: TestRun = { name: (test as any).stepName || test.name, test: test.name, type: test.type, ...(outcome.start && { start: new Date(outcome.start).toISOString() as any }), // serialized as string ...(outcome.end && { end: new Date(outcome.end).toISOString() as any }), // serialized as string result: { status: outcome.status, ...(message && { message }) }, values }; if (outcome.request) { testRun.request = outcome.request; if (outcome.response) testRun.response = outcome.response; } else if (outcome.data && !(outcome.data as { [key: string]: any }).request) { testRun.data = outcome.data; } const storage = new Storage(`${this.path}/${name}.${runNumber + 1}.json`); const content = storage.read(); const testRuns: TestRun[] = content ? JSON.parse(content) : []; testRuns.push(testRun); storage.write(JSON.stringify(testRuns, null, 2)); } async findRunFiles(runsLocation: string, pattern: string): Promise<string[]> { return new Promise((resolve, reject) => { glob(pattern, { cwd: runsLocation }, (err, matches) => { if (err) { reject(err); } else { resolve(matches.map((m) => `${runsLocation}/${m}`)); } }); }); } async loadSuiteRuns( pattern = '**/*.json', filter?: (testRun: TestRun) => boolean ): Promise<SuiteRun[]> { const runFiles = await this.findRunFiles(this.path, pattern); const suiteRuns: SuiteRun[] = []; for (const runFile of runFiles) { const base = path.basename(runFile, '.json'); const lastDot = base.lastIndexOf('.'); const suiteName = base.substring(0, lastDot); const runNumber = parseInt(base.substring(lastDot + 1)); const contents = await fs.promises.readFile(runFile, { encoding: 'utf-8' }); let testRuns: TestRun[] = JSON.parse(contents); if (filter) testRuns = testRuns.filter(filter); testRuns.forEach((tr) => { if (tr.start) tr.start = new Date(tr.start); if (tr.end) tr.end = new Date(tr.end); }); const consolidateResults = (testRuns: TestRun[]): RunResult => { let anySubmitted = false; for (const testRun of testRuns) { if (testRun.result.status === 'Failed' || testRun.result.status === 'Errored') { return testRun.result; } else if (testRun.result.status === 'Submitted') { anySubmitted = true; } } return { status: anySubmitted ? 'Submitted' : 'Passed' }; }; let suitePath = `${fwdSlashes( path.relative(this.path, path.dirname(runFile)) )}/${suiteName}`; // undo file path double-dipping const segs = suitePath.split('/'); if (segs.length % 2 === 0) { suitePath = segs.slice(segs.length / 2).join('/'); } suiteRuns.push({ suite: suitePath, run: runNumber, result: consolidateResults(testRuns), ...(testRuns.length > 0 && testRuns[0].start && { start: testRuns[0].start }), ...(testRuns.length > 0 && testRuns[testRuns.length - 1].end && { end: testRuns[testRuns.length - 1].end }), testRuns }); } suiteRuns.sort((r1, r2) => { if (r1.suite === r2.suite) { return r1.run - r2.run; } else { return r1.suite.localeCompare(r2.suite); } }); return suiteRuns; } async loadPlyResults( pattern = '**/*.json', filter?: (testRun: TestRun) => boolean ): Promise<PlyResults> { const suiteRuns = await this.loadSuiteRuns(pattern, filter); const overall = { Passed: 0, Failed: 0, Errored: 0, Pending: 0, Submitted: 0, Waiting: 0 }; suiteRuns.forEach((sr) => { overall[sr.result.status]++; }); return { overall, runs: suiteRuns }; } }