From d517252ac67d44ecdee4e0fd3bf4f1a28b5b0200 Mon Sep 17 00:00:00 2001 From: Fodor Zoltan Date: Sat, 14 Sep 2024 21:36:15 +0300 Subject: [PATCH] Add more extensive types. --- .gitignore | 1 + src/CtrError.ts | 2 - src/collector/LogCollectBaseControl.ts | 12 +- src/collector/LogCollectBrowserConsole.ts | 28 ++-- src/collector/LogCollectCypressCommand.ts | 17 +-- src/collector/LogCollectCypressFetch.ts | 19 ++- src/collector/LogCollectCypressIntercept.ts | 13 +- src/collector/LogCollectCypressLog.ts | 14 +- src/collector/LogCollectCypressRequest.ts | 19 ++- src/collector/LogCollectCypressXhr.ts | 17 +-- src/collector/LogCollectExtendedControl.ts | 109 +++++++--------- src/collector/LogCollectSimpleControl.ts | 87 ++++++------- src/collector/LogCollectorState.ts | 93 +++++++------- src/collector/LogFormat.ts | 8 +- src/constants.ts | 6 +- src/installLogsCollector.d.ts | 112 ---------------- src/installLogsCollector.ts | 60 ++++----- src/installLogsCollector.types.ts | 88 +++++++++++++ src/installLogsPrinter.d.ts | 121 ------------------ src/installLogsPrinter.ts | 102 ++++++--------- src/installLogsPrinter.types.ts | 113 ++++++++++++++++ src/outputProcessor/BaseOutputProcessor.d.ts | 31 ----- src/outputProcessor/BaseOutputProcessor.ts | 22 +++- .../CustomOutputProcessor.d.ts | 25 ---- src/outputProcessor/CustomOutputProcessor.ts | 11 +- src/outputProcessor/JsonOutputProcessor.d.ts | 6 - src/outputProcessor/JsonOutputProcessor.ts | 4 +- .../NestedOutputProcessorDecorator.ts | 27 ++-- src/outputProcessor/TextOutputProcessor.d.ts | 6 - src/outputProcessor/TextOutputProcessor.ts | 13 +- src/outputProcessor/logsTxtFormatter.ts | 9 +- src/tv4ErrorTransformer.ts | 7 - src/types.ts | 39 ++++++ src/utils.ts | 7 + tsconfig.json | 2 +- 35 files changed, 570 insertions(+), 680 deletions(-) delete mode 100644 src/installLogsCollector.d.ts create mode 100644 src/installLogsCollector.types.ts delete mode 100644 src/installLogsPrinter.d.ts create mode 100644 src/installLogsPrinter.types.ts delete mode 100644 src/outputProcessor/BaseOutputProcessor.d.ts delete mode 100644 src/outputProcessor/CustomOutputProcessor.d.ts delete mode 100644 src/outputProcessor/JsonOutputProcessor.d.ts delete mode 100644 src/outputProcessor/TextOutputProcessor.d.ts delete mode 100644 src/tv4ErrorTransformer.ts create mode 100644 src/types.ts diff --git a/.gitignore b/.gitignore index 386c9ae..62a7e83 100755 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ test/cypress/reports .vscode .aider* src/**/*.js +src/**/*.d.ts diff --git a/src/CtrError.ts b/src/CtrError.ts index f89ab7b..310b89c 100644 --- a/src/CtrError.ts +++ b/src/CtrError.ts @@ -1,7 +1,5 @@ export default class CtrError extends Error { - constructor(message: any) { super(`cypress-terminal-report: ${message}`); } - } diff --git a/src/collector/LogCollectBaseControl.ts b/src/collector/LogCollectBaseControl.ts index 53f8276..754de7e 100644 --- a/src/collector/LogCollectBaseControl.ts +++ b/src/collector/LogCollectBaseControl.ts @@ -1,9 +1,13 @@ import CtrError from '../CtrError'; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; +import LogCollectorState from "./LogCollectorState"; +import {TestData} from "../types"; -export default class LogCollectBaseControl { - collectorState: any; - config: any; - prepareLogs(logStackIndex: any, testData: any) { +export default abstract class LogCollectBaseControl { + protected abstract collectorState: LogCollectorState; + protected abstract config: ExtendedSupportOptions; + + prepareLogs(logStackIndex: number, testData: TestData) { let logsCopy = this.collectorState.consumeLogStacks(logStackIndex); if (logsCopy === null) { diff --git a/src/collector/LogCollectBrowserConsole.ts b/src/collector/LogCollectBrowserConsole.ts index b800720..645d5fc 100644 --- a/src/collector/LogCollectBrowserConsole.ts +++ b/src/collector/LogCollectBrowserConsole.ts @@ -1,14 +1,11 @@ import CONSTANTS from '../constants'; import utils from '../utils'; +import LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; +import {Severity} from "../types"; export default class LogCollectBrowserConsole { - collectorState: any; - config: any; - - constructor(collectorState: any, config: any) { - this.config = config; - this.collectorState = collectorState; - } + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) {} register() { const oldConsoleMethods = {}; @@ -17,16 +14,15 @@ export default class LogCollectBrowserConsole { : 'window:before:load'; Cypress.on(event, () => { - const docIframe = window.parent.document.querySelector("[id*='Your project: ']") || - window.parent.document.querySelector("[id*='Your App']"); - // @ts-expect-error TS(2531): Object is possibly 'null'. - const appWindow = docIframe.contentWindow; + const docIframe = (window.parent.document.querySelector("[id*='Your project: ']") || + window.parent.document.querySelector("[id*='Your App']")) as HTMLIFrameElement; + const appWindow = docIframe.contentWindow as Window & typeof globalThis; // In case of component tests the even will be called multiple times. Prevent registering multiple times. - if (appWindow._ctr_registered) { + if (!appWindow || (appWindow as any)._ctr_registered) { return; } - appWindow._ctr_registered = true; + (appWindow as any)._ctr_registered = true; const processArg = (arg: any) => { if (['string', 'number', 'undefined', 'function'].includes(typeof arg)) { @@ -47,7 +43,11 @@ export default class LogCollectBrowserConsole { return utils.jsonStringify(arg); }; - const createWrapper = (method: any, logType: any, type = CONSTANTS.SEVERITY.SUCCESS) => { + const createWrapper = ( + method: 'warn' | 'error' | 'debug' | 'info' | 'log', + logType: any, + type: Severity = CONSTANTS.SEVERITY.SUCCESS + ) => { // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message oldConsoleMethods[method] = appWindow.console[method]; diff --git a/src/collector/LogCollectCypressCommand.ts b/src/collector/LogCollectCypressCommand.ts index d9d730c..1a99966 100644 --- a/src/collector/LogCollectCypressCommand.ts +++ b/src/collector/LogCollectCypressCommand.ts @@ -1,14 +1,11 @@ import CONSTANTS from '../constants'; import utils from '../utils'; +import LogFormat from "./LogFormat"; +import LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; export default class LogCollectCypressCommand { - collectorState: any; - config: any; - - constructor(collectorState: any, config: any) { - this.config = config; - this.collectorState = collectorState; - } + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) {} register() { const isOfInterest = (options: any) => options.instrument === 'command' && @@ -27,15 +24,15 @@ export default class LogCollectCypressCommand { return message; }; - Cypress.on('log:added', (options: any) => { + Cypress.on('log:added', (options) => { if (isOfInterest(options)) { const log = formatLogMessage(options); - const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.ERROR : ''; + const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.ERROR : CONSTANTS.SEVERITY.SUCCESS; this.collectorState.addLog([CONSTANTS.LOG_TYPES.CYPRESS_COMMAND, log, severity], options.id); } }); - Cypress.on('log:changed', (options: any) => { + Cypress.on('log:changed', (options) => { if (isOfInterest(options)) { const log = formatLogMessage(options); const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.ERROR : CONSTANTS.SEVERITY.SUCCESS; diff --git a/src/collector/LogCollectCypressFetch.ts b/src/collector/LogCollectCypressFetch.ts index e9907a4..f7c6a20 100644 --- a/src/collector/LogCollectCypressFetch.ts +++ b/src/collector/LogCollectCypressFetch.ts @@ -1,15 +1,12 @@ import CONSTANTS from '../constants'; import LogFormat from "./LogFormat"; +import LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; export default class LogCollectCypressFetch { - collectorState: any; - config: any; - format: any; - - constructor(collectorState: any, config: any) { - this.config = config; - this.collectorState = collectorState; + format: LogFormat; + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) { this.format = new LogFormat(config); } @@ -21,17 +18,17 @@ export default class LogCollectCypressFetch { (consoleProps(options)["Request went to origin?"] !== 'yes' ? 'STUBBED ' : '') + consoleProps(options).Method + ' ' + consoleProps(options).URL; - const formatDuration = (durationInMs: any) => durationInMs < 1000 ? `${durationInMs} ms` : `${durationInMs / 1000} s`; + const formatDuration = (durationInMs: number) => durationInMs < 1000 ? `${durationInMs} ms` : `${durationInMs / 1000} s`; - Cypress.on('log:added', (options: any) => { + Cypress.on('log:added', (options) => { if (options.instrument === 'command' && options.name === 'request' && options.displayName === 'fetch') { const log = formatFetch(options); - const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.WARNING : ''; + const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.WARNING : CONSTANTS.SEVERITY.SUCCESS; this.collectorState.addLog([CONSTANTS.LOG_TYPES.CYPRESS_FETCH, log, severity], options.id); } }); - Cypress.on('log:changed', async (options: any) => { + Cypress.on('log:changed', async (options) => { if ( options.instrument === 'command' && options.name === 'request' && options.displayName === 'fetch' && options.state !== 'pending' diff --git a/src/collector/LogCollectCypressIntercept.ts b/src/collector/LogCollectCypressIntercept.ts index 1ba0b95..24f4b61 100644 --- a/src/collector/LogCollectCypressIntercept.ts +++ b/src/collector/LogCollectCypressIntercept.ts @@ -1,24 +1,21 @@ import CONSTANTS from '../constants'; import LogFormat from "./LogFormat"; +import LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; Object.defineProperty(RegExp.prototype, "toJSON", { value: RegExp.prototype.toString }); export default class LogCollectCypressIntercept { - collectorState: any; - config: any; - format: any; - - constructor(collectorState: any, config: any) { - this.config = config; - this.collectorState = collectorState; + format: LogFormat; + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) { this.format = new LogFormat(config); } register() { - Cypress.Commands.overwrite('intercept', (originalFn: any, ...args: any[]) => { + Cypress.Commands.overwrite('intercept', (originalFn, ...args) => { let message = ''; if (typeof args[0] === "string" && CONSTANTS.HTTP_METHODS.includes(args[0].toUpperCase())) { diff --git a/src/collector/LogCollectCypressLog.ts b/src/collector/LogCollectCypressLog.ts index 485bf1c..182db49 100644 --- a/src/collector/LogCollectCypressLog.ts +++ b/src/collector/LogCollectCypressLog.ts @@ -1,17 +1,13 @@ import CONSTANTS from '../constants'; +import LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; export default class LogCollectCypressLog { - collectorState: any; - config: any; - - constructor(collectorState: any, config: any) { - this.config = config; - this.collectorState = collectorState; - } + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) {} register() { - Cypress.Commands.overwrite('log', (subject: any, ...args: any[]) => { - this.collectorState.addLog([CONSTANTS.LOG_TYPES.CYPRESS_LOG, args.join(' ')]); + Cypress.Commands.overwrite('log', (subject, ...args) => { + this.collectorState.addLog([CONSTANTS.LOG_TYPES.CYPRESS_LOG, args.join(' '), CONSTANTS.SEVERITY.SUCCESS]); subject(...args); }); } diff --git a/src/collector/LogCollectCypressRequest.ts b/src/collector/LogCollectCypressRequest.ts index 35a3bc9..56286c1 100644 --- a/src/collector/LogCollectCypressRequest.ts +++ b/src/collector/LogCollectCypressRequest.ts @@ -1,26 +1,23 @@ import CONSTANTS from '../constants'; import LogFormat from "./LogFormat"; +import LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; export default class LogCollectCypressRequest { - collectorState: any; - config: any; - format: any; - - constructor(collectorState: any, config: any) { - this.config = config; - this.collectorState = collectorState; + format: LogFormat; + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) { this.format = new LogFormat(config); } register() { - const isValidHttpMethod = (str: any) => typeof str === 'string' && CONSTANTS.HTTP_METHODS.some((s: any) => str.toUpperCase().includes(s)); + const isValidHttpMethod = (str: any) => typeof str === 'string' && CONSTANTS.HTTP_METHODS.some((s) => str.toUpperCase().includes(s)); const isNetworkError = (e: any) => e.message && e.message.startsWith('`cy.request()` failed trying to load:'); const isStatusCodeFailure = (e: any) => e.message && e.message.startsWith('`cy.request()` failed on:'); - const parseRequestStatusCodeFailureMessage = (message: any) => { + const parseRequestStatusCodeFailureMessage = (message: string) => { const responseStart = '\n\nThe response we got was:\n\n'; const statusStart = 'Status: '; const headersStart = '\nHeaders: '; @@ -46,7 +43,7 @@ export default class LogCollectCypressRequest { return {status: statusStr, headers: headersStr, body: bodyStr.trimEnd()}; }; - const parseRequestNetworkError = (message: any) => { + const parseRequestNetworkError = (message: string) => { const errorPartStart = 'We received this error at the network level:\n\n > '; const errorPrefix = 'Error: '; if (message.indexOf(errorPartStart) === -1) { @@ -138,7 +135,7 @@ export default class LogCollectCypressRequest { }, }); - this.collectorState.addLog([CONSTANTS.LOG_TYPES.CYPRESS_REQUEST, log]); + this.collectorState.addLog([CONSTANTS.LOG_TYPES.CYPRESS_REQUEST, log, CONSTANTS.SEVERITY.SUCCESS]); return response; }); }); diff --git a/src/collector/LogCollectCypressXhr.ts b/src/collector/LogCollectCypressXhr.ts index 3b531cb..c230318 100644 --- a/src/collector/LogCollectCypressXhr.ts +++ b/src/collector/LogCollectCypressXhr.ts @@ -1,15 +1,12 @@ import CONSTANTS from '../constants'; import LogFormat from "./LogFormat"; +import LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; export default class LogCollectCypressXhr { - collectorState: any; - config: any; - format: any; - - constructor(collectorState: any, config: any) { - this.config = config; - this.collectorState = collectorState; + format: LogFormat; + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) { this.format = new LogFormat(config); } @@ -22,19 +19,19 @@ export default class LogCollectCypressXhr { const formatDuration = (durationInMs: any) => durationInMs < 1000 ? `${durationInMs} ms` : `${durationInMs / 1000} s`; - Cypress.on('log:added', (options: any) => { + Cypress.on('log:added', (options) => { if ( options.instrument === 'command' && consoleProps(options) && options.displayName === 'xhr' ) { const log = formatXhr(options); - const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.WARNING : ''; + const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.WARNING : CONSTANTS.SEVERITY.SUCCESS; this.collectorState.addLog([CONSTANTS.LOG_TYPES.CYPRESS_XHR, log, severity], options.id); } }); - Cypress.on('log:changed', async (options: any) => { + Cypress.on('log:changed', async (options) => { if ( options.instrument === 'command' && ['request', 'xhr'].includes(options.name) && diff --git a/src/collector/LogCollectExtendedControl.ts b/src/collector/LogCollectExtendedControl.ts index c614895..9818ea3 100644 --- a/src/collector/LogCollectExtendedControl.ts +++ b/src/collector/LogCollectExtendedControl.ts @@ -1,26 +1,23 @@ import CONSTANTS from '../constants'; import LogCollectBaseControl from './LogCollectBaseControl'; import utils from "../utils"; +import LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; /** * Collects and dispatches all logs from all tests and hooks. */ export default class LogCollectExtendedControl extends LogCollectBaseControl { - collectorState: any; - config: any; getSpecFilePath: any; prepareLogs: any; - constructor(collectorState: any, config: any) { + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) { super(); - this.config = config; - this.collectorState = collectorState; - this.registerCypressBeforeMochaHooksSealEvent(); } register() { - this.collectorState.setStrict(true); + this.collectorState.setStrict(); this.registerState(); this.registerBeforeAllHooks(); @@ -29,10 +26,19 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { this.registerLogToFiles(); } - sendLogsToPrinter(logStackIndex: any, mochaRunnable: any, options = {}) { - // @ts-expect-error TS(2339): Property 'state' does not exist on type '{}'. + sendLogsToPrinter( + logStackIndex: number, + mochaRunnable: Mocha.Runnable, + options: { + state?: string, + title?: string, + noQueue?: boolean, + consoleTitle?: string, + isHook?: boolean, + wait?: number, + } = {} + ) { let testState = options.state || mochaRunnable.state; - // @ts-expect-error TS(2339): Property 'title' does not exist on type '{}'. let testTitle = options.title || mochaRunnable.title; let testLevel = 0; @@ -42,7 +48,6 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { return; } - // @ts-expect-error TS(2339): Property 'wait' does not exist on type '{}'. let wait = typeof options.wait === 'number' ? options.wait : 6; { @@ -60,46 +65,30 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { return logs; }; - // @ts-expect-error TS(2339): Property 'noQueue' does not exist on type '{}'. + const buildDataMessage = () => ({ + spec: spec, + test: testTitle, + messages: prepareLogs(), + state: testState, + level: testLevel, + consoleTitle: options.consoleTitle, + isHook: options.isHook, + continuous: false, + }); + if (options.noQueue) { this.debugLog('Sending with debounce.'); this.debounceNextMochaSuite(Promise.resolve() // Need to wait for command log update debounce. .then(() => new Promise(resolve => setTimeout(resolve, wait))) - .then(() => utils.nonQueueTask(CONSTANTS.TASK_NAME, { - spec: spec, - test: testTitle, - messages: prepareLogs(), - state: testState, - level: testLevel, - // @ts-expect-error TS(2339): Property 'consoleTitle' does not exist on type '{}... Remove this comment to see the full error message - consoleTitle: options.consoleTitle, - // @ts-expect-error TS(2339): Property 'isHook' does not exist on type '{}'. - isHook: options.isHook, - continuous: false, - })) + .then(() => utils.nonQueueTask(CONSTANTS.TASK_NAME, buildDataMessage())) .catch(console.error) ); } else { // Need to wait for command log update debounce. cy.wait(wait, {log: false}) .then(() => { - cy.task( - CONSTANTS.TASK_NAME, - { - spec: spec, - test: testTitle, - messages: prepareLogs(), - state: testState, - level: testLevel, - // @ts-expect-error TS(2339): Property 'consoleTitle' does not exist on type '{}... Remove this comment to see the full error message - consoleTitle: options.consoleTitle, - // @ts-expect-error TS(2339): Property 'isHook' does not exist on type '{}'. - isHook: options.isHook, - continuous: false, - }, - {log: false} - ); + cy.task(CONSTANTS.TASK_NAME, buildDataMessage(), {log: false}); }); } } @@ -107,26 +96,26 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { registerState() { const self = this; - Cypress.on('log:changed', (options: any) => { + Cypress.on('log:changed', (options) => { if (options.state === 'failed') { this.collectorState.updateLogStatusForChainId(options.id); } }); - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message - Cypress.mocha.getRunner().on('test', (test: any) => { + // @ts-ignore + Cypress.mocha.getRunner().on('test', (test) => { this.collectorState.startTest(test); }); - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('suite', () => { this.collectorState.startSuite(); }); - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('suite end', () => { this.collectorState.endSuite(); }); // Keeps track of before and after all hook indexes. - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('hook', function (hook: any) { if (!hook._ctr_hook && !hook.fn._ctr_hook) { // After each hooks get merged with the test. @@ -152,7 +141,7 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { const self = this; // Logs commands from before all hook if the hook passed. - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('hook end', function(this: any, hook: any) { if (hook.hookName === "before all" && self.collectorState.hasLogsCurrentStack() && !hook._ctr_hook) { self.debugLog('extended: sending logs of passed before all hook'); @@ -196,7 +185,7 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { const self = this; // Logs commands from after all hooks that passed. - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('hook end', function (hook: any) { if (hook.hookName === "after all" && self.collectorState.hasLogsCurrentStack() && !hook._ctr_hook) { self.debugLog('extended: sending logs of passed after all hook'); @@ -237,14 +226,14 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { // Have to wait for debounce on log updates to have correct state information. // Done state is used as callback and awaited in Cypress.fail. - // @ts-expect-error TS(2339): Property 'state' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.state('done', async (error: any) => { await new Promise(resolve => setTimeout(resolve, 6)); throw error; }); } - // @ts-expect-error TS(2339): Property 'state' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.state('error', error); throw error; }); @@ -287,7 +276,7 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { }; // Logs commands form each separate test when after each hooks are present. - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('hook end', function (hook: any) { if (hook.hookName === 'after each') { if (isLastAfterEachHookForTest(self.collectorState.getCurrentTest(), hook)) { @@ -297,7 +286,7 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { } }); // Logs commands form each separate test when there is no after each hook. - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('test end', function (test: any) { if (!testHasAfterEachHooks(test)) { self.debugLog('extended: sending logs for ended test, that has not after each hooks: ' + self.collectorState.getCurrentTest().title); @@ -305,7 +294,7 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { } }); // Logs commands if test was manually skipped. - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('pending', function (test: any) { if (self.collectorState.getCurrentTest() === test) { // In case of fully skipped tests we might not yet have a log stack. @@ -324,8 +313,8 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { }); } - debounceNextMochaSuite(promise: any) { - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + debounceNextMochaSuite(promise: Promise) { + // @ts-ignore const runner = Cypress.mocha.getRunner(); // Hack to make mocha wait for our logs to be written to console before @@ -349,16 +338,14 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { // Hack to have dynamic after hook per suite. // The onSpecReady in cypress is called before the hooks are 'condensed', or so // to say sealed and thus in this phase we can register dynamically hooks. - // @ts-expect-error TS(2339): Property 'onSpecReady' does not exist on type 'Cyp... Remove this comment to see the full error message - const oldOnSpecReady = Cypress.onSpecReady; - // @ts-expect-error TS(2339): Property 'onSpecReady' does not exist on type 'Cyp... Remove this comment to see the full error message - Cypress.onSpecReady = function () { + const oldOnSpecReady = (Cypress as any).onSpecReady; + (Cypress as any).onSpecReady = function () { Cypress.emit('before:mocha:hooks:seal'); oldOnSpecReady(...arguments); }; } - prependBeforeAllHookInAllSuites(rootSuites: any, hookCallback: any) { + prependBeforeAllHookInAllSuites(rootSuites: any, hookCallback: (this: any) => void) { const recursiveSuites = (suites: any) => { if (suites) { suites.forEach((suite: any) => { @@ -402,7 +389,7 @@ export default class LogCollectExtendedControl extends LogCollectBaseControl { preventNextMochaPassEmit() { const oldAction = Cypress.action; - Cypress.action = function (actionName: any, ...args: any[]) { + Cypress.action = function (actionName: string, ...args: any[]) { if (actionName === 'runner:pass') { Cypress.action = oldAction; return; diff --git a/src/collector/LogCollectSimpleControl.ts b/src/collector/LogCollectSimpleControl.ts index a70c52a..05809e0 100644 --- a/src/collector/LogCollectSimpleControl.ts +++ b/src/collector/LogCollectSimpleControl.ts @@ -1,17 +1,15 @@ import CONSTANTS from '../constants'; import LogCollectBaseControl from './LogCollectBaseControl'; import utils from "../utils"; +import type LogCollectorState from "./LogCollectorState"; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; +import * as stream from "node:stream"; /** * Collects and dispatches all logs from all tests and hooks. */ export default class LogCollectSimpleControl extends LogCollectBaseControl { - collectorState: any; - config: any; - getSpecFilePath: any; - prepareLogs: any; - - constructor(collectorState: any, config: any) { + constructor(protected collectorState: LogCollectorState, protected config: ExtendedSupportOptions) { super(); this.config = config; this.collectorState = collectorState; @@ -23,10 +21,18 @@ export default class LogCollectSimpleControl extends LogCollectBaseControl { this.registerLogToFiles(); } - sendLogsToPrinter(logStackIndex: any, mochaRunnable: any, options = {}) { - // @ts-expect-error TS(2339): Property 'state' does not exist on type '{}'. + sendLogsToPrinter( + logStackIndex: number, + mochaRunnable: Mocha.Runnable, + options: { + state?: string, + title?: string, + noQueue?: boolean, + consoleTitle?: string, + isHook?: boolean, + } = {} + ) { let testState = options.state || mochaRunnable.state; - // @ts-expect-error TS(2339): Property 'title' does not exist on type '{}'. let testTitle = options.title || mochaRunnable.title; let testLevel = 0; @@ -48,67 +54,52 @@ export default class LogCollectSimpleControl extends LogCollectBaseControl { } } - if (testState === 'failed' && mochaRunnable && mochaRunnable._retries > 0) { - testTitle += ` (Attempt ${mochaRunnable && mochaRunnable._currentRetry + 1})` + if (testState === 'failed' && mochaRunnable && (mochaRunnable as any)._retries > 0) { + testTitle += ` (Attempt ${mochaRunnable && (mochaRunnable as any)._currentRetry + 1})` } const prepareLogs = () => { return this.prepareLogs(logStackIndex, {mochaRunnable, testState, testTitle, testLevel}); }; - // @ts-expect-error TS(2339): Property 'noQueue' does not exist on type '{}'. + const buildDataMessage = () => ({ + spec: spec, + test: testTitle, + messages: prepareLogs(), + state: testState, + level: testLevel, + consoleTitle: options.consoleTitle, + isHook: options.isHook, + continuous: this.config.enableContinuousLogging, + }); + if (options.noQueue) { - utils.nonQueueTask(CONSTANTS.TASK_NAME, { - spec: spec, - test: testTitle, - messages: prepareLogs(), - state: testState, - level: testLevel, - // @ts-expect-error TS(2339): Property 'consoleTitle' does not exist on type '{}... Remove this comment to see the full error message - consoleTitle: options.consoleTitle, - // @ts-expect-error TS(2339): Property 'isHook' does not exist on type '{}'. - isHook: options.isHook, - continuous: this.config.enableContinuousLogging, - }).catch(console.error); + utils.nonQueueTask(CONSTANTS.TASK_NAME, buildDataMessage()).catch(console.error); } else { // Need to wait for command log update debounce. cy.wait(wait, {log: false}) - .then(() => { - cy.task( - CONSTANTS.TASK_NAME, - { - spec: spec, - test: testTitle, - messages: prepareLogs(), - state: testState, - level: testLevel, - // @ts-expect-error TS(2339): Property 'consoleTitle' does not exist on type '{}... Remove this comment to see the full error message - consoleTitle: options.consoleTitle, - // @ts-expect-error TS(2339): Property 'isHook' does not exist on type '{}'. - isHook: options.isHook, - continuous: this.config.enableContinuousLogging, - }, - {log: false} - ); - }); + .then(() => cy.task(CONSTANTS.TASK_NAME, buildDataMessage(), {log: false})); } } registerState() { - Cypress.on('log:changed', (options: any) => { + Cypress.on('log:changed', (options) => { if (options.state === 'failed') { this.collectorState.updateLogStatusForChainId(options.id); } }); - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message - Cypress.mocha.getRunner().on('test', (test: any) => { + + // @ts-ignore + Cypress.mocha.getRunner().on('test', (test: Mocha.Runnable) => { this.collectorState.startTest(test); }); - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + + // @ts-ignore Cypress.mocha.getRunner().on('suite', () => { this.collectorState.startSuite(); }); - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + + // @ts-ignore Cypress.mocha.getRunner().on('suite end', () => { this.collectorState.endSuite(); }); @@ -130,7 +121,7 @@ export default class LogCollectSimpleControl extends LogCollectBaseControl { }); // Logs commands if test was manually skipped. - // @ts-expect-error TS(2339): Property 'mocha' does not exist on type 'Cypress &... Remove this comment to see the full error message + // @ts-ignore Cypress.mocha.getRunner().on('pending', function () { let test = self.collectorState.getCurrentTest(); if (test && test.state === 'pending') { diff --git a/src/collector/LogCollectorState.ts b/src/collector/LogCollectorState.ts index fe7936a..dc99d94 100644 --- a/src/collector/LogCollectorState.ts +++ b/src/collector/LogCollectorState.ts @@ -1,18 +1,27 @@ import CONSTANTS from '../constants'; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; +import {Log, Severity} from "../types"; + +type LogArray = [Log['type'], Log['message'], Log['severity']] + +interface StackLog extends Log { + timeString?: string; + chainId?: string; +} + +export type StackLogArray = StackLog[] & { _ctr_before_each?: number } export default class LogCollectorState { - afterHookIndexes: any; - beforeHookIndexes: any; - config: any; + afterHookIndexes: number[]; + beforeHookIndexes: number[]; currentTest: any; - isStrict: any; - listeners: any; - logStacks: any; - suiteStartTime: any; - xhrIdsOfLoggedResponses: any; - constructor(config: any) { - this.config = config; + isStrict: boolean; + listeners: Record; + logStacks: Array; + suiteStartTime: Date | null; + xhrIdsOfLoggedResponses: string[]; + constructor(protected config: ExtendedSupportOptions) { this.listeners = {}; this.currentTest = null; this.logStacks = []; @@ -23,7 +32,7 @@ export default class LogCollectorState { this.suiteStartTime = null; } - setStrict(strict: any) { + setStrict() { this.isStrict = true; } @@ -46,7 +55,7 @@ export default class LogCollectorState { const stack = this.logStacks[index]; this.logStacks[index] = null; - stack.forEach((log: any) => { + stack?.forEach((log) => { if (log.chainId) { delete log.chainId; } @@ -55,7 +64,7 @@ export default class LogCollectorState { return stack; } hasLogsCurrentStack() { - return this.getCurrentLogStack() && !!(this.getCurrentLogStack().length); + return this.getCurrentLogStack() && !!(this.getCurrentLogStack()?.length); } getCurrentTest() { return this.currentTest; @@ -64,7 +73,7 @@ export default class LogCollectorState { this.currentTest = test; } - addLog(entry: any, chainId: any, xhrIdOfLoggedResponse: any) { + addLog(entry: LogArray, chainId?: any, xhrIdOfLoggedResponse?: any) { entry[2] = entry[2] || CONSTANTS.SEVERITY.SUCCESS; const currentStack = this.getCurrentLogStack(); @@ -75,22 +84,19 @@ export default class LogCollectorState { return; } - const structuredEntry = { + const structuredEntry: StackLog = { type: entry[0], message: entry[1], severity: entry[2], }; if (chainId) { - // @ts-expect-error TS(2339): Property 'chainId' does not exist on type '{ type:... Remove this comment to see the full error message structuredEntry.chainId = chainId; } if (this.config.commandTimings) { if (this.config.commandTimings == 'timestamp') { - // @ts-expect-error TS(2339): Property 'timeString' does not exist on type '{ ty... Remove this comment to see the full error message structuredEntry.timeString = Date.now() + ""; - } else if (this.config.commandTimings == 'seconds') { - // @ts-expect-error TS(2339): Property 'timeString' does not exist on type '{ ty... Remove this comment to see the full error message + } else if (this.config.commandTimings == 'seconds' && this.suiteStartTime) { structuredEntry.timeString = (Date.now() - this.suiteStartTime.getTime()) / 1000 + "s"; } } @@ -102,7 +108,7 @@ export default class LogCollectorState { this.emit('log'); } - updateLog(log: any, severity: any, id: any) { + updateLog(log: string, severity: Severity, id: string) { this.loopLogStacks((entry: any) => { if (entry.chainId === id) { entry.message = log; @@ -112,7 +118,7 @@ export default class LogCollectorState { this.emit('log'); } - updateLogStatusForChainId(chainId: any, state = CONSTANTS.SEVERITY.ERROR) { + updateLogStatusForChainId(chainId: string, state: Severity = CONSTANTS.SEVERITY.ERROR) { this.loopLogStacks((entry: any) => { if (entry.chainId === chainId) { entry.severity = state; @@ -120,31 +126,26 @@ export default class LogCollectorState { }); } - loopLogStacks(callback: any) { + loopLogStacks(callback: (entry: StackLog) => void) { this.logStacks.forEach((logStack: any) => { - if (!logStack) { - return; + if (logStack) { + logStack.forEach((entry: any) => { + if (entry) { + callback(entry); + } + }); } - - logStack.forEach((entry: any) => { - if (!entry) { - return; - } - - callback(entry); - }); }); } - hasXhrResponseBeenLogged(xhrId: any) { - return this.xhrIdsOfLoggedResponses.includes(xhrId); - } - markCurrentStackFromBeforeEach() { if (this.config.debug) { console.log(CONSTANTS.DEBUG_LOG_PREFIX + 'current log stack is before each at ' + this.getCurrentLogStackIndex()); } - this.getCurrentLogStack()._ctr_before_each = 1; + let stack = this.getCurrentLogStack(); + if (stack) { + stack._ctr_before_each = 1; + } } incrementBeforeHookIndex() { @@ -188,25 +189,23 @@ export default class LogCollectorState { this.addNewLogStack(); - // Merge together before each logs. + // Merge together before each log. const currentIndex = this.getCurrentLogStackIndex(); let previousIndex = currentIndex - 1; - while ( - // @ts-expect-error TS(2554): Expected 0 arguments, but got 1. - this.getCurrentLogStack(previousIndex) - // @ts-expect-error TS(2554): Expected 0 arguments, but got 1. - && this.getCurrentLogStack(previousIndex)._ctr_before_each - ) { - this.logStacks[currentIndex] = this.logStacks[previousIndex].concat(this.logStacks[currentIndex]); + while (this.getCurrentLogStack()?._ctr_before_each) { + const previousStack = this.logStacks[previousIndex]; + if (previousStack) { + this.logStacks[currentIndex] = previousStack.concat(this.logStacks[currentIndex] || []); + } --previousIndex; } } - emit(event: any) { + emit(event: 'log') { (this.listeners[event] || []).forEach((callback: any) => callback()); } - on(event: any, callback: any) { + on(event: 'log', callback: () => void) { this.listeners[event] = this.listeners[event] || []; this.listeners[event].push(callback); } diff --git a/src/collector/LogFormat.ts b/src/collector/LogFormat.ts index 08e4e9e..5c67e41 100644 --- a/src/collector/LogFormat.ts +++ b/src/collector/LogFormat.ts @@ -1,9 +1,7 @@ -export default class LogFormat { - config: any; +import {ExtendedSupportOptions} from "../installLogsCollector.types"; - constructor(config: any) { - this.config = config; - } +export default class LogFormat { + constructor(protected config: ExtendedSupportOptions) {} formatXhrLog(xhrLog: any) { let logMessage = ''; diff --git a/src/constants.ts b/src/constants.ts index 020ab3d..b6c0d30 100755 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,5 @@ +import {LogType, Severity} from "./types"; + const CONSTANTS = { TASK_NAME: 'ctrLogMessages', TASK_NAME_OUTPUT: 'ctrLogFiles', @@ -17,13 +19,13 @@ const CONSTANTS = { CYPRESS_COMMAND: 'cy:command', PLUGIN_LOG_TYPE: 'ctr:info', - }, + } satisfies Record, SEVERITY: { SUCCESS: 'success', ERROR: 'error', WARNING: 'warning', - }, + } satisfies Record, HOOK_TITLES: { BEFORE: '[[ before all {index} ]]', diff --git a/src/installLogsCollector.d.ts b/src/installLogsCollector.d.ts deleted file mode 100644 index 64ce06e..0000000 --- a/src/installLogsCollector.d.ts +++ /dev/null @@ -1,112 +0,0 @@ -declare function installLogsCollector(config?: installLogsCollector.SupportOptions): void; -declare namespace installLogsCollector { - type Severity = '' | 'error' | 'warning'; - - type LogType = 'cons:log' | - 'cons:info' | - 'cons:warn' | - 'cons:error' | - 'cons:debug' | - 'cy:log' | - 'cy:xhr' | - 'cy:fetch' | - 'cy:request' | - 'cy:intercept' | - 'cy:command' | - 'ctr:info'; - - type Log = { - type: LogType, - message: string, - severity: Severity, - timeString?: string, - }; - - interface SupportOptions { - /** - * What types of logs to collect and print. - * By default all types are enabled. - * The 'cy:command' is the general type that contain all types of commands that are not specially treated. - * @default ['cons:log', 'cons:info', 'cons:warn', 'cons:error', 'cy:log', 'cy:xhr', 'cy:fetch', 'cy:request', 'cy:intercept', 'cy:command'] - */ - collectTypes?: readonly LogType[]; - - /** - * Callback to filter logs manually. The type is from the same list as for the collectTypes option. - * Severity can be of ['', 'error', 'warning']. - * @default undefined - */ - filterLog?: - | null - | NonNullable[number] - | ((args: Log) => boolean); - - /** - * Callback to process logs manually. The type is from the same list as for the collectTypes option. - * Severity can be of ['', 'error', 'warning']. - * @default undefined - */ - processLog?: - | null - | NonNullable[number] - | ((args: Log) => Log); - - /** - * Callback to collect each test case's logs after its run. - * @default undefined - */ - collectTestLogs?: ( - context: {mochaRunnable: any, testState: string, testTitle: string, testLevel: number}, - messages: Log[] - ) => void; - - xhr?: { - /** - * Whether to print body for XHR requests. Controls request body printing as well when request data logging is - * enabled. - * @default true - */ - printBody?: boolean; - - /** - * Whether to print header data for XHR requests. - * @default false - */ - printHeaderData?: boolean; - - /** - * Whether to print request data for XHR requests besides response data. - * @default false - */ - printRequestData?: boolean; - }; - - /** - * Enables extended log collection: including after all and before all hooks. - * @unstable - * @default false - */ - enableExtendedCollector?: boolean; - - /** - * Enables continuous logging of logs to terminal one by one, as they get registerd or modified. - * @unstable - * @default false - */ - enableContinuousLogging?: boolean; - - /** - * Adds time information to logs. - * @default null - */ - commandTimings?: null | 'timestamp' | 'seconds' - - /** - * Enabled debug logging. - * @default false - */ - debug?: boolean; - } -} - -export = installLogsCollector; diff --git a/src/installLogsCollector.ts b/src/installLogsCollector.ts index 9ffb6ca..8e6cc45 100755 --- a/src/installLogsCollector.ts +++ b/src/installLogsCollector.ts @@ -2,7 +2,6 @@ import tv4 from 'tv4'; import semver from 'semver'; import schema from './installLogsCollector.schema.json'; import CtrError from './CtrError'; -import tv4ErrorTransformer from './tv4ErrorTransformer'; import LogCollectBrowserConsole from "./collector/LogCollectBrowserConsole"; import LogCollectCypressCommand from "./collector/LogCollectCypressCommand"; import LogCollectCypressRequest from "./collector/LogCollectCypressRequest"; @@ -15,41 +14,45 @@ import LogCollectExtendedControl from "./collector/LogCollectExtendedControl"; import LogCollectSimpleControl from "./collector/LogCollectSimpleControl"; import logsTxtFormatter from "./outputProcessor/logsTxtFormatter"; import CONSTANTS from "./constants"; +import {ExtendedSupportOptions, SupportOptions} from "./installLogsCollector.types"; +import {LogType} from "./types"; +import utils from "./utils"; + +declare global { + namespace Cypress { + interface TerminalReport { + getLogs(format: string): any; + } + } +} /** - * Installs the logs collector for cypress. - * - * Needs to be added to support file. - * - * @see ./installLogsCollector.d.ts - * @type {import('./installLogsCollector')} + * Installs the logs collector for cypress. Needs to be added to support file. */ -function installLogsCollector(config = {}) { +function installLogsCollector(config: SupportOptions = {}) { validateConfig(config); - // @ts-expect-error TS(2339): Property 'collectTypes' does not exist on type '{}... Remove this comment to see the full error message - config.collectTypes = config.collectTypes || Object.values(CONSTANTS.LOG_TYPES); - // @ts-expect-error TS(2339): Property 'collectBody' does not exist on type '{}'... Remove this comment to see the full error message - config.collectBody = config.xhr && config.xhr.printBody !== undefined ? !!config.xhr.printBody : true; - // @ts-expect-error TS(2339): Property 'collectRequestData' does not exist on ty... Remove this comment to see the full error message - config.collectRequestData = config.xhr && config.xhr.printRequestData; - // @ts-expect-error TS(2339): Property 'collectHeaderData' does not exist on typ... Remove this comment to see the full error message - config.collectHeaderData = config.xhr && config.xhr.printHeaderData; + const extendedConfig: ExtendedSupportOptions = { + ...config, + collectTypes: config.collectTypes || Object.values(CONSTANTS.LOG_TYPES) as LogType[], + collectBody: config.xhr && config.xhr.printBody !== undefined ? config.xhr.printBody : true, + collectRequestData: config.xhr && config.xhr.printRequestData, + collectHeaderData: config.xhr && config.xhr.printHeaderData, + }; - let logCollectorState = new LogCollectorState(config); - registerLogCollectorTypes(logCollectorState, config); + let logCollectorState = new LogCollectorState(extendedConfig); + registerLogCollectorTypes(logCollectorState, extendedConfig); - // @ts-expect-error TS(2339): Property 'enableExtendedCollector' does not exist ... Remove this comment to see the full error message - if (config.enableExtendedCollector) { - (new LogCollectExtendedControl(logCollectorState, config)).register(); + if (extendedConfig.enableExtendedCollector) { + (new LogCollectExtendedControl(logCollectorState, extendedConfig)).register(); } else { - (new LogCollectSimpleControl(logCollectorState, config)).register(); + (new LogCollectSimpleControl(logCollectorState, extendedConfig)).register(); } registerGlobalApi(logCollectorState); } -function registerLogCollectorTypes(logCollectorState: any, config: any) { +function registerLogCollectorTypes(logCollectorState: any, config: ExtendedSupportOptions) { (new LogCollectBrowserConsole(logCollectorState, config)).register() if (config.collectTypes.includes(CONSTANTS.LOG_TYPES.CYPRESS_LOG)) { @@ -73,9 +76,8 @@ function registerLogCollectorTypes(logCollectorState: any, config: any) { } function registerGlobalApi(logCollectorState: any) { - // @ts-expect-error TS(2339): Property 'TerminalReport' does not exist on type '... Remove this comment to see the full error message - Cypress.TerminalReport = { - getLogs: (format: any) => { + (Cypress as any).TerminalReport = { + getLogs: (format: string) => { const logs = logCollectorState.getCurrentLogStack(); if (format === 'txt') { @@ -89,14 +91,12 @@ function registerGlobalApi(logCollectorState: any) { }; } -function validateConfig(config: any) { +function validateConfig(config: SupportOptions) { const result = tv4.validateMultiple(config, schema); if (!result.valid) { throw new CtrError( - `Invalid plugin install options: ${tv4ErrorTransformer.toReadableString( - result.errors - )}` + `Invalid plugin install options: ${utils.tv4ToString(result.errors)}` ); } diff --git a/src/installLogsCollector.types.ts b/src/installLogsCollector.types.ts new file mode 100644 index 0000000..17e089a --- /dev/null +++ b/src/installLogsCollector.types.ts @@ -0,0 +1,88 @@ +import {Log, LogType, TestData} from "./types"; + +export interface SupportOptions { + /** + * What types of logs to collect and print. + * By default all types are enabled. + * The 'cy:command' is the general type that contain all types of commands that are not specially treated. + * @default ['cons:log', 'cons:info', 'cons:warn', 'cons:error', 'cy:log', 'cy:xhr', 'cy:fetch', 'cy:request', 'cy:intercept', 'cy:command'] + */ + collectTypes?: LogType[]; + + /** + * Callback to filter logs manually. The type is from the same list as for the collectTypes option. + * Severity can be of ['', 'error', 'warning']. + * @default undefined + */ + filterLog?: null | ((args: Log) => boolean); + + /** + * Callback to process logs manually. The type is from the same list as for the collectTypes option. + * Severity can be of ['', 'error', 'warning']. + * @default undefined + */ + processLog?: null | ((args: Log) => Log); + + /** + * Callback to collect each test case's logs after its run. + * @default undefined + */ + collectTestLogs?: ( + context: TestData, + messages: Log[] + ) => void; + + xhr?: { + /** + * Whether to print body for XHR requests. Controls request body printing as well when request data logging is + * enabled. + * @default true + */ + printBody?: boolean; + + /** + * Whether to print header data for XHR requests. + * @default false + */ + printHeaderData?: boolean; + + /** + * Whether to print request data for XHR requests besides response data. + * @default false + */ + printRequestData?: boolean; + }; + + /** + * Enables extended log collection: including after all and before all hooks. + * @unstable + * @default false + */ + enableExtendedCollector?: boolean; + + /** + * Enables continuous logging of logs to terminal one by one, as they get registerd or modified. + * @unstable + * @default false + */ + enableContinuousLogging?: boolean; + + /** + * Adds time information to logs. + * @default null + */ + commandTimings?: null | 'timestamp' | 'seconds' + + /** + * Enabled debug logging. + * @default false + */ + debug?: boolean; +} + +export interface ExtendedSupportOptions extends SupportOptions { + collectTypes: LogType[]; + collectBody?: boolean; + collectRequestData?: boolean; + collectHeaderData?: boolean; +} diff --git a/src/installLogsPrinter.d.ts b/src/installLogsPrinter.d.ts deleted file mode 100644 index d16bea6..0000000 --- a/src/installLogsPrinter.d.ts +++ /dev/null @@ -1,121 +0,0 @@ -/// -import type CustomOutputProcessor from "./outputProcessor/CustomOutputProcessor"; -import type { Severity as Severity_, LogType as LogType_, Log as Log_ } from "./installLogsCollector"; - -declare function installLogsPrinter(on: Cypress.PluginEvents, options?: installLogsPrinter.PluginOptions): void; -declare namespace installLogsPrinter { - export type Severity = Severity_ - export type LogType = LogType_ - export type Log = Log_ - - type AllMessages = { - [specPath: string]: { - [testTitle: string]: Log[] - } - }; - - type CustomOutputProcessorCallback = (this: CustomOutputProcessor, allMessages: AllMessages) => void; - - interface PluginOptions { - /** - * Max length of `cy.log` and `console.warn`/`console.error`. - * @default 800 - */ - defaultTrimLength?: number; - - /** - * Max length of `cy` commands. - * @default 800 - */ - commandTrimLength?: number; - - /** - * Max length of `cy.route` request data. - * @default 5000 - */ - routeTrimLength?: number; - - /** - * If set to a number greater or equal to 0, this amount of logs will be printed only around failing commands. - * Use this to have shorter output especially for when there are a lot of commands in tests. - * When used with `options.printLogs=always`, for tests that don't have any `severity=error` logs, nothing will be printed. - * @default null - */ - compactLogs?: number | null; - - /** - * If it is set to a number greater or equal to 0, will override `compactLogs` for the file log output specifically. - * Use this for compacting of the terminal and the file output logs to different levels. - * @default null - */ - outputCompactLogs?: false | number | null; - - /** - * Required if outputTarget provided. [More details](https://github.com/archfz/cypress-terminal-report#logging-to-files). - * @default null - */ - outputRoot?: string | null; - - /** - * Output logs to files. [More details](https://github.com/archfz/cypress-terminal-report#logging-to-files). - * @default null - */ - outputTarget?: Record< - string, - | 'json' - | 'txt' - | CustomOutputProcessorCallback - >; - - /** - * Toggles verbose output. - * @default true - */ - outputVerbose?: boolean | true; - - /** - * Toggles debug output. - * @default false - */ - debug?: boolean; - - /** - * Cypress specs root relative to package json. [More details](https://github.com/archfz/cypress-terminal-report#logging-to-files). - * @default null - */ - specRoot?: string | null; - - /** - * When set to always logs will be printed for console for successful test as well as failing ones. - * @default 'onFail' - */ - printLogsToConsole?: 'onFail' | 'always' | 'never'; - - /** - * When set to always logs will be printed to file for successful test as well as failing ones. - * @default 'onFail' - */ - printLogsToFile?: 'onFail' | 'always' | 'never'; - - /** - * Whether to log commands from hooks that passed. - * If enabled even when all tests pass in a spec the commands will always - * be printed from before and after hooks. - * @default false - */ - includeSuccessfulHookLogs?: boolean; - - /** - * When set to `true`, enables additional log write pass to files. - * @default false - */ - logToFilesOnAfterRun?: boolean; - - /** - * Callback to collect each test case's logs after its run. - * @default undefined - */ - collectTestLogs?: (context: {spec: string, test: string, state: string}, messages: Log[]) => void; - } -} -export = installLogsPrinter; diff --git a/src/installLogsPrinter.ts b/src/installLogsPrinter.ts index 3f44384..3a0ac4c 100755 --- a/src/installLogsPrinter.ts +++ b/src/installLogsPrinter.ts @@ -2,17 +2,20 @@ import chalk from 'chalk'; import path from 'path'; import tv4 from 'tv4'; import schema from './installLogsPrinter.schema.json'; -import tv4ErrorTransformer from './tv4ErrorTransformer'; import CtrError from './CtrError'; import CONSTANTS from './constants'; import CustomOutputProcessor from './outputProcessor/CustomOutputProcessor'; import NestedOutputProcessorDecorator from './outputProcessor/NestedOutputProcessorDecorator'; import JsonOutputProcessor from "./outputProcessor/JsonOutputProcessor"; import TextOutputProcessor from "./outputProcessor/TextOutputProcessor"; +import {CustomOutputProcessorCallback, PluginOptions} from "./installLogsPrinter.types"; +import {Log, MessageData} from "./types"; +import {IOutputProcecessor} from "./outputProcessor/BaseOutputProcessor"; +import utils from "./utils"; const LOG_TYPES = CONSTANTS.LOG_TYPES; const KNOWN_TYPES = Object.values(CONSTANTS.LOG_TYPES); -const OUTPUT_PROCESSOR_TYPE = { +const OUTPUT_PROCESSOR_TYPE: Record = { 'json': JsonOutputProcessor, 'txt': TextOutputProcessor, }; @@ -39,11 +42,11 @@ const LOG_SYMBOLS = (() => { } })(); -let writeToFileMessages = {}; -let outputProcessors: any = []; +let writeToFileMessages: Record> = {}; +let outputProcessors: IOutputProcecessor[] = []; -const createLogger = (enabled: any) => enabled - ? (message: any) => console.log(`[cypress-terminal-report:debug] ${message}`) +const createLogger = (enabled: boolean) => enabled + ? (message: string) => console.log(`[cypress-terminal-report:debug] ${message}`) : () => {} /** @@ -54,69 +57,54 @@ const createLogger = (enabled: any) => enabled * @see ./installLogsPrinter.d.ts * @type {import('./installLogsPrinter')} */ -function installLogsPrinter(on: any, options = {}) { - // @ts-expect-error TS(2339): Property 'printLogsToFile' does not exist on type ... Remove this comment to see the full error message +function installLogsPrinter(on: Cypress.PluginEvents, options: PluginOptions = {}) { options.printLogsToFile = options.printLogsToFile || "onFail"; - // @ts-expect-error TS(2339): Property 'printLogsToConsole' does not exist on ty... Remove this comment to see the full error message options.printLogsToConsole = options.printLogsToConsole || "onFail"; const result = tv4.validateMultiple(options, schema); if (!result.valid) { - throw new CtrError(`Invalid plugin install options: ${tv4ErrorTransformer.toReadableString(result.errors)}`); + throw new CtrError(`Invalid plugin install options: ${utils.tv4ToString(result.errors)}`); } // @ts-expect-error TS(2339): Property 'debug' does not exist on type '{}'. const logDebug = createLogger(options.debug); on('task', { - [CONSTANTS.TASK_NAME]: function (/** @type {Data} */ data: any) { + [CONSTANTS.TASK_NAME]: function (data: MessageData) { logDebug(`${CONSTANTS.TASK_NAME}: Received ${data.messages.length} messages, for ${data.spec}:${data.test}, with state ${data.state}.`); let messages = data.messages; const terminalMessages = - // @ts-expect-error TS(2339): Property 'compactLogs' does not exist on type '{}'... Remove this comment to see the full error message typeof options.compactLogs === 'number' && options.compactLogs >= 0 - // @ts-expect-error TS(2339): Property 'compactLogs' does not exist on type '{}'... Remove this comment to see the full error message ? compactLogs(messages, options.compactLogs, logDebug) : messages; const isHookAndShouldLog = data.isHook && - // @ts-expect-error TS(2339): Property 'includeSuccessfulHookLogs' does not exis... Remove this comment to see the full error message (options.includeSuccessfulHookLogs || data.state === 'failed'); - // @ts-expect-error TS(2339): Property 'outputTarget' does not exist on type '{}... Remove this comment to see the full error message if (options.outputTarget && options.printLogsToFile !== "never") { if ( data.state === "failed" || - // @ts-expect-error TS(2339): Property 'printLogsToFile' does not exist on type ... Remove this comment to see the full error message options.printLogsToFile === "always" || isHookAndShouldLog ) { let outputFileMessages = - // @ts-expect-error TS(2339): Property 'outputCompactLogs' does not exist on typ... Remove this comment to see the full error message typeof options.outputCompactLogs === 'number' - // @ts-expect-error TS(2339): Property 'outputCompactLogs' does not exist on typ... Remove this comment to see the full error message ? compactLogs(messages, options.outputCompactLogs, logDebug) - // @ts-expect-error TS(2339): Property 'outputCompactLogs' does not exist on typ... Remove this comment to see the full error message : options.outputCompactLogs === false ? messages : terminalMessages; logDebug(`Storing for file logging ${outputFileMessages.length} messages, for ${data.spec}:${data.test}.`); - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message writeToFileMessages[data.spec] = writeToFileMessages[data.spec] || {}; - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message writeToFileMessages[data.spec][data.test] = outputFileMessages; } } if ( - // @ts-expect-error TS(2339): Property 'printLogsToConsole' does not exist on ty... Remove this comment to see the full error message options.printLogsToConsole !== "never" && ( - // @ts-expect-error TS(2339): Property 'printLogsToConsole' does not exist on ty... Remove this comment to see the full error message options.printLogsToConsole === "always" - // @ts-expect-error TS(2339): Property 'printLogsToConsole' does not exist on ty... Remove this comment to see the full error message || (options.printLogsToConsole === "onFail" && data.state !== "passed") || isHookAndShouldLog ) @@ -125,10 +113,8 @@ function installLogsPrinter(on: any, options = {}) { logToTerminal(terminalMessages, options, data); } - // @ts-expect-error TS(2339): Property 'collectTestLogs' does not exist on type ... Remove this comment to see the full error message if (options.collectTestLogs) { logDebug(`Running \`collectTestLogs\` on ${terminalMessages.length} messages, for ${data.spec}:${data.test}.`); - // @ts-expect-error TS(2339): Property 'collectTestLogs' does not exist on type ... Remove this comment to see the full error message options.collectTestLogs( {spec: data.spec, test: data.test, state: data.state}, terminalMessages @@ -144,12 +130,8 @@ function installLogsPrinter(on: any, options = {}) { } }); - // @ts-expect-error TS(2339): Property 'outputTarget' does not exist on type '{}... Remove this comment to see the full error message - if (options.outputTarget) { - installOutputProcessors(on, options); - } + installOutputProcessors(on, options); - // @ts-expect-error TS(2339): Property 'logToFilesOnAfterRun' does not exist on ... Remove this comment to see the full error message if (options.logToFilesOnAfterRun) { on('after:run', () => { logDebug(`after:run: Attempting file logging on after run.`); @@ -158,8 +140,8 @@ function installLogsPrinter(on: any, options = {}) { } } -function logToFiles(/** @type {PluginOptions} */ options: any) { - outputProcessors.forEach((processor: any) => { +function logToFiles(options: PluginOptions) { + outputProcessors.forEach((processor) => { if (Object.entries(writeToFileMessages).length !== 0){ processor.write(writeToFileMessages); if (options.outputVerbose !== false) @@ -170,10 +152,9 @@ function logToFiles(/** @type {PluginOptions} */ options: any) { } -function logOutputTarget(processor: any) { +function logOutputTarget(processor: IOutputProcecessor) { let message; let standardOutputType = Object.keys(OUTPUT_PROCESSOR_TYPE).find( - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message (type) => processor instanceof OUTPUT_PROCESSOR_TYPE[type] ); if (standardOutputType) { @@ -183,26 +164,30 @@ function logOutputTarget(processor: any) { } console.log('cypress-terminal-report:', message); } -function installOutputProcessors(on: any, /** @type {PluginOptions} */ options: any) { + +function installOutputProcessors(on: Cypress.PluginEvents, options: PluginOptions) { + if (!options.outputTarget) { + return; + } if (!options.outputRoot) { throw new CtrError(`Missing outputRoot configuration.`); } - const createProcessorFromType = (file: any, type: any) => { + const createProcessorFromType = (file: string, type: string | CustomOutputProcessorCallback) => { if (typeof type === 'string') { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message - return new OUTPUT_PROCESSOR_TYPE[type](path.join(options.outputRoot, file)); + return new OUTPUT_PROCESSOR_TYPE[type](path.join(options.outputRoot || '', file)); } if (typeof type === 'function') { - return new CustomOutputProcessor(path.join(options.outputRoot, file), type); + return new CustomOutputProcessor(path.join(options.outputRoot || '', file), type); } + + throw new Error('Unexpected type case.'); }; Object.entries(options.outputTarget).forEach(([file, type]) => { const requiresNested = file.match(/^[^|]+\|.*$/); - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message if (typeof type === 'string' && !OUTPUT_PROCESSOR_TYPE[type]) { throw new CtrError(`Unknown output format '${type}'.`); } @@ -214,7 +199,7 @@ function installOutputProcessors(on: any, /** @type {PluginOptions} */ options: const parts = file.split('|'); const root = parts[0]; const ext = parts[1]; - outputProcessors.push(new NestedOutputProcessorDecorator(root, options.specRoot, ext, (nestedFile: any) => { + outputProcessors.push(new NestedOutputProcessorDecorator(root, options.specRoot || '', ext, (nestedFile: string) => { return createProcessorFromType(nestedFile, type); })); } else { @@ -222,25 +207,22 @@ function installOutputProcessors(on: any, /** @type {PluginOptions} */ options: } }); - outputProcessors.forEach((processor: any) => processor.initialize()); + outputProcessors.forEach((processor) => processor.initialize()); } function compactLogs( - /** @type {Log[]} */ - logs: any, - /** @type {number} */ - keepAroundCount: any, - /** @type {function} */ - logDebug: any, + logs: Log[], + keepAroundCount: number, + logDebug: (message: string) => void, ) { logDebug(`Compacting ${logs.length} logs.`) - const failingIndexes = logs.filter((log: any) => log.severity === CONSTANTS.SEVERITY.ERROR) - .map((log: any) => logs.indexOf(log)); + const failingIndexes = logs.filter((log) => log.severity === CONSTANTS.SEVERITY.ERROR) + .map((log) => logs.indexOf(log)); const includeIndexes = new Array(logs.length); - failingIndexes.forEach((index: any) => { + failingIndexes.forEach((index) => { const from = Math.max(0, index - keepAroundCount); const to = Math.min(logs.length - 1, index + keepAroundCount); for (let i = from; i <= to; i++) { @@ -249,7 +231,7 @@ function compactLogs( }); const compactedLogs = []; - const addOmittedLog = (count: any) => compactedLogs.push({ + const addOmittedLog = (count: number) => compactedLogs.push({ type: CONSTANTS.LOG_TYPES.PLUGIN_LOG_TYPE, message: `[ ... ${count} omitted logs ... ]`, severity: CONSTANTS.SEVERITY.SUCCESS @@ -277,16 +259,14 @@ function compactLogs( } function logToTerminal( - /** @type {Log[]} */ - messages: any, - /** @type {PluginOptions} */ - options: any, - /** @type {Data} */ - data: any) { + messages: Log[], + options: PluginOptions, + data: MessageData +) { const tabLevel = data.level || 0; const levelPadding = ' '.repeat(Math.max(0, tabLevel - 1)); const padding = CONSTANTS.PADDING.LOG + levelPadding; - const padType = (type: any) => new Array(Math.max(padding.length - type.length - 3, 0)).join(' ') + type + ' '; + const padType = (type: string) => new Array(Math.max(padding.length - type.length - 3, 0)).join(' ') + type + ' '; if (data.consoleTitle) { console.log(' '.repeat(4) + levelPadding + chalk.gray(data.consoleTitle)); @@ -297,7 +277,7 @@ function logToTerminal( message, severity, timeString - }: any) => { + }) => { let color = 'white', typeString = KNOWN_TYPES.includes(type) ? padType(type) : padType('[unknown]'), processedMessage = message, diff --git a/src/installLogsPrinter.types.ts b/src/installLogsPrinter.types.ts new file mode 100644 index 0000000..5f1ad00 --- /dev/null +++ b/src/installLogsPrinter.types.ts @@ -0,0 +1,113 @@ +/// +import type CustomOutputProcessor from "./outputProcessor/CustomOutputProcessor"; +import {Log} from "./types"; + +export type AllMessages = { + [specPath: string]: { + [testTitle: string]: Log[] + } +}; + +export type CustomOutputProcessorCallback = (this: CustomOutputProcessor, allMessages: AllMessages) => void; + +export interface PluginOptions { + /** + * Max length of `cy.log` and `console.warn`/`console.error`. + * @default 800 + */ + defaultTrimLength?: number; + + /** + * Max length of `cy` commands. + * @default 800 + */ + commandTrimLength?: number; + + /** + * Max length of `cy.route` request data. + * @default 5000 + */ + routeTrimLength?: number; + + /** + * If set to a number greater or equal to 0, this amount of logs will be printed only around failing commands. + * Use this to have shorter output especially for when there are a lot of commands in tests. + * When used with `options.printLogs=always`, for tests that don't have any `severity=error` logs, nothing will be printed. + * @default null + */ + compactLogs?: number | null; + + /** + * If it is set to a number greater or equal to 0, will override `compactLogs` for the file log output specifically. + * Use this for compacting of the terminal and the file output logs to different levels. + * @default null + */ + outputCompactLogs?: false | number | null; + + /** + * Required if outputTarget provided. [More details](https://github.com/archfz/cypress-terminal-report#logging-to-files). + * @default null + */ + outputRoot?: string | null; + + /** + * Output logs to files. [More details](https://github.com/archfz/cypress-terminal-report#logging-to-files). + * @default null + */ + outputTarget?: Record< + string, + | 'json' + | 'txt' + | CustomOutputProcessorCallback + >; + + /** + * Toggles verbose output. + * @default true + */ + outputVerbose?: boolean | true; + + /** + * Toggles debug output. + * @default false + */ + debug?: boolean; + + /** + * Cypress specs root relative to package json. [More details](https://github.com/archfz/cypress-terminal-report#logging-to-files). + * @default null + */ + specRoot?: string | null; + + /** + * When set to always logs will be printed for console for successful test as well as failing ones. + * @default 'onFail' + */ + printLogsToConsole?: 'onFail' | 'always' | 'never'; + + /** + * When set to always logs will be printed to file for successful test as well as failing ones. + * @default 'onFail' + */ + printLogsToFile?: 'onFail' | 'always' | 'never'; + + /** + * Whether to log commands from hooks that passed. + * If enabled even when all tests pass in a spec the commands will always + * be printed from before and after hooks. + * @default false + */ + includeSuccessfulHookLogs?: boolean; + + /** + * When set to `true`, enables additional log write pass to files. + * @default false + */ + logToFilesOnAfterRun?: boolean; + + /** + * Callback to collect each test case's logs after its run. + * @default undefined + */ + collectTestLogs?: (context: {spec: string, test: string, state: string}, messages: Log[]) => void; +} diff --git a/src/outputProcessor/BaseOutputProcessor.d.ts b/src/outputProcessor/BaseOutputProcessor.d.ts deleted file mode 100644 index 3bf050f..0000000 --- a/src/outputProcessor/BaseOutputProcessor.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Base output processor class that the actual output processors extend. - */ -declare class BaseOutputProcessor { - constructor(file: string); - - /** - * Current char size of the output file. - */ - size: number; - /** - * The count of the chunk to be written. - */ - atChunk: number; - /** - * The initial content of the file. Defaults to `''`. Set this before the first chunk write in order for it to work. - */ - initialContent: string; - /** - * Chunk separator string. Defaults to `''`. This string will be written between each chunk. - * If you need a special separator between chunks, use this as it is internally handled to properly write and replace the chunks. - */ - chunkSeparator: string; - - /** - * Writes a chunk of data in the output file. - */ - writeSpecChunk(spec: string, chunk: string, pos?: number | null): void; -} - -export = BaseOutputProcessor; \ No newline at end of file diff --git a/src/outputProcessor/BaseOutputProcessor.ts b/src/outputProcessor/BaseOutputProcessor.ts index c3581bc..2205a27 100755 --- a/src/outputProcessor/BaseOutputProcessor.ts +++ b/src/outputProcessor/BaseOutputProcessor.ts @@ -1,15 +1,23 @@ import * as fs from 'fs'; import * as path from 'path'; import CtrError from '../CtrError'; +import {AllMessages} from "../installLogsPrinter.types"; + +export interface IOutputProcecessor { + initialize(): void; + getTarget(): string; + getSpentTime(): number; + write(allMessages: AllMessages): void; +} export default class BaseOutputProcessor { - atChunk: any; - chunkSeparator: any; - file: any; - initialContent: any; - size: any; - specChunksWritten: any; - writeSpendTime: any; + protected atChunk: any; + protected chunkSeparator: string; + protected file: any; + protected initialContent: any; + protected size: any; + protected specChunksWritten: any; + protected writeSpendTime: any; constructor(file: any) { this.file = file; diff --git a/src/outputProcessor/CustomOutputProcessor.d.ts b/src/outputProcessor/CustomOutputProcessor.d.ts deleted file mode 100644 index 7e4a0fd..0000000 --- a/src/outputProcessor/CustomOutputProcessor.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { CustomOutputProcessorCallback } from "../installLogsPrinter"; -import type BaseOutputProcessor from "./BaseOutputProcessor"; - -/** - * Gives the functions and variables available for use when specifying a custom output processor. [More details](https://github.com/archfz/cypress-terminal-report#custom-output-log-processor). - * - * i.e. Allows use of `this.writeSpecChunk` without ts warning/error. - * - * @example - * ``` - * import type { AllMessages } from 'cypress-terminal-report/src/installLogsPrinter' - * import type CustomOutputProcessor from 'cypress-terminal-report/src/outputProcessor/CustomOutputProcessor' - * - * ... - * - * outputTarget: { - * 'custom|cts': function outputProcessor(this: CustomOutputProcessor, allMessages: AllMessages){ } - * } - * ``` - */ -declare class CustomOutputProcessor extends BaseOutputProcessor { - constructor(file: string, processorCallback: CustomOutputProcessorCallback); -} - -export = CustomOutputProcessor; \ No newline at end of file diff --git a/src/outputProcessor/CustomOutputProcessor.ts b/src/outputProcessor/CustomOutputProcessor.ts index 2d96627..6c16f40 100644 --- a/src/outputProcessor/CustomOutputProcessor.ts +++ b/src/outputProcessor/CustomOutputProcessor.ts @@ -1,16 +1,17 @@ -import BaseOutputProcessor from './BaseOutputProcessor'; +import BaseOutputProcessor, {IOutputProcecessor} from './BaseOutputProcessor'; +import {AllMessages} from "../installLogsPrinter.types"; -export default class CustomOutputProcessor extends BaseOutputProcessor { +export default class CustomOutputProcessor extends BaseOutputProcessor implements IOutputProcecessor { processorCallback: any; - constructor(file: any, - /** @type {import('../installLogsPrinter').CustomOutputProcessorCallback} */ + constructor(file: any, + /** @type {import('../installLogsPrinter').CustomOutputProcessorCallback} */ processorCallback: any) { super(file); this.processorCallback = processorCallback; } - write(/** @type {import('../installLogsPrinter').AllMessages} */ allMessages: any) { + write(allMessages: AllMessages) { this.processorCallback.call(this, allMessages); } }; diff --git a/src/outputProcessor/JsonOutputProcessor.d.ts b/src/outputProcessor/JsonOutputProcessor.d.ts deleted file mode 100644 index b184620..0000000 --- a/src/outputProcessor/JsonOutputProcessor.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type BaseOutputProcessor from "./BaseOutputProcessor"; - -declare class JsonOutputProcessor extends BaseOutputProcessor {} - -export = JsonOutputProcessor; - \ No newline at end of file diff --git a/src/outputProcessor/JsonOutputProcessor.ts b/src/outputProcessor/JsonOutputProcessor.ts index dfc5e42..41337f7 100644 --- a/src/outputProcessor/JsonOutputProcessor.ts +++ b/src/outputProcessor/JsonOutputProcessor.ts @@ -1,6 +1,6 @@ -import BaseOutputProcessor from './BaseOutputProcessor'; +import BaseOutputProcessor, {IOutputProcecessor} from './BaseOutputProcessor'; -export default class JsonOutputProcessor extends BaseOutputProcessor { +export default class JsonOutputProcessor extends BaseOutputProcessor implements IOutputProcecessor { chunkSeparator: any; initialContent: any; writeSpecChunk: any; diff --git a/src/outputProcessor/NestedOutputProcessorDecorator.ts b/src/outputProcessor/NestedOutputProcessorDecorator.ts index bdecc98..82bb533 100755 --- a/src/outputProcessor/NestedOutputProcessorDecorator.ts +++ b/src/outputProcessor/NestedOutputProcessorDecorator.ts @@ -1,26 +1,28 @@ import * as path from 'path'; +import {IOutputProcecessor} from "./BaseOutputProcessor"; +import {AllMessages} from "../installLogsPrinter.types"; -export default class NestedOutputProcessorDecorator { - decoratedFactory: any; - ext: any; - processors: any; - root: any; - specRoot: any; +export default class NestedOutputProcessorDecorator implements IOutputProcecessor { + protected decoratedFactory: (directory: string) => IOutputProcecessor; + protected ext: string; + protected processors: Record; + protected root: string; + protected specRoot: string; - constructor(root: any, specRoot: any, ext: any, decoratedFactory: any) { + constructor(root: string, specRoot: string, ext: string, decoratedFactory: (directory: string) => IOutputProcecessor) { this.root = root; this.ext = ext; - this.specRoot = specRoot || ''; + this.specRoot = specRoot; this.decoratedFactory = decoratedFactory; - this.processors = []; + this.processors = {}; } initialize() { /* noop */ } - getProcessor(spec: any) { + getProcessor(spec: string) { if (this.processors[spec]) { return this.processors[spec]; } @@ -35,7 +37,7 @@ export default class NestedOutputProcessorDecorator { return processor; } - write(/** @type {import('../installLogsPrinter').AllMessages} */ allMessages: any) { + write(allMessages: AllMessages) { Object.entries(allMessages).forEach(([spec, messages]) => { this.getProcessor(spec).write({[spec]: messages}); }); @@ -46,6 +48,7 @@ export default class NestedOutputProcessorDecorator { } getSpentTime() { - return Object.values(this.processors).reduce((count: any, processor: any) => count + processor.getSpentTime(), 0); + return Object.values(this.processors) + .reduce((count: number, processor: IOutputProcecessor) => count + processor.getSpentTime(), 0); } }; diff --git a/src/outputProcessor/TextOutputProcessor.d.ts b/src/outputProcessor/TextOutputProcessor.d.ts deleted file mode 100644 index 4e10001..0000000 --- a/src/outputProcessor/TextOutputProcessor.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type BaseOutputProcessor from "./BaseOutputProcessor"; - -declare class TextOutputProcessor extends BaseOutputProcessor {} - -export = TextOutputProcessor; - \ No newline at end of file diff --git a/src/outputProcessor/TextOutputProcessor.ts b/src/outputProcessor/TextOutputProcessor.ts index 8ef1712..093dbb1 100644 --- a/src/outputProcessor/TextOutputProcessor.ts +++ b/src/outputProcessor/TextOutputProcessor.ts @@ -1,22 +1,19 @@ -import BaseOutputProcessor from './BaseOutputProcessor'; +import BaseOutputProcessor, {IOutputProcecessor} from './BaseOutputProcessor'; import logsTxtFormatter from './logsTxtFormatter'; import { EOL } from 'os'; +import {AllMessages} from "../installLogsPrinter.types"; const PADDING = ' '; -export default class TextOutputProcessor extends BaseOutputProcessor { - chunkSeparator: any; - writeSpecChunk: any; - - constructor(file: any) { +export default class TextOutputProcessor extends BaseOutputProcessor implements IOutputProcecessor { + constructor(file: string) { super(file); this.chunkSeparator = EOL + EOL; } - write(/** @type {import('../installLogsPrinter').AllMessages} */ allMessages: any) { + write(allMessages: AllMessages) { Object.entries(allMessages).forEach(([spec, tests]) => { let text = `${spec}:${EOL}`; - // @ts-expect-error TS(2550): Property 'entries' does not exist on type 'ObjectC... Remove this comment to see the full error message Object.entries(tests).forEach(([test, messages]) => { text += `${PADDING}${test}${EOL}`; text += logsTxtFormatter(messages, EOL); diff --git a/src/outputProcessor/logsTxtFormatter.ts b/src/outputProcessor/logsTxtFormatter.ts index 0228d8b..83ec8d6 100644 --- a/src/outputProcessor/logsTxtFormatter.ts +++ b/src/outputProcessor/logsTxtFormatter.ts @@ -1,24 +1,25 @@ import CONSTANTS from "../constants"; +import {Log} from "../types"; const PADDING = ' '; const PADDING_LOGS = `${PADDING}`.repeat(6); -const padTypeText = (text: any) => { +const padTypeText = (text: string) => { return Array(Math.max(PADDING_LOGS.length - text.length + 1, 0)).join(' ') + text; } -const padTimeText = (text: any) => { +const padTimeText = (text: string) => { return PADDING_LOGS + text; } -function logsTxtFormatter(logs: any, EOL = '\n') { +function logsTxtFormatter(logs: Log[], EOL = '\n') { return logs.map(({ type, message, severity, timeString - }: any) => { + }) => { let formattedLog = (padTypeText(`${type} (${{ [CONSTANTS.SEVERITY.ERROR]: 'X', [CONSTANTS.SEVERITY.WARNING]: '!', diff --git a/src/tv4ErrorTransformer.ts b/src/tv4ErrorTransformer.ts deleted file mode 100644 index ecd0ffd..0000000 --- a/src/tv4ErrorTransformer.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - toReadableString: function (errorList: any) { - return '\n' + errorList.map((error: any) => { - return `=> ${error.dataPath.replace(/\//, '.')}: ${error.message}`; - }).join('\n') + '\n'; - } -}; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..cb8b406 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,39 @@ +export type Severity = 'success' | 'error' | 'warning'; + +export type LogType = 'cons:log' | + 'cons:info' | + 'cons:warn' | + 'cons:error' | + 'cons:debug' | + 'cy:log' | + 'cy:xhr' | + 'cy:fetch' | + 'cy:request' | + 'cy:intercept' | + 'cy:command' | + 'ctr:info'; + +export type Log = { + type: LogType, + message: string, + severity: Severity, + timeString?: string, +}; + +export type MessageData = { + spec: string, + test: string, + state: 'failed' | 'passed', + messages: Log[], + consoleTitle?: string; + level?: number, + isHook?: boolean; + continuous?: boolean; +} + +export type TestData = { + mochaRunnable: Mocha.Runnable, + testState?: string, + testTitle: string, + testLevel: number +} diff --git a/src/utils.ts b/src/utils.ts index 0e3da6d..6330c28 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,6 @@ import semver from "semver"; import jsonPrune from "./jsonPrune"; +import tv4 from "tv4"; const utils = { nonQueueTask: function (name: any, data: any) { @@ -83,6 +84,12 @@ const utils = { } return json; + }, + + tv4ToString: function (errorList: tv4.ValidationError[]) { + return '\n' + errorList.map((error: any) => { + return `=> ${error.dataPath.replace(/\//, '.')}: ${error.message}`; + }).join('\n') + '\n'; } } diff --git a/tsconfig.json b/tsconfig.json index 6085a5c..e5274f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -50,7 +50,7 @@ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */