From c5b3abfe1581593ab52473308087e93453f80955 Mon Sep 17 00:00:00 2001 From: Andrei Rusu Date: Tue, 6 Apr 2021 15:42:28 +0200 Subject: [PATCH] Added filename_format config option for setting the filename format of the screenshots -- #2023 --- lib/api/client-commands/end.js | 6 +- lib/index.js | 20 +---- lib/settings/defaults.js | 24 +++++- lib/settings/settings.js | 24 +++++- lib/utils/screenshots.js | 29 +++---- test/src/api/commands/testClick.js | 4 +- test/src/api/commands/testEnd.js | 80 +++++++++++++++++++- test/src/index/testSettings.js | 18 +++-- test/src/runner/cli/testCliRunnerGenerate.js | 2 +- 9 files changed, 159 insertions(+), 48 deletions(-) diff --git a/lib/api/client-commands/end.js b/lib/api/client-commands/end.js index 21e2b20122..2d8ccfad0d 100644 --- a/lib/api/client-commands/end.js +++ b/lib/api/client-commands/end.js @@ -23,8 +23,10 @@ class End extends EventEmitter { if (this.testFailuresExist() && this.shouldTakeScreenshot()) { Logger.info(`Failures in "${this.api.currentTest.name}". Taking screenshot...`); - const prefix = `${this.api.currentTest.module}/${this.api.currentTest.name}`; - const fileNamePath = Screenshots.getFileName(prefix, false, client.options.screenshots.path); + const fileNamePath = Screenshots.getFileName({ + testSuite: this.api.currentTest.module, + testCase: this.api.currentTest.name + }, client.options.screenshots); this.api.saveScreenshot(fileNamePath, (result, err) => { if (!err && this.client.transport.isResultSuccess(result)) { diff --git a/lib/index.js b/lib/index.js index c4b1ececfd..5caad744fb 100644 --- a/lib/index.js +++ b/lib/index.js @@ -176,8 +176,8 @@ class NightwatchClient extends EventEmitter { return this.settings.start_session; } - get screenshotsEnabled() { - return Utils.isObject(this.settings.screenshots) ? this.settings.screenshots && this.options.screenshots.enabled === true : false; + screenshotsEnabled() { + return Utils.isObject(this.settings.screenshots) ? (this.settings.screenshots && this.options.screenshots.enabled === true) : false; } get reporter() { @@ -297,23 +297,11 @@ class NightwatchClient extends EventEmitter { } setScreenshotOptions() { - let screenshots = this.settings.screenshots; - - if (this.screenshotsEnabled) { - if (typeof screenshots.path == 'undefined') { - throw new Error('Please specify the screenshots.path in nightwatch.json.'); - } - - this.settings.screenshots.on_error = this.settings.screenshots.on_error || typeof this.options.screenshots.on_error == 'undefined'; - this.settings.screenshotsPath = screenshots.path; + const {screenshots} = this.settings; + if (this.screenshotsEnabled()) { this.setApiProperty('screenshotsPath', screenshots.path) .setApiOption('screenshotsPath', screenshots.path); - } else { - this.settings.screenshots = { - enabled : false, - path : '' - }; } return this; diff --git a/lib/settings/defaults.js b/lib/settings/defaults.js index fc1e18a4ac..1ab3cae6e3 100644 --- a/lib/settings/defaults.js +++ b/lib/settings/defaults.js @@ -1,3 +1,19 @@ +const filename_format = function ({testSuite = '', testCase = '', isError = false, dateObject = new Date()} = {}) { + const fileName = []; + const dateParts = dateObject.toString().replace(/:/g,'').split(' '); + dateParts.shift(); + + const dateStamp = dateParts.slice(0,5).join('-'); + if (testSuite) { + fileName.push(testSuite); + } + if (testCase) { + fileName.push(testCase); + } + + return `${fileName.join('/')}${isError ? '_ERROR' : '_FAILED'}_${dateStamp}.png`; +}; + module.exports = { // Location(s) where custom commands will be loaded from. custom_commands_path: null, @@ -184,7 +200,13 @@ module.exports = { report_command_errors: false, // Take error and failure screenshots during test execution - screenshots: false, + screenshots: { + enabled: false, + filename_format, + path: '', + on_error: true, + on_failure: true + }, // Used to enable showing the Base64 image data in the (verbose) log when taking screenshots. log_screenshot_data: false, diff --git a/lib/settings/settings.js b/lib/settings/settings.js index 83e69df7c1..42468e9b1b 100644 --- a/lib/settings/settings.js +++ b/lib/settings/settings.js @@ -1,11 +1,12 @@ const dotenv = require('dotenv'); +const path = require('path'); const defaultsDeep = require('lodash.defaultsdeep'); const lodashClone = require('lodash.clone'); const lodashMerge = require('lodash.merge'); const CI_Info = require('ci-info'); const Defaults = require('./defaults.js'); const Utils = require('../utils'); -const {Logger} = Utils; +const {Logger, isObject} = Utils; class Settings { @@ -40,7 +41,7 @@ class Settings { let copyVal = lodashClone(this.baseSettings[key], true); - if (Utils.isObject(this.settings[key])) { + if (isObject(this.settings[key])) { Object.assign(this.settings[key], copyVal); } else { this.settings[key] = copyVal; @@ -56,6 +57,8 @@ class Settings { lodashMerge(this.settings, settings); this.setCliOptions(); + this.setScreenshotsOptions(); + this.setScreenshotsPath(); this.setUnitTestsMode(); this.setParallelMode(); this.setTestRunner(); @@ -202,6 +205,23 @@ class Settings { } } + setScreenshotsOptions() { + if (isObject(this.settings.screenshots)) { + this.settings.screenshots.path = path.resolve(this.settings.screenshots.path); + } else { + const enabled = this.settings.screenshots === true; + this.settings.screenshots = Object.assign({}, Defaults.screenshots, {enabled}); + } + + return this; + } + + setScreenshotsPath() { + this.settings.screenshotsPath = this.settings.screenshots.path; + + return this; + } + setCliOptions() { if (this.argv.verbose) { this.settings.silent = false; diff --git a/lib/utils/screenshots.js b/lib/utils/screenshots.js index a4d020d088..956b9b35c6 100644 --- a/lib/utils/screenshots.js +++ b/lib/utils/screenshots.js @@ -1,28 +1,29 @@ const path = require('path'); const fs = require('fs'); const mkpath = require('mkpath'); +const Defaults = require('../settings/defaults.js'); class Screenshots { /** - * @param {string} prefix - * @param {boolean} isError - * @param {string} screenshotsPath + * @param {object} prefix + * @param {object} screenshots * @return {string} */ - static getFileName(prefix, isError, screenshotsPath) { - let filenamePrefix = prefix - .replace(/\s/g, '-') - .replace(/"|'/g, ''); + static getFileName({testSuite, testCase, isError = false}, screenshots) { + const dateObject = new Date(); + let filename; + let filename_format; - filenamePrefix += isError ? '_ERROR' : '_FAILED'; + if (typeof screenshots.filename_format == 'function') { + filename_format = screenshots.filename_format.bind(screenshots); + } else { + filename_format = Defaults.screenshots.filename_format; + } - const dateParts = new Date().toString().replace(/:/g,'').split(' '); - dateParts.shift(); - dateParts.pop(); + filename = filename_format({testSuite, testCase, isError, dateObject}); + filename = filename.replace(/\s/g, '-').replace(/["']/g, ''); - const dateStamp = dateParts.join('-'); - - return path.resolve(path.join(screenshotsPath, `${filenamePrefix}_${dateStamp}.png`)); + return path.join(screenshots.path, filename); } /** diff --git a/test/src/api/commands/testClick.js b/test/src/api/commands/testClick.js index 7f3f150ec9..91427da0ef 100644 --- a/test/src/api/commands/testClick.js +++ b/test/src/api/commands/testClick.js @@ -3,7 +3,7 @@ const Nightwatch = require('../../../lib/nightwatch.js'); const MockServer = require('../../../lib/mockserver.js'); const CommandGlobals = require('../../../lib/globals/commands.js'); -describe('click', function() { +describe('.click()', function() { before(function(done) { CommandGlobals.beforeEach.call(this, done); }); @@ -87,6 +87,8 @@ describe('click', function() { version2: false, start_process: false }, + output: true, + silent: false, webdriver:{ start_process: true }, diff --git a/test/src/api/commands/testEnd.js b/test/src/api/commands/testEnd.js index 193e79c311..416eb1488a 100644 --- a/test/src/api/commands/testEnd.js +++ b/test/src/api/commands/testEnd.js @@ -3,7 +3,7 @@ const MockServer = require('../../../lib/mockserver.js'); const Nightwatch = require('../../../lib/nightwatch.js'); const CommandGlobals = require('../../../lib/globals/commands.js'); -describe('end', function() { +describe('.end()', function() { before(function(done) { CommandGlobals.beforeEach.call(this, done); }); @@ -45,9 +45,28 @@ describe('end', function() { screenshots: { enabled: true, on_failure: true, - path: './screens' + path: 'screens' } }).then(client => { + assert.strictEqual(typeof client.settings.screenshots.filename_format, 'function'); + + const fileNameFailed = client.settings.screenshots.filename_format({ + testSuite: 'xxTestSuite', + testCase: 'xxTestCase', + isError: false + }); + + const fileNameError = client.settings.screenshots.filename_format({ + testSuite: 'xxTestSuite', + testCase: 'xxTestCase', + isError: true + }); + + assert.ok(fileNameFailed.startsWith('xxTestSuite/xxTestCase_FAILED_')); + assert.ok(fileNameFailed.endsWith('.png')); + assert.ok(fileNameError.startsWith('xxTestSuite/xxTestCase_ERROR_')); + assert.ok(fileNameError.endsWith('.png')); + client.api.currentTest = { module: 'test_module', name: 'test_name', @@ -62,6 +81,57 @@ describe('end', function() { }); client.start(done); + }).catch(err => done(err)); + }); + + it('client.end() - with screenshot and custom filename format', function (done) { + MockServer.addMock({ + url: '/wd/hub/session/1352110219202/screenshot', + method: 'GET', + response: JSON.stringify({ + status: 0, + state: 'success', + value: '==content' + }) + }, true); + + Nightwatch.initClient({ + screenshots: { + enabled: true, + on_failure: true, + path: './screens', + filename_format({testSuite, testCase, isError, dateObject}) { + return `${testSuite}/${testCase}--failed.png`; + } + } + }).then(client => { + client.api.currentTest = { + module: 'test_module', + name: 'test_name', + results: { + failed: 1, + passed: 0 + } + }; + + let screenFileName; + const saveScreenshot = client.api.saveScreenshot; + client.api.saveScreenshot = function (file, callback) { + screenFileName = file; + }; + client.api.end(function callback(result) { + assert.strictEqual(result.status, 0); + }); + + client.start(function(err) { + try { + assert.ok(screenFileName.endsWith('test_module/test_name--failed.png')); + client.api.saveScreenshot = saveScreenshot; + done(err); + } catch (e) { + done(e); + } + }); }); }); @@ -91,6 +161,7 @@ describe('end', function() { } }; + const saveScreenshot = client.api.saveScreenshot; client.api.saveScreenshot = function (file, callback) { throw new Error('saveScreenshot should not be called'); }; @@ -99,7 +170,10 @@ describe('end', function() { assert.strictEqual(result.status, 0); }); - client.start(done); + client.start(function(err) { + client.api.saveScreenshot = saveScreenshot; + done(err); + }); }); }); }); diff --git a/test/src/index/testSettings.js b/test/src/index/testSettings.js index 93c1103a34..56c3e40366 100644 --- a/test/src/index/testSettings.js +++ b/test/src/index/testSettings.js @@ -27,7 +27,8 @@ describe('test Settings', function () { eq(client.options.webdriver.port, 4444); eq(client.options.webdriver.ssl, false); - assert.deepEqual(client.api.options.screenshots, {enabled: false, path: ''}); + eq(client.api.options.screenshots.enabled, false); + eq(client.api.options.screenshots.path, ''); eq(client.options.start_session, true); eq(client.options.end_session_on_fail, true); @@ -131,7 +132,6 @@ describe('test Settings', function () { eq(client.api.options.log_screenshot_data, true); eq(client.options.screenshots.on_error, true); - eq(client.api.options.screenshotsPath, ''); }); it('testSetOptionsScreenshotsOnError', function () { @@ -148,13 +148,15 @@ describe('test Settings', function () { }); it('testSetOptionsScreenshotsThrows', function () { - assert.throws(function () { - Nightwatch.createClient({ - screenshots: { - enabled: true - } - }); + let client = Nightwatch.createClient({ + screenshots: true }); + + eq(client.settings.screenshots.enabled, true); + eq(client.settings.screenshots.on_error, true); + eq(client.settings.screenshots.on_failure, true); + eq(client.settings.screenshots.path, ''); + assert.ok(client.settings.screenshots.filename_format().startsWith('_FAILED_')); }); it('testEndSessionOnFail', function () { diff --git a/test/src/runner/cli/testCliRunnerGenerate.js b/test/src/runner/cli/testCliRunnerGenerate.js index ab8b796f3c..81603ff006 100644 --- a/test/src/runner/cli/testCliRunnerGenerate.js +++ b/test/src/runner/cli/testCliRunnerGenerate.js @@ -54,7 +54,7 @@ describe('Test CLI Runner Generate', function() { } }, - readFileSync: function (fileName) { + readFileSync: function(fileName) { return { toString: function () { return tplData;