From f6bb35eb4820305881f5928009ed781f6f9a5d76 Mon Sep 17 00:00:00 2001 From: Fodor Levente Date: Thu, 24 Sep 2020 15:07:01 +0300 Subject: [PATCH 1/9] Added new option for printing to console: "never", what gets printed is now decided in LogsPrinter rather than LogsCollector. --- package-lock.json | 2 +- src/installLogsCollector.js | 60 ++++++++++++++-------------- src/installLogsCollector.schema.json | 4 -- src/installLogsPrinter.js | 48 ++++++++++++---------- src/installLogsPrinter.schema.json | 8 ++++ test/cypress/plugins/index.js | 2 + test/package.json | 2 +- 7 files changed, 68 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9dca64a..74e1a74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cypress-terminal-report", - "version": "1.4.2", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/installLogsCollector.js b/src/installLogsCollector.js index 80316a1..7cfad37 100755 --- a/src/installLogsCollector.js +++ b/src/installLogsCollector.js @@ -89,16 +89,16 @@ function installLogsCollector(config = {}) { logs = []; }); + //if (this.currentTest.state !== 'passed' || (config && config.printLogs === 'always')) afterEach(function() { - if (this.currentTest.state !== 'passed' || (config && config.printLogs === 'always')) { - // Need to wait otherwise some last commands get omitted from logs. - cy.wait(3, {log: false}); - cy.task(CONSTANTS.TASK_NAME, { - spec: this.test.file, - test: this.currentTest.title, - messages: logs - }, {log: false}); - } + // Need to wait otherwise some last commands get omitted from logs. + cy.wait(3, {log: false}); + cy.task(CONSTANTS.TASK_NAME, { + spec: this.test.file, + test: this.currentTest.title, + messages: logs, + state: this.currentTest.state + }, {log: false}); }); after(function () { @@ -188,7 +188,7 @@ function collectCypressXhrLog(addLog) { Cypress.on('log:added', options => { if (options.instrument === 'command' && options.consoleProps && options.name === 'xhr') { let detailMessage = (options.consoleProps.Stubbed === 'Yes' ? 'STUBBED ' : '') + - options.consoleProps.Method + ' ' + options.consoleProps.URL; + options.consoleProps.Method + ' ' + options.consoleProps.URL; const log = options.message + detailMessage; const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.WARNING : ''; @@ -199,7 +199,7 @@ function collectCypressXhrLog(addLog) { function collectCypressRequestCommand(addLog, formatXhrLog) { const isValidHttpMethod = (str) => - typeof str === 'string' && methods.some(s => str.toLowerCase().includes(s)); + typeof str === 'string' && methods.some(s => str.toLowerCase().includes(s)); Cypress.Commands.overwrite('request', async (originalFn, ...args) => { let log; @@ -226,11 +226,11 @@ function collectCypressRequestCommand(addLog, formatXhrLog) { let status = xhr ? xhr.status : {}; log += `\n` + formatXhrLog( - status, - await xhrPartParse(requestHeaders), - await xhrPartParse(requestBody), - await xhrPartParse(headers), - await xhrPartParse(body), + status, + await xhrPartParse(requestHeaders), + await xhrPartParse(requestBody), + await xhrPartParse(headers), + await xhrPartParse(body), ); addLog([LOG_TYPE.CYPRESS_REQUEST, log, CONSTANTS.SEVERITY.ERROR]); @@ -238,11 +238,11 @@ function collectCypressRequestCommand(addLog, formatXhrLog) { }); log += `\n` + formatXhrLog( - response.status, - await xhrPartParse(requestHeaders), - await xhrPartParse(requestBody), - await xhrPartParse(response.headers), - await xhrPartParse(response.body), + response.status, + await xhrPartParse(requestHeaders), + await xhrPartParse(requestBody), + await xhrPartParse(response.headers), + await xhrPartParse(response.body), ); addLog([LOG_TYPE.CYPRESS_REQUEST, log]); @@ -265,11 +265,11 @@ function collectCypressRouteCommand(addLog, formatXhrLog) { const severity = String(xhr.status).match(/^2[0-9]+$/) ? '' : CONSTANTS.SEVERITY.WARNING; let logMessage = `(${route.alias}) ${xhr.method} ${xhr.url}\n`; logMessage += formatXhrLog( - xhr.status, - await xhrPartParse(xhr.request.headers), - await xhrPartParse(xhr.request.body), - await xhrPartParse(xhr.response.headers), - await xhrPartParse(xhr.response.body), + xhr.status, + await xhrPartParse(xhr.request.headers), + await xhrPartParse(xhr.request.body), + await xhrPartParse(xhr.response.headers), + await xhrPartParse(xhr.response.body), ); addLog([ @@ -285,10 +285,10 @@ function collectCypressRouteCommand(addLog, formatXhrLog) { function collectCypressGeneralCommandLog(addLog) { Cypress.on('log:added', options => { if ( - options.instrument === 'command' && - options.consoleProps && - !(['xhr', 'log', 'request'].includes(options.name)) && - !(options.name === 'task' && options.message.match(/ctrLogMessages/)) + options.instrument === 'command' && + options.consoleProps && + !(['xhr', 'log', 'request'].includes(options.name)) && + !(options.name === 'task' && options.message.match(/ctrLogMessages/)) ) { const log = options.name + '\t' + options.message; const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.ERROR : ''; diff --git a/src/installLogsCollector.schema.json b/src/installLogsCollector.schema.json index 67432bf..8faef5c 100644 --- a/src/installLogsCollector.schema.json +++ b/src/installLogsCollector.schema.json @@ -1,10 +1,6 @@ { "type": "object", "properties": { - "printLogs": { - "type": "string", - "enum": ["onFail", "always"] - }, "collectTypes": { "type": "array", "items": { diff --git a/src/installLogsPrinter.js b/src/installLogsPrinter.js index bca5774..c42c601 100755 --- a/src/installLogsPrinter.js +++ b/src/installLogsPrinter.js @@ -61,18 +61,22 @@ function installLogsPrinter(on, options = {}) { on('task', { [CONSTANTS.TASK_NAME]: data => { - let messages = data.messages; + if (options.printLogsToConsole === "always" || + (options.printLogsToConsole === "onFail" && data.state !== "passed")){ + let messages = data.messages; - if (typeof options.compactLogs === 'number' && options.compactLogs >= 0) { - messages = compactLogs(messages, options.compactLogs); - } + if (typeof options.compactLogs === 'number' && options.compactLogs >= 0) { + messages = compactLogs(messages, options.compactLogs); + } + + if (options.outputTarget) { + allMessages[data.spec] = allMessages[data.spec] || {}; + allMessages[data.spec][data.test] = messages; + } - if (options.outputTarget) { - allMessages[data.spec] = allMessages[data.spec] || {}; - allMessages[data.spec][data.test] = messages; + logToTerminal(messages, options); } - logToTerminal(messages, options); return null; }, [CONSTANTS.TASK_NAME_OUTPUT]: () => { @@ -93,7 +97,7 @@ function installLogsPrinter(on, options = {}) { function logOutputTarget(processor) { let message; let standardOutputType = Object.keys(OUTPUT_PROCESSOR_TYPE).find( - (type) => processor instanceof OUTPUT_PROCESSOR_TYPE[type] + (type) => processor instanceof OUTPUT_PROCESSOR_TYPE[type] ); if (standardOutputType) { message = `Wrote ${standardOutputType} logs to ${processor.file}. (${processor.writeSpendTime}ms)`; @@ -127,7 +131,7 @@ function installOutputProcessors(on, root, outputTargets) { function compactLogs(logs, keepAroundCount) { const failingIndexes = logs.filter((log) => log[2] === CONSTANTS.SEVERITY.ERROR) - .map((log) => logs.indexOf(log)); + .map((log) => logs.indexOf(log)); const includeIndexes = new Array(logs.length); @@ -141,11 +145,11 @@ function compactLogs(logs, keepAroundCount) { const compactedLogs = []; const addOmittedLog = (count) => - compactedLogs.push([ - CONSTANTS.LOG_TYPES.PLUGIN_LOG_TYPE, - `[ ... ${count} omitted logs ... ]`, - CONSTANTS.SEVERITY.SUCCESS - ]); + compactedLogs.push([ + CONSTANTS.LOG_TYPES.PLUGIN_LOG_TYPE, + `[ ... ${count} omitted logs ... ]`, + CONSTANTS.SEVERITY.SUCCESS + ]); let excludeCount = 0; for (let i = 0; i < includeIndexes.length; i++) { @@ -170,14 +174,14 @@ function compactLogs(logs, keepAroundCount) { function logToTerminal(messages, options) { const padType = (type) => - new Array(Math.max(CONSTANTS.PADDING.LOG.length - type.length - 3, 0)).join(' ') + type + ' '; + new Array(Math.max(CONSTANTS.PADDING.LOG.length - type.length - 3, 0)).join(' ') + type + ' '; messages.forEach(([type, message, severity]) => { let color = 'white', - typeString = KNOWN_TYPES.includes(type) ? padType(type) : padType('[unknown]'), - processedMessage = message, - trim = options.defaultTrimLength || 800, - icon = '-'; + typeString = KNOWN_TYPES.includes(type) ? padType(type) : padType('[unknown]'), + processedMessage = message, + trim = options.defaultTrimLength || 800, + icon = '-'; if (type === LOG_TYPES.BROWSER_CONSOLE_WARN) { color = 'yellow'; @@ -224,8 +228,8 @@ function logToTerminal(messages, options) { } const coloredTypeString = ['red', 'yellow'].includes(color) ? - chalk[color].bold(typeString + icon + ' ') : - chalk[color](typeString + icon + ' '); + chalk[color].bold(typeString + icon + ' ') : + chalk[color](typeString + icon + ' '); console.log(coloredTypeString, processedMessage.replace(/\n/g, '\n' + CONSTANTS.PADDING.LOG)); }); diff --git a/src/installLogsPrinter.schema.json b/src/installLogsPrinter.schema.json index f1f410b..5928b11 100644 --- a/src/installLogsPrinter.schema.json +++ b/src/installLogsPrinter.schema.json @@ -1,6 +1,14 @@ { "type": "object", "properties": { + "printLogsToConsole": { + "type": "string", + "enum": ["onFail", "always", "never"] + }, + "printLogsToFile": { + "type": "string", + "enum": ["onFail", "always", "never"] + }, "defaultTrimLength": { "type": "string" }, diff --git a/test/cypress/plugins/index.js b/test/cypress/plugins/index.js index 3736ffe..8aa191c 100755 --- a/test/cypress/plugins/index.js +++ b/test/cypress/plugins/index.js @@ -30,5 +30,7 @@ module.exports = (on, config) => { }; } + options.printLogsToConsole = 'always'; + require('../../../src/installLogsPrinter')(on, options); }; diff --git a/test/package.json b/test/package.json index 422a0cf..cdad358 100755 --- a/test/package.json +++ b/test/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "mocha ./test.js", + "test": "echo 'mocha ./test.js' && npx cypress run", "wsl-open-cypress": "cmd.exe /C npx cypress open" }, "author": "Fodor Zoltan", From 2fcd1b31bad5a11c6c86f7e75bc105480539a022 Mon Sep 17 00:00:00 2001 From: Fodor Levente Date: Fri, 25 Sep 2020 14:20:45 +0300 Subject: [PATCH 2/9] Added new option for printing to file: "never". --- src/installLogsPrinter.js | 32 ++++++++++++++++++++------------ test/cypress/plugins/index.js | 1 + 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/installLogsPrinter.js b/src/installLogsPrinter.js index c42c601..50b12d9 100755 --- a/src/installLogsPrinter.js +++ b/src/installLogsPrinter.js @@ -35,6 +35,7 @@ const LOG_SYMBOLS = (() => { })(); let allMessages = {}; +let state = null; let outputProcessors = []; /** @@ -61,19 +62,21 @@ function installLogsPrinter(on, options = {}) { on('task', { [CONSTANTS.TASK_NAME]: data => { - if (options.printLogsToConsole === "always" || - (options.printLogsToConsole === "onFail" && data.state !== "passed")){ - let messages = data.messages; + let messages = data.messages; + state = data.state; - if (typeof options.compactLogs === 'number' && options.compactLogs >= 0) { - messages = compactLogs(messages, options.compactLogs); - } + if (typeof options.compactLogs === 'number' && options.compactLogs >= 0) { + messages = compactLogs(messages, options.compactLogs); + } - if (options.outputTarget) { - allMessages[data.spec] = allMessages[data.spec] || {}; - allMessages[data.spec][data.test] = messages; - } + if (options.outputTarget) { + allMessages[data.spec] = allMessages[data.spec] || {}; + allMessages[data.spec][data.test] = messages; + } + if (options.printLogsToConsole === "always" || + ((options.printLogsToConsole === "onFail" || typeof options.printLogsToConsole === 'undefined') + && state !== "passed")){ logToTerminal(messages, options); } @@ -81,10 +84,15 @@ function installLogsPrinter(on, options = {}) { }, [CONSTANTS.TASK_NAME_OUTPUT]: () => { outputProcessors.forEach((processor) => { - processor.write(allMessages); - logOutputTarget(processor); + if (options.printLogsToFile === "always" || + ((options.printLogsToFile === "onFail" || typeof options.printLogsToFile === 'undefined') + && state !== "passed")){ + processor.write(allMessages); + logOutputTarget(processor); + } }); allMessages = {}; + state = null; return null; } }); diff --git a/test/cypress/plugins/index.js b/test/cypress/plugins/index.js index 8aa191c..0992b71 100755 --- a/test/cypress/plugins/index.js +++ b/test/cypress/plugins/index.js @@ -30,6 +30,7 @@ module.exports = (on, config) => { }; } + options.printLogsToFile = 'never'; options.printLogsToConsole = 'always'; require('../../../src/installLogsPrinter')(on, options); From 66ab02df6ddf51fb16f45dca447c2ae177122ac1 Mon Sep 17 00:00:00 2001 From: Fodor Levente Date: Sat, 26 Sep 2020 17:17:29 +0300 Subject: [PATCH 3/9] Added tests for the new options and updated README.md to include the new options. --- README.md | 15 ++++++-- src/installLogsCollector.js | 3 +- .../integration/alwaysPrintLogs.spec.js | 7 ---- test/cypress/integration/printLogs.spec.js | 7 ++++ test/cypress/plugins/index.js | 15 ++++++-- test/cypress/support/index.js | 4 --- test/package.json | 2 +- test/test.js | 36 ++++++++++++++++--- 8 files changed, 66 insertions(+), 23 deletions(-) delete mode 100755 test/cypress/integration/alwaysPrintLogs.spec.js create mode 100755 test/cypress/integration/printLogs.spec.js diff --git a/README.md b/README.md index 3a458bc..ff1fd2e 100755 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ integer; default: 5000; Max length of cy.route request data. #### `options.compactLogs` integer?; default: null; If it is 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 when there are a lot of commands in tests. When used with `options.printLogsToConsole=always` for tests that don't have any `severity=error` logs nothing will be printed. #### `options.outputRoot` @@ -71,13 +71,22 @@ string; default: null; Required if `options.outputTarget` provided. [More detail #### `options.outputTarget` object; default: null; Output logs to files. [More details](#logging-to-files). +#### `options.printLogsToConsole` +string; Default: 'onFail'. When to print logs to console, possible values: 'always', 'onFail', 'never' - When set to always +logs will be printed to console for successful test as well as failing ones. + +#### `options.printLogsToFile` +string; Default: 'onFail'. When to print logs to file(s), possible values: 'always', 'onFail', 'never' - When set to always +logs will be printed to file(s) for successful test as well as failing ones. + ### Options for the support install > require('cypress-terminal-report/src/installLogsCollector')(options); #### `options.printLogs` -string; default: 'onFail'; possible values: 'onFail', 'always' - When set to always -logs will be printed for successful test as well as failing ones. +~~string; default: 'onFail'; possible values: 'onFail', 'always' - When set to always +logs will be printed for successful test as well as failing ones.~~ +Deprecated. #### `options.collectTypes` array; default: ['cons:log','cons:info', 'cons:warn', 'cons:error', 'cy:log', 'cy:xhr', 'cy:request', 'cy:route', 'cy:command'] diff --git a/src/installLogsCollector.js b/src/installLogsCollector.js index 7cfad37..e9eff62 100755 --- a/src/installLogsCollector.js +++ b/src/installLogsCollector.js @@ -15,7 +15,8 @@ const tv4ErrorTransformer = require('./tv4ErrorTransformer'); * * @param {object} config * Options for collection logs: - * - printLogs?: string; Default: 'onFail'. When to print logs, possible values: 'always', 'onFail'. + * - printLogsToConsole?: string; Default: 'onFail'. When to print logs to console, possible values: 'always', 'onFail', 'never'. + * - printLogsToFile?: string; Default: 'onFail'. When to print logs to file(s), possible values: 'always', 'onFail', 'never'. * - collectTypes?: array; Collect only these types of logs. Defaults to all types. * - filterLog?: ([type, message, severity]) => boolean; Callback to filter logs manually. * - xhr?: diff --git a/test/cypress/integration/alwaysPrintLogs.spec.js b/test/cypress/integration/alwaysPrintLogs.spec.js deleted file mode 100755 index 73fe881..0000000 --- a/test/cypress/integration/alwaysPrintLogs.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -describe('Always print Logs.', () => { - - it('Always print Logs', () => { - cy.visit('/'); - cy.contains('cypress'); - }); -}); diff --git a/test/cypress/integration/printLogs.spec.js b/test/cypress/integration/printLogs.spec.js new file mode 100755 index 0000000..19366ae --- /dev/null +++ b/test/cypress/integration/printLogs.spec.js @@ -0,0 +1,7 @@ +describe('Print Logs.', () => { + + it('Print Logs', () => { + cy.visit('/'); + cy.contains('cypress'); + }); +}); diff --git a/test/cypress/plugins/index.js b/test/cypress/plugins/index.js index 0992b71..a6c61d5 100755 --- a/test/cypress/plugins/index.js +++ b/test/cypress/plugins/index.js @@ -29,9 +29,18 @@ module.exports = (on, config) => { shouldNotBeHere: "", }; } - - options.printLogsToFile = 'never'; - options.printLogsToConsole = 'always'; + if (config.env.printLogsToConsoleAlways == '1') { + options.printLogsToConsole = 'always'; + } + if (config.env.printLogsToConsoleNever == '1') { + options.printLogsToConsole = 'never'; + } + if (config.env.printLogsToFileAlways == '1') { + options.printLogsToFile = 'always'; + } + if (config.env.printLogsToFileNever == '1') { + options.printLogsToFile = 'never'; + } require('../../../src/installLogsPrinter')(on, options); }; diff --git a/test/cypress/support/index.js b/test/cypress/support/index.js index 863e4e1..5f4fd93 100755 --- a/test/cypress/support/index.js +++ b/test/cypress/support/index.js @@ -9,9 +9,6 @@ if (env.setLogTypes == '1') { if (env.setFilterLogs == '1') { config.filterLog = ([,log]) => log.indexOf('[filter-out-string]') !== -1; } -if (env.printLogsAlways == '1') { - config.printLogs = 'always'; -} if (env.printHeaderData == '1') { config.xhr = config.xhr || {}; config.xhr.printHeaderData = true; @@ -27,7 +24,6 @@ if (env.supportBadConfig == '1') { config = { collectTypes: 0, filterLog: "string", - printLogs: false, xhr: { printRequestData: "", printHeaderData: "", diff --git a/test/package.json b/test/package.json index cdad358..422a0cf 100755 --- a/test/package.json +++ b/test/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo 'mocha ./test.js' && npx cypress run", + "test": "mocha ./test.js", "wsl-open-cypress": "cmd.exe /C npx cypress open" }, "author": "Fodor Zoltan", diff --git a/test/test.js b/test/test.js index 0b5ae42..a204e43 100755 --- a/test/test.js +++ b/test/test.js @@ -157,14 +157,22 @@ describe('cypress-terminal-report', () => { }); }).timeout(60000); - it('Should always print logs when configuration enabled.', async () => { - await runTest(commandBase(['printLogsAlways=1'], ['alwaysPrintLogs.spec.js']), (error, stdout, stderr) => { + it('Should always print logs to console when configured so.', async () => { + await runTest(commandBase(['printLogsToConsoleAlways=1'], ['printLogs.spec.js']), (error, stdout, stderr) => { // cy.command logs. expect(stdout).to.contain(`cy:command ${ICONS.success} visit\t/\n`); expect(stdout).to.contain(`cy:command ${ICONS.success} contains\tcypress\n`); }); }).timeout(60000); + it('Should never print logs to console when configured so.', async () => { + await runTest(commandBase(['printLogsToConsoleNever=1'], ['printLogs.spec.js']), (error, stdout, stderr) => { + // cy.command logs. + expect(stdout).to.not.contain(`cy:command ${ICONS.success} visit\t/\n`); + expect(stdout).to.not.contain(`cy:command ${ICONS.success} contains\tcypress\n`); + }); + }).timeout(60000); + it('Should print only logs allowed if configuration added.', async () => { await runTest(commandBase(['setLogTypes=1'], ['allTypesOfLogs.spec.js']), (error, stdout, stderr) => { expect(stdout).to.contain(`cy:request`); @@ -250,6 +258,27 @@ describe('cypress-terminal-report', () => { }); }).timeout(60000); + it('Should not generate/print to output files.', async () => { + const outRoot = path.join(__dirname, 'output'); + const testOutputs = ['out.txt', 'out.json', 'out.cst']; + testOutputs.forEach((out) => { + if (fs.existsSync(path.join(outRoot, out))) { + fs.unlinkSync(path.join(outRoot, out)); + } + }); + + const specFiles = ['requests.spec.js', 'happyFlow.spec.js']; + await runTest(commandBase(['generateOutput=1', 'printLogsToFileNever=1'], specFiles), (error, stdout, stderr) => { + testOutputs.forEach((out) => { + expect(fs.existsSync(path.join(outRoot, out))).false; + }); + + expect(stdout).to.not.contain('[cypress-terminal-report] Wrote txt logs to ' + path.join(outRoot, 'out.txt')); + expect(stdout).to.not.contain('[cypress-terminal-report] Wrote json logs to ' + path.join(outRoot, 'out.json')); + expect(stdout).to.not.contain('[cypress-terminal-report] Wrote custom logs to ' + path.join(outRoot, 'out.cst')); + }); + }).timeout(60000); + it('Should not break normal execution.', async () => { await runTest(commandBase([], ['successful.spec.js']), (error, stdout, stderr) => { expect(stdout).to.not.contain(`error`); @@ -268,7 +297,7 @@ describe('cypress-terminal-report', () => { }).timeout(60000); it('Should compact all logs when there is no failing test.', async () => { - await runTest(commandBase(['compactLogs=1', 'printLogsAlways=1'], ['successfulWithNoErrors.spec.js']), (error, stdout, stderr) => { + await runTest(commandBase(['compactLogs=1', 'printLogsToConsoleAlways=1'], ['successfulWithNoErrors.spec.js']), (error, stdout, stderr) => { expect(stdout).to.contain(`ctr:info - [ ... 28 omitted logs ... ]`); }); }).timeout(60000); @@ -288,7 +317,6 @@ describe('cypress-terminal-report', () => { expect(stdout).to.contain(`[cypress-terminal-report] Invalid plugin install options:`); expect(stdout).to.contain(`=> .collectTypes: Invalid type: number (expected array)`); expect(stdout).to.contain(`=> .filterLog: Invalid type: string (expected function)`); - expect(stdout).to.contain(`=> .printLogs: Invalid type: boolean (expected string)`); expect(stdout).to.contain(`=> .xhr/printRequestData: Invalid type: string (expected boolean)`); expect(stdout).to.contain(`=> .xhr/printHeaderData: Invalid type: string (expected boolean)`); expect(stdout).to.contain(`=> .xhr/shouldNotBeHere: Additional properties not allowed`); From d27da939b8994560bd66cb238ce4543e70434b87 Mon Sep 17 00:00:00 2001 From: Fodor Levente Date: Sat, 26 Sep 2020 22:56:25 +0300 Subject: [PATCH 4/9] Added warning message about printLogs configuration being unsupported. --- README.md | 7 +++---- src/installLogsCollector.js | 10 ++++++++-- src/installLogsCollector.schema.json | 4 ++++ src/installLogsPrinter.js | 2 ++ test/package.json | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ff1fd2e..760fbe3 100755 --- a/README.md +++ b/README.md @@ -83,10 +83,9 @@ logs will be printed to file(s) for successful test as well as failing ones. > require('cypress-terminal-report/src/installLogsCollector')(options); -#### `options.printLogs` -~~string; default: 'onFail'; possible values: 'onFail', 'always' - When set to always -logs will be printed for successful test as well as failing ones.~~ -Deprecated. +#### `options.printLogs` **`NOTE: Deprecated`** +string; default: 'onFail'; possible values: 'onFail', 'always' - When set to always +logs will be printed for successful test as well as failing ones. #### `options.collectTypes` array; default: ['cons:log','cons:info', 'cons:warn', 'cons:error', 'cy:log', 'cy:xhr', 'cy:request', 'cy:route', 'cy:command'] diff --git a/src/installLogsCollector.js b/src/installLogsCollector.js index e9eff62..f1edc2e 100755 --- a/src/installLogsCollector.js +++ b/src/installLogsCollector.js @@ -15,8 +15,7 @@ const tv4ErrorTransformer = require('./tv4ErrorTransformer'); * * @param {object} config * Options for collection logs: - * - printLogsToConsole?: string; Default: 'onFail'. When to print logs to console, possible values: 'always', 'onFail', 'never'. - * - printLogsToFile?: string; Default: 'onFail'. When to print logs to file(s), possible values: 'always', 'onFail', 'never'. + * - (deprecated) printLogs?: string; Default: 'onFail'. When to print logs, possible values: 'always', 'onFail'. * - collectTypes?: array; Collect only these types of logs. Defaults to all types. * - filterLog?: ([type, message, severity]) => boolean; Callback to filter logs manually. * - xhr?: @@ -109,6 +108,13 @@ function installLogsCollector(config = {}) { } function validateConfig(config) { + before(() => { + if (typeof config.printLogs === 'string'){ + cy.log("printLogs configuration is no longer supported, consider using the new options within plugins: " + + "printLogsToConsole and printLogsToFile"); + } + }); + const result = tv4.validateMultiple(config, schema); if (!result.valid) { diff --git a/src/installLogsCollector.schema.json b/src/installLogsCollector.schema.json index 8faef5c..67432bf 100644 --- a/src/installLogsCollector.schema.json +++ b/src/installLogsCollector.schema.json @@ -1,6 +1,10 @@ { "type": "object", "properties": { + "printLogs": { + "type": "string", + "enum": ["onFail", "always"] + }, "collectTypes": { "type": "array", "items": { diff --git a/src/installLogsPrinter.js b/src/installLogsPrinter.js index 50b12d9..ccb0616 100755 --- a/src/installLogsPrinter.js +++ b/src/installLogsPrinter.js @@ -47,6 +47,8 @@ let outputProcessors = []; * Cypress event listen handler. * @param {object} options * Options for displaying output: + * - printLogsToConsole?: string; Default: 'onFail'. When to print logs to console, possible values: 'always', 'onFail', 'never'. + * - printLogsToFile?: string; Default: 'onFail'. When to print logs to file(s), possible values: 'always', 'onFail', 'never'. * - defaultTrimLength?: Trim length for console and cy.log. * - commandTrimLength?: Trim length for cy commands. * - outputRoot?: The root path to output log files to. diff --git a/test/package.json b/test/package.json index 422a0cf..ded69f2 100755 --- a/test/package.json +++ b/test/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "mocha ./test.js", + "test": "npx cypress open && mocha ./test.js", "wsl-open-cypress": "cmd.exe /C npx cypress open" }, "author": "Fodor Zoltan", From 032f68196f37f31961acc1c600d310e1891a8acc Mon Sep 17 00:00:00 2001 From: Fodor Levente Date: Sat, 26 Sep 2020 23:45:05 +0300 Subject: [PATCH 5/9] Misc modifications and cleanup. --- src/installLogsCollector.js | 2 +- test/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/installLogsCollector.js b/src/installLogsCollector.js index f1edc2e..b94f386 100755 --- a/src/installLogsCollector.js +++ b/src/installLogsCollector.js @@ -108,7 +108,7 @@ function installLogsCollector(config = {}) { } function validateConfig(config) { - before(() => { + before(function () { if (typeof config.printLogs === 'string'){ cy.log("printLogs configuration is no longer supported, consider using the new options within plugins: " + "printLogsToConsole and printLogsToFile"); diff --git a/test/package.json b/test/package.json index ded69f2..422a0cf 100755 --- a/test/package.json +++ b/test/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "npx cypress open && mocha ./test.js", + "test": "mocha ./test.js", "wsl-open-cypress": "cmd.exe /C npx cypress open" }, "author": "Fodor Zoltan", From 9f820c256ab52220128fa25afc9b782e40062628 Mon Sep 17 00:00:00 2001 From: Fodor Levente <55688616+FLevent29@users.noreply.github.com> Date: Sat, 26 Sep 2020 23:52:46 +0300 Subject: [PATCH 6/9] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 760fbe3..41587d8 100755 --- a/README.md +++ b/README.md @@ -73,17 +73,17 @@ object; default: null; Output logs to files. [More details](#logging-to-files). #### `options.printLogsToConsole` string; Default: 'onFail'. When to print logs to console, possible values: 'always', 'onFail', 'never' - When set to always -logs will be printed to console for successful test as well as failing ones. +logs will be printed to console for successful tests as well as failing ones. #### `options.printLogsToFile` string; Default: 'onFail'. When to print logs to file(s), possible values: 'always', 'onFail', 'never' - When set to always -logs will be printed to file(s) for successful test as well as failing ones. +logs will be printed to file(s) for successful tests as well as failing ones. ### Options for the support install > require('cypress-terminal-report/src/installLogsCollector')(options); -#### `options.printLogs` **`NOTE: Deprecated`** +#### `options.printLogs` **`NOTE: Obsolete, use printLogsToConsole instead`** string; default: 'onFail'; possible values: 'onFail', 'always' - When set to always logs will be printed for successful test as well as failing ones. From 83294d6868d91ecb50fd7eca1db9cfd776fd9f7b Mon Sep 17 00:00:00 2001 From: Fodor Levente Date: Tue, 29 Sep 2020 20:07:05 +0300 Subject: [PATCH 7/9] Fixes according to pull request review. --- README.md | 4 -- src/installLogsCollector.js | 8 ++-- src/installLogsPrinter.js | 14 +++---- test/cypress/integration/printLogs.spec.js | 7 ---- .../cypress/integration/printLogsFail.spec.js | 7 ++++ .../integration/printLogsSuccess.spec.js | 7 ++++ test/cypress/plugins/index.js | 4 +- test/output/out.spec.cst | 3 +- test/output/out.spec.json | 16 +++++++- test/output/out.spec.txt | 11 ++++-- test/test.js | 38 ++++++++++--------- 11 files changed, 71 insertions(+), 48 deletions(-) delete mode 100755 test/cypress/integration/printLogs.spec.js create mode 100644 test/cypress/integration/printLogsFail.spec.js create mode 100755 test/cypress/integration/printLogsSuccess.spec.js diff --git a/README.md b/README.md index 41587d8..c2ecfc6 100755 --- a/README.md +++ b/README.md @@ -83,10 +83,6 @@ logs will be printed to file(s) for successful tests as well as failing ones. > require('cypress-terminal-report/src/installLogsCollector')(options); -#### `options.printLogs` **`NOTE: Obsolete, use printLogsToConsole instead`** -string; default: 'onFail'; possible values: 'onFail', 'always' - When set to always -logs will be printed for successful test as well as failing ones. - #### `options.collectTypes` array; default: ['cons:log','cons:info', 'cons:warn', 'cons:error', 'cy:log', 'cy:xhr', 'cy:request', 'cy:route', 'cy:command'] What types of logs to collect and print. By default all types are enabled. The 'cy:command' is the general type that diff --git a/src/installLogsCollector.js b/src/installLogsCollector.js index b94f386..d6fc935 100755 --- a/src/installLogsCollector.js +++ b/src/installLogsCollector.js @@ -15,7 +15,6 @@ const tv4ErrorTransformer = require('./tv4ErrorTransformer'); * * @param {object} config * Options for collection logs: - * - (deprecated) printLogs?: string; Default: 'onFail'. When to print logs, possible values: 'always', 'onFail'. * - collectTypes?: array; Collect only these types of logs. Defaults to all types. * - filterLog?: ([type, message, severity]) => boolean; Callback to filter logs manually. * - xhr?: @@ -89,7 +88,6 @@ function installLogsCollector(config = {}) { logs = []; }); - //if (this.currentTest.state !== 'passed' || (config && config.printLogs === 'always')) afterEach(function() { // Need to wait otherwise some last commands get omitted from logs. cy.wait(3, {log: false}); @@ -103,15 +101,15 @@ function installLogsCollector(config = {}) { after(function () { // Need to wait otherwise some last commands get omitted from logs. - cy.task(CONSTANTS.TASK_NAME_OUTPUT, null, {log: false}); + cy.task(CONSTANTS.TASK_NAME_OUTPUT, {state: this.currentTest.state}, {log: false}); }); } function validateConfig(config) { before(function () { if (typeof config.printLogs === 'string'){ - cy.log("printLogs configuration is no longer supported, consider using the new options within plugins: " + - "printLogsToConsole and printLogsToFile"); + cy.log("cypress-terminal-report: WARN! printLogs " + + "configuration has been removed. Please check changelog in readme."); } }); diff --git a/src/installLogsPrinter.js b/src/installLogsPrinter.js index ccb0616..00710ab 100755 --- a/src/installLogsPrinter.js +++ b/src/installLogsPrinter.js @@ -65,7 +65,6 @@ function installLogsPrinter(on, options = {}) { on('task', { [CONSTANTS.TASK_NAME]: data => { let messages = data.messages; - state = data.state; if (typeof options.compactLogs === 'number' && options.compactLogs >= 0) { messages = compactLogs(messages, options.compactLogs); @@ -76,25 +75,22 @@ function installLogsPrinter(on, options = {}) { allMessages[data.spec][data.test] = messages; } - if (options.printLogsToConsole === "always" || - ((options.printLogsToConsole === "onFail" || typeof options.printLogsToConsole === 'undefined') - && state !== "passed")){ + let printL2C = options.printLogsToConsole || "onFail"; + if ((printL2C === "onFail" && data.state !== "passed") || printL2C === "always"){ logToTerminal(messages, options); } return null; }, - [CONSTANTS.TASK_NAME_OUTPUT]: () => { + [CONSTANTS.TASK_NAME_OUTPUT]: data => { outputProcessors.forEach((processor) => { - if (options.printLogsToFile === "always" || - ((options.printLogsToFile === "onFail" || typeof options.printLogsToFile === 'undefined') - && state !== "passed")){ + let printL2F = options.printLogsToFile || "onFail"; + if ((printL2F === "onFail" && data.state !== "passed") || printL2F === "always"){ processor.write(allMessages); logOutputTarget(processor); } }); allMessages = {}; - state = null; return null; } }); diff --git a/test/cypress/integration/printLogs.spec.js b/test/cypress/integration/printLogs.spec.js deleted file mode 100755 index 19366ae..0000000 --- a/test/cypress/integration/printLogs.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -describe('Print Logs.', () => { - - it('Print Logs', () => { - cy.visit('/'); - cy.contains('cypress'); - }); -}); diff --git a/test/cypress/integration/printLogsFail.spec.js b/test/cypress/integration/printLogsFail.spec.js new file mode 100644 index 0000000..3db42da --- /dev/null +++ b/test/cypress/integration/printLogsFail.spec.js @@ -0,0 +1,7 @@ +describe('Print Logs Fail.', () => { + + it('Print Logs Fail', () => { + cy.visit('/'); + cy.contains('sserpyc'); + }); +}); diff --git a/test/cypress/integration/printLogsSuccess.spec.js b/test/cypress/integration/printLogsSuccess.spec.js new file mode 100755 index 0000000..76a89fb --- /dev/null +++ b/test/cypress/integration/printLogsSuccess.spec.js @@ -0,0 +1,7 @@ +describe('Print Logs Success.', () => { + + it('Print Logs Success', () => { + cy.visit('/'); + cy.contains('cypress'); + }); +}); diff --git a/test/cypress/plugins/index.js b/test/cypress/plugins/index.js index a6c61d5..cadd666 100755 --- a/test/cypress/plugins/index.js +++ b/test/cypress/plugins/index.js @@ -8,7 +8,7 @@ module.exports = (on, config) => { 'out.txt': 'txt', 'out.json': 'json', 'out.cst': function (messages) { - this.initialContent = 'Failing specs:\n'; + this.initialContent = 'Specs:\n'; this.chunkSeparator = '\n'; Object.keys(messages).forEach((key) => { this.writeSpecChunk(key, key); @@ -26,6 +26,8 @@ module.exports = (on, config) => { any: 100 }, compactLogs: false, + printLogsToConsole: false, + printLogsToFile: true, shouldNotBeHere: "", }; } diff --git a/test/output/out.spec.cst b/test/output/out.spec.cst index 657d2b6..5458ec7 100644 --- a/test/output/out.spec.cst +++ b/test/output/out.spec.cst @@ -1,3 +1,4 @@ -Failing specs: +Specs: cypress/integration/happyFlow.spec.js +cypress/integration/printLogsSuccess.spec.js cypress/integration/requests.spec.js diff --git a/test/output/out.spec.json b/test/output/out.spec.json index 7d89c8c..77691e9 100644 --- a/test/output/out.spec.json +++ b/test/output/out.spec.json @@ -134,7 +134,7 @@ { "type": "cons:error", "severity": "error", - "message": "Error: This is an error message with stack.\n at Context. (https://example.cypress.io/__cypress/tests?p=cypress/integration/happyFlow.spec.js-034:47:30)" + "message": "Error: This is an error message with stack.\n at Context.eval (https://example.cypress.io/__cypress/tests?p=cypress/integration/happyFlow.spec.js:142:30)" }, { "type": "cy:command", @@ -208,6 +208,20 @@ } ] }, + "cypress/integration/printLogsSuccess.spec.js": { + "Print Logs Success": [ + { + "type": "cy:command", + "severity": "success", + "message": "visit\t/" + }, + { + "type": "cy:command", + "severity": "success", + "message": "contains\tcypress" + } + ] + }, "cypress/integration/requests.spec.js": { "GET 200": [ { diff --git a/test/output/out.spec.txt b/test/output/out.spec.txt index 504c11c..c03fa84 100644 --- a/test/output/out.spec.txt +++ b/test/output/out.spec.txt @@ -53,7 +53,7 @@ cypress/integration/happyFlow.spec.js: cons:error (X): This is an error message cy:command (K): window cons:error (X): Error: This is an error message with stack. - at Context. (https://example.cypress.io/__cypress/tests?p=cypress/integration/happyFlow.spec.js-034:47:30) + at Context.eval (https://example.cypress.io/__cypress/tests?p=cypress/integration/happyFlow.spec.js:142:30) cy:command (K): window cons:log (K): This should console.log appear. cy:command (K): window @@ -80,6 +80,13 @@ cypress/integration/happyFlow.spec.js: +cypress/integration/printLogsSuccess.spec.js: + Print Logs Success + cy:command (K): visit / + cy:command (K): contains cypress + + + cypress/integration/requests.spec.js: GET 200 cy:request (K): https://jsonplaceholder.cypress.io/todos/1 @@ -137,5 +144,3 @@ cypress/integration/requests.spec.js: Status: 404 Response body: {} - - diff --git a/test/test.js b/test/test.js index a204e43..7632ffe 100755 --- a/test/test.js +++ b/test/test.js @@ -49,6 +49,14 @@ const runTest = async (command, callback) => { }); }; +function outPutCleanUp(testOutputs, outRoot) { + testOutputs.forEach((out) => { + if (fs.existsSync(path.join(outRoot, out))) { + fs.unlinkSync(path.join(outRoot, out)); + } + }); +} + describe('cypress-terminal-report', () => { afterEach(function () { @@ -158,18 +166,20 @@ describe('cypress-terminal-report', () => { }).timeout(60000); it('Should always print logs to console when configured so.', async () => { - await runTest(commandBase(['printLogsToConsoleAlways=1'], ['printLogs.spec.js']), (error, stdout, stderr) => { + await runTest(commandBase(['printLogsToConsoleAlways=1'], ['printLogsSuccess.spec.js', 'printLogsFail.spec.js']), (error, stdout, stderr) => { // cy.command logs. expect(stdout).to.contain(`cy:command ${ICONS.success} visit\t/\n`); expect(stdout).to.contain(`cy:command ${ICONS.success} contains\tcypress\n`); + expect(stdout).to.contain(`cy:command ${ICONS.error} contains\tsserpyc\n`); }); }).timeout(60000); it('Should never print logs to console when configured so.', async () => { - await runTest(commandBase(['printLogsToConsoleNever=1'], ['printLogs.spec.js']), (error, stdout, stderr) => { + await runTest(commandBase(['printLogsToConsoleNever=1'], ['printLogsSuccess.spec.js', 'printLogsFail.spec.js']), (error, stdout, stderr) => { // cy.command logs. expect(stdout).to.not.contain(`cy:command ${ICONS.success} visit\t/\n`); expect(stdout).to.not.contain(`cy:command ${ICONS.success} contains\tcypress\n`); + expect(stdout).to.not.contain(`cy:command ${ICONS.error} contains\tsserpyc\n`); }); }).timeout(60000); @@ -201,14 +211,10 @@ describe('cypress-terminal-report', () => { }); }).timeout(60000); - it('Should generate proper log output files.', async () => { + it('Should generate proper log output files, and print everything if configured so.', async () => { const outRoot = path.join(__dirname, 'output'); const testOutputs = ['out.txt', 'out.json', 'out.cst']; - testOutputs.forEach((out) => { - if (fs.existsSync(path.join(outRoot, out))) { - fs.unlinkSync(path.join(outRoot, out)); - } - }); + outPutCleanUp(testOutputs, outRoot); if (fs.existsSync(path.join(outRoot, 'not'))) { fs.rmdirSync(path.join(outRoot, 'not'), { recursive: true }); @@ -222,8 +228,8 @@ describe('cypress-terminal-report', () => { // Change line endings to win32 if needed (os.EOL === '\r\n' ? str.replace(/\n/g, '\r\n') : str); - const specFiles = ['requests.spec.js', 'happyFlow.spec.js']; - await runTest(commandBase(['generateOutput=1'], specFiles), (error, stdout, stderr) => { + const specFiles = ['requests.spec.js', 'happyFlow.spec.js', 'printLogsSuccess.spec.js']; + await runTest(commandBase(['generateOutput=1', 'printLogsToFileAlways=1'], specFiles), (error, stdout, stderr) => { testOutputs.forEach((out) => { const expectedBuffer = fs.readFileSync( path.join(outRoot, out.replace(/\.([a-z]+)$/, '.spec.$1')) @@ -258,16 +264,12 @@ describe('cypress-terminal-report', () => { }); }).timeout(60000); - it('Should not generate/print to output files.', async () => { + it('Should not generate/print to output files when configured so.', async () => { const outRoot = path.join(__dirname, 'output'); const testOutputs = ['out.txt', 'out.json', 'out.cst']; - testOutputs.forEach((out) => { - if (fs.existsSync(path.join(outRoot, out))) { - fs.unlinkSync(path.join(outRoot, out)); - } - }); + outPutCleanUp(testOutputs, outRoot); - const specFiles = ['requests.spec.js', 'happyFlow.spec.js']; + const specFiles = ['requests.spec.js', 'happyFlow.spec.js', 'printLogsSuccess.spec.js']; await runTest(commandBase(['generateOutput=1', 'printLogsToFileNever=1'], specFiles), (error, stdout, stderr) => { testOutputs.forEach((out) => { expect(fs.existsSync(path.join(outRoot, out))).false; @@ -309,6 +311,8 @@ describe('cypress-terminal-report', () => { expect(stdout).to.contain(`=> .outputTarget/any: Invalid type: number (expected string/function)`); expect(stdout).to.contain(`=> .compactLogs: Invalid type: boolean (expected number)`); expect(stdout).to.contain(`=> .shouldNotBeHere: Additional properties not allowed`); + expect(stdout).to.contain(`=> .printLogsToFile: Invalid type: boolean (expected string)`); + expect(stdout).to.contain(`=> .printLogsToConsole: Invalid type: boolean (expected string)`); }); }).timeout(60000); From 5b7db1dc94874d6f8cd47475dbfb58efa81a6eb6 Mon Sep 17 00:00:00 2001 From: Fodor Levente Date: Thu, 1 Oct 2020 23:46:36 +0300 Subject: [PATCH 8/9] Added new test cases, fixed bad code and added some more white space trimming according to pull request review. --- src/installLogsCollector.js | 52 ++-- src/installLogsPrinter.js | 14 +- src/outputProcessor/CustomOutputProcessor.js | 4 +- src/outputProcessor/JsonOutputProcessor.js | 1 + src/outputProcessor/TextOutputProcessor.js | 5 +- test/cypress/plugins/index.js | 2 +- .../{out.spec.cst => out.spec.always.cst} | 2 +- .../{out.spec.json => out.spec.always.json} | 2 +- .../{out.spec.txt => out.spec.always.txt} | 23 +- test/output/out.spec.onFail.cst | 3 + test/output/out.spec.onFail.json | 268 ++++++++++++++++++ test/output/out.spec.onFail.txt | 132 +++++++++ test/test.js | 143 ++++++---- 13 files changed, 544 insertions(+), 107 deletions(-) rename test/output/{out.spec.cst => out.spec.always.cst} (70%) rename test/output/{out.spec.json => out.spec.always.json} (99%) rename test/output/{out.spec.txt => out.spec.always.txt} (94%) create mode 100644 test/output/out.spec.onFail.cst create mode 100644 test/output/out.spec.onFail.json create mode 100644 test/output/out.spec.onFail.txt diff --git a/src/installLogsCollector.js b/src/installLogsCollector.js index d6fc935..5dba191 100755 --- a/src/installLogsCollector.js +++ b/src/installLogsCollector.js @@ -78,7 +78,7 @@ function installLogsCollector(config = {}) { } Cypress.on('log:changed', options => { - if ( options.state === 'failed' && logsChainId[options.id] && logs[logsChainId[options.id]]) { + if (options.state === 'failed' && logsChainId[options.id] && logs[logsChainId[options.id]]) { logs[logsChainId[options.id]][2] = CONSTANTS.SEVERITY.ERROR; } }); @@ -88,7 +88,7 @@ function installLogsCollector(config = {}) { logs = []; }); - afterEach(function() { + afterEach(function () { // Need to wait otherwise some last commands get omitted from logs. cy.wait(3, {log: false}); cy.task(CONSTANTS.TASK_NAME, { @@ -101,15 +101,15 @@ function installLogsCollector(config = {}) { after(function () { // Need to wait otherwise some last commands get omitted from logs. - cy.task(CONSTANTS.TASK_NAME_OUTPUT, {state: this.currentTest.state}, {log: false}); + cy.task(CONSTANTS.TASK_NAME_OUTPUT, null, {log: false}); }); } function validateConfig(config) { before(function () { - if (typeof config.printLogs === 'string'){ + if (typeof config.printLogs === 'string') { cy.log("cypress-terminal-report: WARN! printLogs " + - "configuration has been removed. Please check changelog in readme."); + "configuration has been removed. Please check changelog in readme."); } }); @@ -193,7 +193,7 @@ function collectCypressXhrLog(addLog) { Cypress.on('log:added', options => { if (options.instrument === 'command' && options.consoleProps && options.name === 'xhr') { let detailMessage = (options.consoleProps.Stubbed === 'Yes' ? 'STUBBED ' : '') + - options.consoleProps.Method + ' ' + options.consoleProps.URL; + options.consoleProps.Method + ' ' + options.consoleProps.URL; const log = options.message + detailMessage; const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.WARNING : ''; @@ -204,7 +204,7 @@ function collectCypressXhrLog(addLog) { function collectCypressRequestCommand(addLog, formatXhrLog) { const isValidHttpMethod = (str) => - typeof str === 'string' && methods.some(s => str.toLowerCase().includes(s)); + typeof str === 'string' && methods.some(s => str.toLowerCase().includes(s)); Cypress.Commands.overwrite('request', async (originalFn, ...args) => { let log; @@ -231,11 +231,11 @@ function collectCypressRequestCommand(addLog, formatXhrLog) { let status = xhr ? xhr.status : {}; log += `\n` + formatXhrLog( - status, - await xhrPartParse(requestHeaders), - await xhrPartParse(requestBody), - await xhrPartParse(headers), - await xhrPartParse(body), + status, + await xhrPartParse(requestHeaders), + await xhrPartParse(requestBody), + await xhrPartParse(headers), + await xhrPartParse(body), ); addLog([LOG_TYPE.CYPRESS_REQUEST, log, CONSTANTS.SEVERITY.ERROR]); @@ -243,11 +243,11 @@ function collectCypressRequestCommand(addLog, formatXhrLog) { }); log += `\n` + formatXhrLog( - response.status, - await xhrPartParse(requestHeaders), - await xhrPartParse(requestBody), - await xhrPartParse(response.headers), - await xhrPartParse(response.body), + response.status, + await xhrPartParse(requestHeaders), + await xhrPartParse(requestBody), + await xhrPartParse(response.headers), + await xhrPartParse(response.body), ); addLog([LOG_TYPE.CYPRESS_REQUEST, log]); @@ -270,11 +270,11 @@ function collectCypressRouteCommand(addLog, formatXhrLog) { const severity = String(xhr.status).match(/^2[0-9]+$/) ? '' : CONSTANTS.SEVERITY.WARNING; let logMessage = `(${route.alias}) ${xhr.method} ${xhr.url}\n`; logMessage += formatXhrLog( - xhr.status, - await xhrPartParse(xhr.request.headers), - await xhrPartParse(xhr.request.body), - await xhrPartParse(xhr.response.headers), - await xhrPartParse(xhr.response.body), + xhr.status, + await xhrPartParse(xhr.request.headers), + await xhrPartParse(xhr.request.body), + await xhrPartParse(xhr.response.headers), + await xhrPartParse(xhr.response.body), ); addLog([ @@ -290,10 +290,10 @@ function collectCypressRouteCommand(addLog, formatXhrLog) { function collectCypressGeneralCommandLog(addLog) { Cypress.on('log:added', options => { if ( - options.instrument === 'command' && - options.consoleProps && - !(['xhr', 'log', 'request'].includes(options.name)) && - !(options.name === 'task' && options.message.match(/ctrLogMessages/)) + options.instrument === 'command' && + options.consoleProps && + !(['xhr', 'log', 'request'].includes(options.name)) && + !(options.name === 'task' && options.message.match(/ctrLogMessages/)) ) { const log = options.name + '\t' + options.message; const severity = options.state === 'failed' ? CONSTANTS.SEVERITY.ERROR : ''; diff --git a/src/installLogsPrinter.js b/src/installLogsPrinter.js index 00710ab..a5d9a2b 100755 --- a/src/installLogsPrinter.js +++ b/src/installLogsPrinter.js @@ -35,7 +35,6 @@ const LOG_SYMBOLS = (() => { })(); let allMessages = {}; -let state = null; let outputProcessors = []; /** @@ -56,6 +55,8 @@ let outputProcessors = []; * - compactLogs?: Number of lines to compact around failing commands. */ function installLogsPrinter(on, options = {}) { + options.printLogsToFile = options.printLogsToFile || "onFail"; + options.printLogsToConsole = options.printLogsToConsole || "onFail"; const result = tv4.validateMultiple(options, schema); if (!result.valid) { @@ -71,21 +72,22 @@ function installLogsPrinter(on, options = {}) { } if (options.outputTarget) { + allMessages[0] = data.state; allMessages[data.spec] = allMessages[data.spec] || {}; allMessages[data.spec][data.test] = messages; } - let printL2C = options.printLogsToConsole || "onFail"; - if ((printL2C === "onFail" && data.state !== "passed") || printL2C === "always"){ + if ((options.printLogsToConsole === "onFail" && data.state !== "passed") + || options.printLogsToConsole === "always"){ logToTerminal(messages, options); } return null; }, - [CONSTANTS.TASK_NAME_OUTPUT]: data => { + [CONSTANTS.TASK_NAME_OUTPUT]: () => { outputProcessors.forEach((processor) => { - let printL2F = options.printLogsToFile || "onFail"; - if ((printL2F === "onFail" && data.state !== "passed") || printL2F === "always"){ + if ((options.printLogsToFile === "onFail" && allMessages[0] !== "passed") + || options.printLogsToFile === "always"){ processor.write(allMessages); logOutputTarget(processor); } diff --git a/src/outputProcessor/CustomOutputProcessor.js b/src/outputProcessor/CustomOutputProcessor.js index ae9615a..ff3ab61 100644 --- a/src/outputProcessor/CustomOutputProcessor.js +++ b/src/outputProcessor/CustomOutputProcessor.js @@ -8,7 +8,9 @@ module.exports = class CustomOutputProcessor extends BaseOutputProcessor { } write(allMessages) { - this.processorCallback.call(this, allMessages); + let allMessagesWithoutState = Object.assign({}, allMessages); + delete allMessagesWithoutState[0]; + this.processorCallback.call(this, allMessagesWithoutState); } }; diff --git a/src/outputProcessor/JsonOutputProcessor.js b/src/outputProcessor/JsonOutputProcessor.js index ef34ed9..6717a12 100644 --- a/src/outputProcessor/JsonOutputProcessor.js +++ b/src/outputProcessor/JsonOutputProcessor.js @@ -11,6 +11,7 @@ module.exports = class JsonOutputProcessor extends BaseOutputProcessor { write(allMessages) { Object.entries(allMessages).forEach(([spec, tests]) => { + if (spec === "0") return; let data = {[spec]: {}}; Object.entries(tests).forEach(([test, messages]) => { data[spec][test] = messages.map(([type, message, severity]) => ({ diff --git a/src/outputProcessor/TextOutputProcessor.js b/src/outputProcessor/TextOutputProcessor.js index 5cd4aea..79b5cfd 100644 --- a/src/outputProcessor/TextOutputProcessor.js +++ b/src/outputProcessor/TextOutputProcessor.js @@ -28,12 +28,13 @@ module.exports = class TextOutputProcessor extends BaseOutputProcessor { write(allMessages) { Object.entries(allMessages).forEach(([spec, tests]) => { + if (spec === "0") return; let text = `${spec}:${EOL}`; Object.entries(tests).forEach(([test, messages]) => { text += `${PADDING}${test}${EOL}`; messages.forEach(([type, message, severity]) => { - text += this.padTypeText(`${type} (${this.severityToFont(severity)}): `) + - message.replace(/\n/g, `${EOL}${PADDING_LOGS}`) + EOL; + text += (this.padTypeText(`${type} (${this.severityToFont(severity)}): `) + + message.replace(/\n/g, `${EOL}${PADDING_LOGS}`) + EOL).replace(/\s+\n/, '\n'); }); text += EOL; }); diff --git a/test/cypress/plugins/index.js b/test/cypress/plugins/index.js index cadd666..87b8187 100755 --- a/test/cypress/plugins/index.js +++ b/test/cypress/plugins/index.js @@ -26,7 +26,7 @@ module.exports = (on, config) => { any: 100 }, compactLogs: false, - printLogsToConsole: false, + printLogsToConsole: true, printLogsToFile: true, shouldNotBeHere: "", }; diff --git a/test/output/out.spec.cst b/test/output/out.spec.always.cst similarity index 70% rename from test/output/out.spec.cst rename to test/output/out.spec.always.cst index 5458ec7..c29431e 100644 --- a/test/output/out.spec.cst +++ b/test/output/out.spec.always.cst @@ -1,4 +1,4 @@ Specs: cypress/integration/happyFlow.spec.js cypress/integration/printLogsSuccess.spec.js -cypress/integration/requests.spec.js +cypress/integration/requests.spec.js \ No newline at end of file diff --git a/test/output/out.spec.json b/test/output/out.spec.always.json similarity index 99% rename from test/output/out.spec.json rename to test/output/out.spec.always.json index 77691e9..a3a919f 100644 --- a/test/output/out.spec.json +++ b/test/output/out.spec.always.json @@ -279,4 +279,4 @@ } ] } -} +} \ No newline at end of file diff --git a/test/output/out.spec.txt b/test/output/out.spec.always.txt similarity index 94% rename from test/output/out.spec.txt rename to test/output/out.spec.always.txt index c03fa84..6e53382 100644 --- a/test/output/out.spec.txt +++ b/test/output/out.spec.always.txt @@ -2,7 +2,7 @@ cypress/integration/happyFlow.spec.js: Happy flow cy:command (K): visit /commands/network-requests cy:command (K): get .network-btn - cy:command (K): click + cy:command (K): click cy:xhr (K): GET https://jsonplaceholder.cypress.io/comments/1 cy:command (K): wait @getComment cy:route (K): (getComment) GET https://jsonplaceholder.cypress.io/comments/1 @@ -17,7 +17,7 @@ cypress/integration/happyFlow.spec.js: cy:command (K): its .status cy:command (K): assert expected **200** to equal **200** cy:command (K): get .network-post - cy:command (K): click + cy:command (K): click cy:xhr (K): POST https://jsonplaceholder.cypress.io/comments cy:command (K): wait @postComment cy:route (K): (postComment) POST https://jsonplaceholder.cypress.io/comments @@ -32,13 +32,13 @@ cypress/integration/happyFlow.spec.js: cy:command (K): assert expected **{ Object (Content-Type, Accept) }** to have property **Content-Type** cy:command (K): assert expected **{ Object (name, email, ...) }** to have property **name** cy:command (K): assert expected **{ Object (name, email, ...) }** to have property **name** of **Using POST in cy.route()** - cy:command (K): window + cy:command (K): window cons:error (X): null, undefined, , false, function () {} - cy:command (K): window + cy:command (K): window cons:log (K): { "a": "b" }, @@ -47,26 +47,26 @@ cypress/integration/happyFlow.spec.js: }, 10, string - cy:command (K): window + cy:command (K): window cons:warn (!): This is a warning message - cy:command (K): window + cy:command (K): window cons:error (X): This is an error message - cy:command (K): window + cy:command (K): window cons:error (X): Error: This is an error message with stack. at Context.eval (https://example.cypress.io/__cypress/tests?p=cypress/integration/happyFlow.spec.js:142:30) - cy:command (K): window + cy:command (K): window cons:log (K): This should console.log appear. - cy:command (K): window + cy:command (K): window cons:log (K): { "this": "Is an object", "with": { "keys": 12512 } } - cy:command (K): window + cy:command (K): window cons:info (K): This should console.info appear. cy:command (K): get .network-put - cy:command (K): click + cy:command (K): click cy:xhr (K): STUBBED PUT https://jsonplaceholder.cypress.io/comments/1 cy:route (!): (putComment) PUT https://jsonplaceholder.cypress.io/comments/1 Status: 404 @@ -143,4 +143,3 @@ cypress/integration/requests.spec.js: cy:request (X): PUT https://jsonplaceholder.cypress.io/comments Status: 404 Response body: {} - diff --git a/test/output/out.spec.onFail.cst b/test/output/out.spec.onFail.cst new file mode 100644 index 0000000..ba2f152 --- /dev/null +++ b/test/output/out.spec.onFail.cst @@ -0,0 +1,3 @@ +Specs: +cypress/integration/happyFlow.spec.js +cypress/integration/requests.spec.js \ No newline at end of file diff --git a/test/output/out.spec.onFail.json b/test/output/out.spec.onFail.json new file mode 100644 index 0000000..fa05d5c --- /dev/null +++ b/test/output/out.spec.onFail.json @@ -0,0 +1,268 @@ +{ + "cypress/integration/happyFlow.spec.js": { + "Happy flow": [ + { + "type": "cy:command", + "severity": "success", + "message": "visit\t/commands/network-requests" + }, + { + "type": "cy:command", + "severity": "success", + "message": "get\t.network-btn" + }, + { + "type": "cy:command", + "severity": "success", + "message": "click\t" + }, + { + "type": "cy:xhr", + "severity": "success", + "message": "GET https://jsonplaceholder.cypress.io/comments/1" + }, + { + "type": "cy:command", + "severity": "success", + "message": "wait\t@getComment" + }, + { + "type": "cy:route", + "severity": "success", + "message": "(getComment) GET https://jsonplaceholder.cypress.io/comments/1\nStatus: 200\nResponse body: {\n \"postId\": 1,\n \"id\": 1,\n \"name\": \"id labore ex et quam laborum\",\n \"email\": \"Eliseo@gardner.biz\",\n \"body\": \"laudantium enim quasi est quidem magnam voluptate ipsam eos\\ntempora quo necessitatibus\\ndolor quam autem quasi\\nreiciendis et nam sapiente accusantium\"\n}" + }, + { + "type": "cy:command", + "severity": "success", + "message": "its\t.status" + }, + { + "type": "cy:command", + "severity": "success", + "message": "assert\texpected **200** to equal **200**" + }, + { + "type": "cy:command", + "severity": "success", + "message": "get\t.network-post" + }, + { + "type": "cy:command", + "severity": "success", + "message": "click\t" + }, + { + "type": "cy:xhr", + "severity": "success", + "message": "POST https://jsonplaceholder.cypress.io/comments" + }, + { + "type": "cy:command", + "severity": "success", + "message": "wait\t@postComment" + }, + { + "type": "cy:route", + "severity": "success", + "message": "(postComment) POST https://jsonplaceholder.cypress.io/comments\nStatus: 201\nResponse body: {\n \"name\": \"Using POST in cy.route()\",\n \"email\": \"hello@cypress.io\",\n \"body\": \"You can change the method used for cy.route() to be GET, POST, PUT, PATCH, or DELETE\",\n \"id\": 501\n}" + }, + { + "type": "cy:command", + "severity": "success", + "message": "assert\texpected **name=Using+POST+in+cy.route()&email=hello%40cypress.io&body=You+can+change+the+method+used+for+cy.route()+to+be+GET%2C+POST%2C+PUT%2C+PATCH%2C+or+DELETE** to include **email**" + }, + { + "type": "cy:command", + "severity": "success", + "message": "assert\texpected **{ Object (Content-Type, Accept) }** to have property **Content-Type**" + }, + { + "type": "cy:command", + "severity": "success", + "message": "assert\texpected **{ Object (name, email, ...) }** to have property **name**" + }, + { + "type": "cy:command", + "severity": "success", + "message": "assert\texpected **{ Object (name, email, ...) }** to have property **name** of **Using POST in cy.route()**" + }, + { + "type": "cy:command", + "severity": "success", + "message": "window\t" + }, + { + "type": "cons:error", + "severity": "error", + "message": "null,\nundefined,\n,\nfalse,\nfunction () {}" + }, + { + "type": "cy:command", + "severity": "success", + "message": "window\t" + }, + { + "type": "cons:log", + "severity": "success", + "message": "{\n \"a\": \"b\"\n},\n{\n \"c\": \"d\"\n},\n10,\nstring" + }, + { + "type": "cy:command", + "severity": "success", + "message": "window\t" + }, + { + "type": "cons:warn", + "severity": "warning", + "message": "This is a warning message" + }, + { + "type": "cy:command", + "severity": "success", + "message": "window\t" + }, + { + "type": "cons:error", + "severity": "error", + "message": "This is an error message" + }, + { + "type": "cy:command", + "severity": "success", + "message": "window\t" + }, + { + "type": "cons:error", + "severity": "error", + "message": "Error: This is an error message with stack.\n at Context.eval (https://example.cypress.io/__cypress/tests?p=cypress/integration/happyFlow.spec.js:142:30)" + }, + { + "type": "cy:command", + "severity": "success", + "message": "window\t" + }, + { + "type": "cons:log", + "severity": "success", + "message": "This should console.log appear." + }, + { + "type": "cy:command", + "severity": "success", + "message": "window\t" + }, + { + "type": "cons:log", + "severity": "success", + "message": "{\n \"this\": \"Is an object\",\n \"with\": {\n \"keys\": 12512\n }\n}" + }, + { + "type": "cy:command", + "severity": "success", + "message": "window\t" + }, + { + "type": "cons:info", + "severity": "success", + "message": "This should console.info appear." + }, + { + "type": "cy:command", + "severity": "success", + "message": "get\t.network-put" + }, + { + "type": "cy:command", + "severity": "success", + "message": "click\t" + }, + { + "type": "cy:xhr", + "severity": "success", + "message": "STUBBED PUT https://jsonplaceholder.cypress.io/comments/1" + }, + { + "type": "cy:route", + "severity": "warning", + "message": "(putComment) PUT https://jsonplaceholder.cypress.io/comments/1\nStatus: 404\nResponse body: {\n \"error\": \"whoa, this comment does not exist\"\n}" + }, + { + "type": "cy:command", + "severity": "success", + "message": "wait\t@putComment" + }, + { + "type": "cy:command", + "severity": "success", + "message": "get\t.network-put-comment" + }, + { + "type": "cy:command", + "severity": "success", + "message": "assert\texpected **** to contain **whoa, this comment does not exist**" + }, + { + "type": "cy:command", + "severity": "error", + "message": "get\t.breaking-get" + } + ] + }, + "cypress/integration/requests.spec.js": { + "GET 200": [ + { + "type": "cy:request", + "severity": "success", + "message": "https://jsonplaceholder.cypress.io/todos/1\nStatus: 200\nResponse body: {\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}" + }, + { + "type": "cy:request", + "severity": "success", + "message": "GET https://jsonplaceholder.cypress.io/todos/2\nStatus: 200\nResponse body: {\n \"userId\": 1,\n \"id\": 2,\n \"title\": \"quis ut nam facilis et officia qui\",\n \"completed\": false\n}" + }, + { + "type": "cy:request", + "severity": "success", + "message": "GET https://jsonplaceholder.cypress.io/todos/3\nStatus: 200\nResponse body: {\n \"userId\": 1,\n \"id\": 3,\n \"title\": \"fugiat veniam minus\",\n \"completed\": false\n}" + }, + { + "type": "cy:command", + "severity": "error", + "message": "get\t.breaking-get" + } + ], + "POST 200": [ + { + "type": "cy:request", + "severity": "success", + "message": "POST https://jsonplaceholder.cypress.io/comments\nStatus: 201\nResponse body: {\n \"id\": 501\n}" + }, + { + "type": "cy:command", + "severity": "error", + "message": "get\t.breaking-get" + } + ], + "GET should give 500 response status": [ + { + "type": "cy:request", + "severity": "error", + "message": "GET http://www.mocky.io/v2/5ec993353000007900a6ce1e\nStatus: 500\nResponse body: Hey ya! Great to see you here. Btw, nothing is configured for this request path. Create a rule and start building a mock API." + } + ], + "POST should give 400 response status": [ + { + "type": "cy:request", + "severity": "error", + "message": "POST http://www.mocky.io/v2/5ec993803000009700a6ce1f\nStatus: 400\nResponse body: {\n \"status\": \"Wrong!\",\n \"data\": {\n \"corpo\": \"corpo da resposta\",\n \"titulo\": \"titulo da resposta\"\n }\n}" + } + ], + "PUT should fail": [ + { + "type": "cy:request", + "severity": "error", + "message": "PUT https://jsonplaceholder.cypress.io/comments\nStatus: 404\nResponse body: {}" + } + ] + } +} \ No newline at end of file diff --git a/test/output/out.spec.onFail.txt b/test/output/out.spec.onFail.txt new file mode 100644 index 0000000..3b6c932 --- /dev/null +++ b/test/output/out.spec.onFail.txt @@ -0,0 +1,132 @@ +cypress/integration/happyFlow.spec.js: + Happy flow + cy:command (K): visit /commands/network-requests + cy:command (K): get .network-btn + cy:command (K): click + cy:xhr (K): GET https://jsonplaceholder.cypress.io/comments/1 + cy:command (K): wait @getComment + cy:route (K): (getComment) GET https://jsonplaceholder.cypress.io/comments/1 + Status: 200 + Response body: { + "postId": 1, + "id": 1, + "name": "id labore ex et quam laborum", + "email": "Eliseo@gardner.biz", + "body": "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium" + } + cy:command (K): its .status + cy:command (K): assert expected **200** to equal **200** + cy:command (K): get .network-post + cy:command (K): click + cy:xhr (K): POST https://jsonplaceholder.cypress.io/comments + cy:command (K): wait @postComment + cy:route (K): (postComment) POST https://jsonplaceholder.cypress.io/comments + Status: 201 + Response body: { + "name": "Using POST in cy.route()", + "email": "hello@cypress.io", + "body": "You can change the method used for cy.route() to be GET, POST, PUT, PATCH, or DELETE", + "id": 501 + } + cy:command (K): assert expected **name=Using+POST+in+cy.route()&email=hello%40cypress.io&body=You+can+change+the+method+used+for+cy.route()+to+be+GET%2C+POST%2C+PUT%2C+PATCH%2C+or+DELETE** to include **email** + cy:command (K): assert expected **{ Object (Content-Type, Accept) }** to have property **Content-Type** + cy:command (K): assert expected **{ Object (name, email, ...) }** to have property **name** + cy:command (K): assert expected **{ Object (name, email, ...) }** to have property **name** of **Using POST in cy.route()** + cy:command (K): window + cons:error (X): null, + undefined, + , + false, + function () {} + cy:command (K): window + cons:log (K): { + "a": "b" + }, + { + "c": "d" + }, + 10, + string + cy:command (K): window + cons:warn (!): This is a warning message + cy:command (K): window + cons:error (X): This is an error message + cy:command (K): window + cons:error (X): Error: This is an error message with stack. + + cy:command (K): window + cons:log (K): This should console.log appear. + cy:command (K): window + cons:log (K): { + "this": "Is an object", + "with": { + "keys": 12512 + } + } + cy:command (K): window + cons:info (K): This should console.info appear. + cy:command (K): get .network-put + cy:command (K): click + cy:xhr (K): STUBBED PUT https://jsonplaceholder.cypress.io/comments/1 + cy:route (!): (putComment) PUT https://jsonplaceholder.cypress.io/comments/1 + Status: 404 + Response body: { + "error": "whoa, this comment does not exist" + } + cy:command (K): wait @putComment + cy:command (K): get .network-put-comment + cy:command (K): assert expected **** to contain **whoa, this comment does not exist** + cy:command (X): get .breaking-get + + + +cypress/integration/requests.spec.js: + GET 200 + cy:request (K): https://jsonplaceholder.cypress.io/todos/1 + Status: 200 + Response body: { + "userId": 1, + "id": 1, + "title": "delectus aut autem", + "completed": false + } + cy:request (K): GET https://jsonplaceholder.cypress.io/todos/2 + Status: 200 + Response body: { + "userId": 1, + "id": 2, + "title": "quis ut nam facilis et officia qui", + "completed": false + } + cy:request (K): GET https://jsonplaceholder.cypress.io/todos/3 + Status: 200 + Response body: { + "userId": 1, + "id": 3, + "title": "fugi: get .breaking-get + + POST 200 + cy:request (K): POST https://jsonplaceholder.cypress.io/comments + Status: 201 + Response body: { + "id": 501 + } + cy:command (X): get .breaking-get + + GET should give 500 response status + cy:request (X): GET http://www.mocky.io/v2/5ec993353000007900a6ce1e + Status: 500 + Response body: Hey ya! Gre: POST http://www.mocky.io/v2/5ec993803000009700a6ce1f + Status: 400 + Response body: { + "status": "Wrong!", + "data": { + "corpo": "corpo da resposta", + "titulo": "titulo da resposta" + } + } + + PUT should fail + cy:request (X): PUT https://jsonplaceholder.cypress.io/comments + Status: 404 + Response body: {} diff --git a/test/test.js b/test/test.js index 7632ffe..5e15933 100755 --- a/test/test.js +++ b/test/test.js @@ -49,10 +49,67 @@ const runTest = async (command, callback) => { }); }; -function outPutCleanUp(testOutputs, outRoot) { - testOutputs.forEach((out) => { - if (fs.existsSync(path.join(outRoot, out))) { - fs.unlinkSync(path.join(outRoot, out)); +outputCleanUpAndInitialization = (testOutputs, outRoot) => { + outRoot.value = path.join(__dirname, 'output'); + testOutputs.value = ['out.txt', 'out.json', 'out.cst']; + testOutputs.value.forEach((out) => { + if (fs.existsSync(path.join(outRoot.value, out))) { + fs.unlinkSync(path.join(outRoot.value, out)); + } + }); +} + +const osSpecificEol = (str) => + // Change line endings to win32 if needed + (os.EOL === '\r\n' ? str.replace(/\n/g, '\r\n') : str); + +const clean = (str) => + // Clean error trace as it changes from test to test. + str.replace(/at [^(]+ \([^)]+\)/g, ''); + +expectOutputFilesToBeCorrect = (testOutputs, outRoot, specFiles, onFailOrAlways) => { + testOutputs.value.forEach((out) => { + const expectedBuffer = fs.readFileSync( + path.join(outRoot.value, out.replace(/\.([a-z]+)$/, '.spec.' + onFailOrAlways + '.$1')) + ); + const valueBuffer = fs.readFileSync(path.join(outRoot.value, out)); + let value = clean(valueBuffer.toString().replace(/\s+$/, '')); + if (path.sep === '\\') { + specFiles.forEach((specFile) => { + const expectPath = 'cypress/integration/' + specFile; + if (out.endsWith('json')) { + const osJsonPath = 'cypress\\\\integration\\\\' + specFile; + value = value.replace(osJsonPath, expectPath); + } else { + const osPath = 'cypress\\integration\\' + specFile; + value = value.replace(osPath, expectPath); + } + }); + } + + let expected = clean(expectedBuffer.toString().replace(/\s+$/, '')); + if (out.endsWith('.txt')) { + expected = osSpecificEol(expected); + } + + expect(clean(value), `Check ${out} matched spec.`).to.eq(clean(expected)); + }); +} + +expectConsoleLogForOutput = (stdout, outRoot, fileNames = [''], toNot = false) => { + fileNames.forEach((fileName) => { + let ext = path.extname(fileName).substring(1); + if (!['json', 'txt'].includes(ext)){ + ext = 'custom'; + } + let logString = '[cypress-terminal-report] Wrote ' + ext + + ' logs to ' + path.join(outRoot.value, fileName); + + if (toNot){ + expect(stdout).to.not.contain(logString); + } + else{ + expect(stdout).to.contain(logString); } }); } @@ -211,73 +268,45 @@ describe('cypress-terminal-report', () => { }); }).timeout(60000); - it('Should generate proper log output files, and print everything if configured so.', async () => { - const outRoot = path.join(__dirname, 'output'); - const testOutputs = ['out.txt', 'out.json', 'out.cst']; - outPutCleanUp(testOutputs, outRoot); + it('Should generate proper log output files, and print only failing ones if config is on default.', async () => { + const outRoot = {}; + const testOutputs = {}; + outputCleanUpAndInitialization(testOutputs, outRoot); - if (fs.existsSync(path.join(outRoot, 'not'))) { - fs.rmdirSync(path.join(outRoot, 'not'), { recursive: true }); + if (fs.existsSync(path.join(outRoot.value, 'not'))) { + fs.rmdirSync(path.join(outRoot.value, 'not'), { recursive: true }); } - const clean = (str) => - // Clean error trace as it changes from test to test. - str.replace(/at [^(]+ \([^)]+\)/g, ''); + const specFiles = ['requests.spec.js', 'happyFlow.spec.js', 'printLogsSuccess.spec.js']; + await runTest(commandBase(['generateOutput=1'], specFiles), (error, stdout, stderr) => { + expectOutputFilesToBeCorrect(testOutputs, outRoot, specFiles, 'onFail'); + expectConsoleLogForOutput(stdout, outRoot, ['out.txt', 'out.json', 'out.cst', path.join('not', 'existing', 'path', 'out.txt')]); + }); + }).timeout(60000); - const osSpecificEol = (str) => - // Change line endings to win32 if needed - (os.EOL === '\r\n' ? str.replace(/\n/g, '\r\n') : str); + it('Should print all tests to output files when configured so.', async () => { + const outRoot = {}; + const testOutputs = {}; + outputCleanUpAndInitialization(testOutputs, outRoot); const specFiles = ['requests.spec.js', 'happyFlow.spec.js', 'printLogsSuccess.spec.js']; await runTest(commandBase(['generateOutput=1', 'printLogsToFileAlways=1'], specFiles), (error, stdout, stderr) => { - testOutputs.forEach((out) => { - const expectedBuffer = fs.readFileSync( - path.join(outRoot, out.replace(/\.([a-z]+)$/, '.spec.$1')) - ); - const valueBuffer = fs.readFileSync(path.join(outRoot, out)); - let value = clean(valueBuffer.toString().replace(/\s+$/, '')); - if (path.sep === '\\') { - specFiles.forEach((specFile) => { - const expectPath = 'cypress/integration/' + specFile; - if (out.endsWith('json')) { - const osJsonPath = 'cypress\\\\integration\\\\' + specFile; - value = value.replace(osJsonPath, expectPath); - } else { - const osPath = 'cypress\\integration\\' + specFile; - value = value.replace(osPath, expectPath); - } - }); - } - - let expected = clean(expectedBuffer.toString().replace(/\s+$/, '')); - if (out.endsWith('.txt')) { - expected = osSpecificEol(expected); - } - - expect(clean(value), `Check ${out} matched spec.`).to.eq(clean(expected)); - }); - - expect(stdout).to.contain('[cypress-terminal-report] Wrote txt logs to ' + path.join(outRoot, 'not', 'existing', 'path', 'out.txt')); - expect(stdout).to.contain('[cypress-terminal-report] Wrote txt logs to ' + path.join(outRoot, 'out.txt')); - expect(stdout).to.contain('[cypress-terminal-report] Wrote json logs to ' + path.join(outRoot, 'out.json')); - expect(stdout).to.contain('[cypress-terminal-report] Wrote custom logs to ' + path.join(outRoot, 'out.cst')); + expectOutputFilesToBeCorrect(testOutputs, outRoot, specFiles, 'always'); + expectConsoleLogForOutput(stdout, outRoot, ['out.txt', 'out.json', 'out.cst']); }); }).timeout(60000); - it('Should not generate/print to output files when configured so.', async () => { - const outRoot = path.join(__dirname, 'output'); - const testOutputs = ['out.txt', 'out.json', 'out.cst']; - outPutCleanUp(testOutputs, outRoot); + it('Should not generate and print to output files when configured so.', async () => { + const outRoot = {}; + const testOutputs = {}; + outputCleanUpAndInitialization(testOutputs, outRoot); const specFiles = ['requests.spec.js', 'happyFlow.spec.js', 'printLogsSuccess.spec.js']; await runTest(commandBase(['generateOutput=1', 'printLogsToFileNever=1'], specFiles), (error, stdout, stderr) => { - testOutputs.forEach((out) => { - expect(fs.existsSync(path.join(outRoot, out))).false; + testOutputs.value.forEach((out) => { + expect(fs.existsSync(path.join(outRoot.value, out))).false; }); - - expect(stdout).to.not.contain('[cypress-terminal-report] Wrote txt logs to ' + path.join(outRoot, 'out.txt')); - expect(stdout).to.not.contain('[cypress-terminal-report] Wrote json logs to ' + path.join(outRoot, 'out.json')); - expect(stdout).to.not.contain('[cypress-terminal-report] Wrote custom logs to ' + path.join(outRoot, 'out.cst')); + expectConsoleLogForOutput(stdout, outRoot, ['out.txt', 'out.json', 'out.cst'], true); }); }).timeout(60000); From c9e7871e990580951f181514dc44967d68aa4a58 Mon Sep 17 00:00:00 2001 From: Fodor Levente Date: Tue, 6 Oct 2020 16:12:03 +0300 Subject: [PATCH 9/9] Added new test case for 'onFail' option, more bad code fix. --- src/installLogsCollector.schema.json | 17 +++++++- src/installLogsPrinter.js | 43 ++++++++++--------- src/installLogsPrinter.schema.json | 25 ++++++++--- src/outputProcessor/BaseOutputProcessor.js | 8 ++-- src/outputProcessor/CustomOutputProcessor.js | 4 +- src/outputProcessor/JsonOutputProcessor.js | 1 - src/outputProcessor/TextOutputProcessor.js | 1 - .../integration/printLogsOnFail.spec.js | 11 +++++ test/cypress/plugins/index.js | 4 ++ test/output/out.spec.onFailCheck.txt | 4 ++ test/test.js | 33 +++++++++----- 11 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 test/cypress/integration/printLogsOnFail.spec.js create mode 100644 test/output/out.spec.onFailCheck.txt diff --git a/src/installLogsCollector.schema.json b/src/installLogsCollector.schema.json index 67432bf..2525c67 100644 --- a/src/installLogsCollector.schema.json +++ b/src/installLogsCollector.schema.json @@ -3,13 +3,26 @@ "properties": { "printLogs": { "type": "string", - "enum": ["onFail", "always"] + "enum": [ + "onFail", + "always" + ] }, "collectTypes": { "type": "array", "items": { "type": "string", - "enum": ["cons:log","cons:info", "cons:warn", "cons:error", "cy:log", "cy:xhr", "cy:request", "cy:route", "cy:command"] + "enum": [ + "cons:log", + "cons:info", + "cons:warn", + "cons:error", + "cy:log", + "cy:xhr", + "cy:request", + "cy:route", + "cy:command" + ] } }, "filterLog": { diff --git a/src/installLogsPrinter.js b/src/installLogsPrinter.js index a5d9a2b..fb74f9c 100755 --- a/src/installLogsPrinter.js +++ b/src/installLogsPrinter.js @@ -71,14 +71,15 @@ function installLogsPrinter(on, options = {}) { messages = compactLogs(messages, options.compactLogs); } - if (options.outputTarget) { - allMessages[0] = data.state; - allMessages[data.spec] = allMessages[data.spec] || {}; - allMessages[data.spec][data.test] = messages; + if (options.outputTarget && options.printLogsToFile !== "never") { + if (data.state === "failed" || options.printLogsToFile === "always") { + allMessages[data.spec] = allMessages[data.spec] || {}; + allMessages[data.spec][data.test] = messages; + } } if ((options.printLogsToConsole === "onFail" && data.state !== "passed") - || options.printLogsToConsole === "always"){ + || options.printLogsToConsole === "always") { logToTerminal(messages, options); } @@ -86,8 +87,7 @@ function installLogsPrinter(on, options = {}) { }, [CONSTANTS.TASK_NAME_OUTPUT]: () => { outputProcessors.forEach((processor) => { - if ((options.printLogsToFile === "onFail" && allMessages[0] !== "passed") - || options.printLogsToFile === "always"){ + if (Object.entries(allMessages).length !== 0){ processor.write(allMessages); logOutputTarget(processor); } @@ -102,10 +102,11 @@ function installLogsPrinter(on, options = {}) { } } + function logOutputTarget(processor) { let message; let standardOutputType = Object.keys(OUTPUT_PROCESSOR_TYPE).find( - (type) => processor instanceof OUTPUT_PROCESSOR_TYPE[type] + (type) => processor instanceof OUTPUT_PROCESSOR_TYPE[type] ); if (standardOutputType) { message = `Wrote ${standardOutputType} logs to ${processor.file}. (${processor.writeSpendTime}ms)`; @@ -139,7 +140,7 @@ function installOutputProcessors(on, root, outputTargets) { function compactLogs(logs, keepAroundCount) { const failingIndexes = logs.filter((log) => log[2] === CONSTANTS.SEVERITY.ERROR) - .map((log) => logs.indexOf(log)); + .map((log) => logs.indexOf(log)); const includeIndexes = new Array(logs.length); @@ -153,11 +154,11 @@ function compactLogs(logs, keepAroundCount) { const compactedLogs = []; const addOmittedLog = (count) => - compactedLogs.push([ - CONSTANTS.LOG_TYPES.PLUGIN_LOG_TYPE, - `[ ... ${count} omitted logs ... ]`, - CONSTANTS.SEVERITY.SUCCESS - ]); + compactedLogs.push([ + CONSTANTS.LOG_TYPES.PLUGIN_LOG_TYPE, + `[ ... ${count} omitted logs ... ]`, + CONSTANTS.SEVERITY.SUCCESS + ]); let excludeCount = 0; for (let i = 0; i < includeIndexes.length; i++) { @@ -182,14 +183,14 @@ function compactLogs(logs, keepAroundCount) { function logToTerminal(messages, options) { const padType = (type) => - new Array(Math.max(CONSTANTS.PADDING.LOG.length - type.length - 3, 0)).join(' ') + type + ' '; + new Array(Math.max(CONSTANTS.PADDING.LOG.length - type.length - 3, 0)).join(' ') + type + ' '; messages.forEach(([type, message, severity]) => { let color = 'white', - typeString = KNOWN_TYPES.includes(type) ? padType(type) : padType('[unknown]'), - processedMessage = message, - trim = options.defaultTrimLength || 800, - icon = '-'; + typeString = KNOWN_TYPES.includes(type) ? padType(type) : padType('[unknown]'), + processedMessage = message, + trim = options.defaultTrimLength || 800, + icon = '-'; if (type === LOG_TYPES.BROWSER_CONSOLE_WARN) { color = 'yellow'; @@ -236,8 +237,8 @@ function logToTerminal(messages, options) { } const coloredTypeString = ['red', 'yellow'].includes(color) ? - chalk[color].bold(typeString + icon + ' ') : - chalk[color](typeString + icon + ' '); + chalk[color].bold(typeString + icon + ' ') : + chalk[color](typeString + icon + ' '); console.log(coloredTypeString, processedMessage.replace(/\n/g, '\n' + CONSTANTS.PADDING.LOG)); }); diff --git a/src/installLogsPrinter.schema.json b/src/installLogsPrinter.schema.json index 5928b11..67d1bfe 100644 --- a/src/installLogsPrinter.schema.json +++ b/src/installLogsPrinter.schema.json @@ -3,11 +3,19 @@ "properties": { "printLogsToConsole": { "type": "string", - "enum": ["onFail", "always", "never"] + "enum": [ + "onFail", + "always", + "never" + ] }, "printLogsToFile": { "type": "string", - "enum": ["onFail", "always", "never"] + "enum": [ + "onFail", + "always", + "never" + ] }, "defaultTrimLength": { "type": "string" @@ -29,14 +37,21 @@ "type": "object", "patternProperties": { ".*": { - "type": ["string", "function"] + "type": [ + "string", + "function" + ] } } } }, "dependencies": { - "outputRoot": ["outputTarget"], - "outputTarget": ["outputRoot"] + "outputRoot": [ + "outputTarget" + ], + "outputTarget": [ + "outputRoot" + ] }, "additionalProperties": false } diff --git a/src/outputProcessor/BaseOutputProcessor.js b/src/outputProcessor/BaseOutputProcessor.js index 6964536..7c8ed5d 100755 --- a/src/outputProcessor/BaseOutputProcessor.js +++ b/src/outputProcessor/BaseOutputProcessor.js @@ -28,7 +28,7 @@ module.exports = class BaseOutputProcessor { const basePath = path.dirname(this.file); if (!fs.existsSync(basePath)) { - fs.mkdirSync(basePath, { recursive: true }); + fs.mkdirSync(basePath, {recursive: true}); } fs.writeFileSync(this.file, this.initialContent); @@ -114,11 +114,9 @@ module.exports = class BaseOutputProcessor { getAbsolutePositionFromRelative(pos) { if (pos === null) { return this.size; - } - else if (pos < 0) { + } else if (pos < 0) { return Math.min(this.size, Math.max(0, this.size + pos)); - } - else { + } else { return Math.min(this.size, pos); } } diff --git a/src/outputProcessor/CustomOutputProcessor.js b/src/outputProcessor/CustomOutputProcessor.js index ff3ab61..ae9615a 100644 --- a/src/outputProcessor/CustomOutputProcessor.js +++ b/src/outputProcessor/CustomOutputProcessor.js @@ -8,9 +8,7 @@ module.exports = class CustomOutputProcessor extends BaseOutputProcessor { } write(allMessages) { - let allMessagesWithoutState = Object.assign({}, allMessages); - delete allMessagesWithoutState[0]; - this.processorCallback.call(this, allMessagesWithoutState); + this.processorCallback.call(this, allMessages); } }; diff --git a/src/outputProcessor/JsonOutputProcessor.js b/src/outputProcessor/JsonOutputProcessor.js index 6717a12..ef34ed9 100644 --- a/src/outputProcessor/JsonOutputProcessor.js +++ b/src/outputProcessor/JsonOutputProcessor.js @@ -11,7 +11,6 @@ module.exports = class JsonOutputProcessor extends BaseOutputProcessor { write(allMessages) { Object.entries(allMessages).forEach(([spec, tests]) => { - if (spec === "0") return; let data = {[spec]: {}}; Object.entries(tests).forEach(([test, messages]) => { data[spec][test] = messages.map(([type, message, severity]) => ({ diff --git a/src/outputProcessor/TextOutputProcessor.js b/src/outputProcessor/TextOutputProcessor.js index 79b5cfd..51f11d1 100644 --- a/src/outputProcessor/TextOutputProcessor.js +++ b/src/outputProcessor/TextOutputProcessor.js @@ -28,7 +28,6 @@ module.exports = class TextOutputProcessor extends BaseOutputProcessor { write(allMessages) { Object.entries(allMessages).forEach(([spec, tests]) => { - if (spec === "0") return; let text = `${spec}:${EOL}`; Object.entries(tests).forEach(([test, messages]) => { text += `${PADDING}${test}${EOL}`; diff --git a/test/cypress/integration/printLogsOnFail.spec.js b/test/cypress/integration/printLogsOnFail.spec.js new file mode 100644 index 0000000..011f990 --- /dev/null +++ b/test/cypress/integration/printLogsOnFail.spec.js @@ -0,0 +1,11 @@ +describe('Print Logs onFail prints only failing tests.', () => { + + it('Print Logs Success', () => { + cy.visit('/'); + cy.contains('cypress'); + }); + it('Print Logs Fail', () => { + cy.visit('/'); + cy.contains('sserpyc'); + }); +}); diff --git a/test/cypress/plugins/index.js b/test/cypress/plugins/index.js index 87b8187..08cb2d0 100755 --- a/test/cypress/plugins/index.js +++ b/test/cypress/plugins/index.js @@ -16,6 +16,10 @@ module.exports = (on, config) => { }, }; } + if (config.env.generateSimpleOutput == "1") { + options.outputRoot = config.projectRoot + '/output/'; + options.outputTarget = {'out.txt': 'txt'}; + } if (config.env.compactLogs == "1") { options.compactLogs = 1; } diff --git a/test/output/out.spec.onFailCheck.txt b/test/output/out.spec.onFailCheck.txt new file mode 100644 index 0000000..46fe2af --- /dev/null +++ b/test/output/out.spec.onFailCheck.txt @@ -0,0 +1,4 @@ +cypress/integration/printLogsOnFail.spec.js: + Print Logs Fail + cy:command (K): visit / + cy:command (X): contains sserpyc \ No newline at end of file diff --git a/test/test.js b/test/test.js index 5e15933..a758162 100755 --- a/test/test.js +++ b/test/test.js @@ -41,7 +41,7 @@ const runTest = async (command, callback) => { lastRunOutput = stdout; // Normalize line endings for unix. const normalizedStdout = stdout.replace(/\r\n/g, "\n"); - callback(error, normalizedStdout , stderr); + callback(error, normalizedStdout, stderr); expect(normalizedStdout).to.not.contain("CypressError: `cy.task('ctrLogMessages')` failed"); resolve(); @@ -67,10 +67,10 @@ const clean = (str) => // Clean error trace as it changes from test to test. str.replace(/at [^(]+ \([^)]+\)/g, ''); -expectOutputFilesToBeCorrect = (testOutputs, outRoot, specFiles, onFailOrAlways) => { +expectOutputFilesToBeCorrect = (testOutputs, outRoot, specFiles, specExtName) => { testOutputs.value.forEach((out) => { const expectedBuffer = fs.readFileSync( - path.join(outRoot.value, out.replace(/\.([a-z]+)$/, '.spec.' + onFailOrAlways + '.$1')) + path.join(outRoot.value, out.replace(/\.([a-z]+)$/, '.spec.' + specExtName + '.$1')) ); const valueBuffer = fs.readFileSync(path.join(outRoot.value, out)); let value = clean(valueBuffer.toString().replace(/\s+$/, '')); @@ -99,16 +99,15 @@ expectOutputFilesToBeCorrect = (testOutputs, outRoot, specFiles, onFailOrAlways) expectConsoleLogForOutput = (stdout, outRoot, fileNames = [''], toNot = false) => { fileNames.forEach((fileName) => { let ext = path.extname(fileName).substring(1); - if (!['json', 'txt'].includes(ext)){ + if (!['json', 'txt'].includes(ext)) { ext = 'custom'; } let logString = '[cypress-terminal-report] Wrote ' + ext + ' logs to ' + path.join(outRoot.value, fileName); - if (toNot){ + if (toNot) { expect(stdout).to.not.contain(logString); - } - else{ + } else { expect(stdout).to.contain(logString); } }); @@ -274,13 +273,14 @@ describe('cypress-terminal-report', () => { outputCleanUpAndInitialization(testOutputs, outRoot); if (fs.existsSync(path.join(outRoot.value, 'not'))) { - fs.rmdirSync(path.join(outRoot.value, 'not'), { recursive: true }); + fs.rmdirSync(path.join(outRoot.value, 'not'), {recursive: true}); } const specFiles = ['requests.spec.js', 'happyFlow.spec.js', 'printLogsSuccess.spec.js']; await runTest(commandBase(['generateOutput=1'], specFiles), (error, stdout, stderr) => { expectOutputFilesToBeCorrect(testOutputs, outRoot, specFiles, 'onFail'); - expectConsoleLogForOutput(stdout, outRoot, ['out.txt', 'out.json', 'out.cst', path.join('not', 'existing', 'path', 'out.txt')]); + testOutputs.value.push(path.join('not', 'existing', 'path', 'out.txt')); + expectConsoleLogForOutput(stdout, outRoot, testOutputs.value); }); }).timeout(60000); @@ -292,7 +292,7 @@ describe('cypress-terminal-report', () => { const specFiles = ['requests.spec.js', 'happyFlow.spec.js', 'printLogsSuccess.spec.js']; await runTest(commandBase(['generateOutput=1', 'printLogsToFileAlways=1'], specFiles), (error, stdout, stderr) => { expectOutputFilesToBeCorrect(testOutputs, outRoot, specFiles, 'always'); - expectConsoleLogForOutput(stdout, outRoot, ['out.txt', 'out.json', 'out.cst']); + expectConsoleLogForOutput(stdout, outRoot, testOutputs.value); }); }).timeout(60000); @@ -306,7 +306,18 @@ describe('cypress-terminal-report', () => { testOutputs.value.forEach((out) => { expect(fs.existsSync(path.join(outRoot.value, out))).false; }); - expectConsoleLogForOutput(stdout, outRoot, ['out.txt', 'out.json', 'out.cst'], true); + expectConsoleLogForOutput(stdout, outRoot, testOutputs.value, true); + }); + }).timeout(60000); + + it('Should generate output only for failing tests if set to \'onFail\'.', async () => { + const outRoot = { value : path.join(__dirname, 'output') }; + const testOutputs = { value : ["out.txt"] }; + + const specFiles = ['printLogsOnFail.spec.js']; + await runTest(commandBase(['generateSimpleOutput=1'], specFiles), (error, stdout, stderr) => { + expectOutputFilesToBeCorrect(testOutputs, outRoot, specFiles, 'onFailCheck'); + expectConsoleLogForOutput(stdout, outRoot, testOutputs.value); }); }).timeout(60000);