diff --git a/README.md b/README.md index b16737b88..cad07ce24 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ npm install -g backstopjs - [Approving Changes](#approving-changes) - [Using BackstopJS](#using-backstopjs) - [Scenario Properties](#scenario-properties) + - [Global Scenario Properties](#global-scenario-properties) - [Advanced Scenarios](#advanced-scenarios) - [Developing, Bug Fixing, Contributing...](#developing-bug-fixing-contributing) - [Troubleshooting](#troubleshooting) @@ -168,7 +169,7 @@ Pass a `--filter=` argument to promote only the test captu ### Scenario Properties -Scenario properties are described throughout this document and **processed sequentially in the following order...** +Scenario properties, [which may be global](#global-scenario-properties), are described throughout this document and **processed sequentially in the following order...** | Property | Description | |--------------------------|--------------------------------------------------------------------------------------------------------------------------------| @@ -198,6 +199,88 @@ Scenario properties are described throughout this document and **processed seque | `viewports` | An array of screen size objects your DOM will be tested against. This configuration will override the viewports property assigned at the config root. | | `gotoParameters` | An array of settings passed to page.goto(url, parameters) function. | +### Global Scenario Properties + +One may opt to include any of the above properties at the "global" level, in the `scenarioDefaults` configuration object. + +
+ Expand Example + + ```json + { + "id": "backstop_playwright", + "viewports": [ + { + "label": "phone", + "width": 320, + "height": 480 + }, + { + "label": "tablet", + "width": 1024, + "height": 768 + } + ], + "onBeforeScript": "playwright/onBefore.js", + "onReadyScript": "playwright/onReady.js", + "scenarioDefaults": { + "cookiePath": "backstop_data/engine_scripts/cookies.json", + "url": "https://garris.github.io/BackstopJS/", + "readySelector": "", + "delay": 0, + "hideSelectors": [".getItBlock"], + "removeSelectors": [".logoBlock"], + "hoverSelector": "", + "clickSelector": "", + "postInteractionWait": 1000, + "selectors": [], + "selectorExpansion": true, + "misMatchThreshold" : 0.1, + "requireSameDimensions": true + }, + "scenarios": [ + { + "label": "BackstopJS Homepage", + "cookiePath": "backstop_data/engine_scripts/cookies.json", + "url": "https://garris.github.io/BackstopJS/", + "referenceUrl": "", + "readyEvent": "", + "readySelector": "", + "delay": 0, + "hoverSelector": "", + "clickSelector": "", + "selectors": [], + "selectorExpansion": true, + "misMatchThreshold" : 0.1, + "requireSameDimensions": true + } + ], + "paths": { + "bitmaps_reference": "backstop_data/bitmaps_reference", + "bitmaps_test": "backstop_data/bitmaps_test", + "engine_scripts": "backstop_data/engine_scripts", + "html_report": "backstop_data/html_report", + "ci_report": "backstop_data/ci_report" + }, + "report": ["browser"], + "engine": "playwright", + "engineOptions": { + "args": ["--no-sandbox"] + }, + "asyncCaptureLimit": 5, + "asyncCompareLimit": 50, + "debug": false, + "debugWindow": false, + "archiveReport": true, + "scenarioLogsInReports": true + } + ``` + +
+ +> [!IMPORTANT] +> Global configuration is overridden at the `scenario` level. A scenario with `selectors: []` set as an empty array will yield zero selectors. E.g. `scenarioDefaults.selectors: [".fancy", ".global", ".classes"]` will be set to `[]`, as `scenario.selectors` takes precedence. + ### Advanced Scenarios #### Testing Click and Hover Interactions diff --git a/core/util/engineTools.js b/core/util/engineTools.js index 2894679a9..47c2e7305 100644 --- a/core/util/engineTools.js +++ b/core/util/engineTools.js @@ -1,7 +1,12 @@ +/** + * @description Retrieves the mismatch threshold based on the given scenario and configuration. + * + * @param {Object} scenario - The scenario object, which may contain a misMatchThresholdconfig property. + * @param {Object} config - The configuration object, which includes misMatchThreshold and defaultMisMatchThreshold properties. + * @returns {number} The mismatch threshold value. + */ function getMisMatchThreshHold (scenario, config) { - if (typeof scenario.misMatchThreshold !== 'undefined') { return scenario.misMatchThreshold; } - if (typeof config.misMatchThreshold !== 'undefined') { return config.misMatchThreshold; } - return config.defaultMisMatchThreshold; + return scenario?.misMatchThresholdconfig || config?.misMatchThreshold || config?.defaultMisMatchThreshold || 0.1; } function ensureFileSuffix (filename, suffix) { @@ -30,14 +35,15 @@ function genHash (str) { return hash.toString().replace(/^-/, 0); } +/** + * @description Determines whether the same dimensions are required based on the given scenario and configuration. + * + * @param {Object} scenario - The scenario object, which may contain a requireSameDimensions property. + * @param {Object} config - The configuration object, which includes requireSameDimensions and defaultMisMatchThreshold properties. + * @returns {boolean} True if the same dimensions are required, otherwise false. + */ function getRequireSameDimensions (scenario, config) { - if (scenario.requireSameDimensions !== undefined) { - return scenario.requireSameDimensions; - } else if (config.requireSameDimensions !== undefined) { - return config.requireSameDimensions; - } else { - return config.defaultRequireSameDimensions; - } + return scenario?.requireSameDimensions || config?.requireSameDimensions || config?.defaultMisMatchThreshold || true; } function getSelectorName (selector) { diff --git a/core/util/runPlaywright.js b/core/util/runPlaywright.js index ed9320aa2..3aaac1e71 100644 --- a/core/util/runPlaywright.js +++ b/core/util/runPlaywright.js @@ -104,11 +104,7 @@ module.exports.createPlaywrightBrowser = async function (config) { return await playwright[browserChoice].launch(playwrightArgs); }; -module.exports.runPlaywright = function (args) { - const scenario = args.scenario; - const viewport = args.viewport; - const config = args.config; - const browser = args._playwrightBrowser; +module.exports.runPlaywright = function ({ scenario, viewport, config, _playwrightBrowser: browser }) { const scenarioLabelSafe = engineTools.makeSafe(scenario.label); const variantOrScenarioLabelSafe = scenario._parent ? engineTools.makeSafe(scenario._parent.label) : scenarioLabelSafe; @@ -127,7 +123,18 @@ module.exports.disposePlaywrightBrowser = async function (browser) { }; async function processScenarioView (scenario, variantOrScenarioLabelSafe, scenarioLabelSafe, viewport, config, browser) { - const { engineOptions } = config; + const { engineOptions, scenarioDefaults = {} } = config; + + /** + * @type {Object} + * @description Spread `scenarioDefaults` into the scenario. + * @default `scenario` + */ + scenario = { + ...scenarioDefaults, + ...scenario + }; + if (!config.paths) { config.paths = {}; } @@ -220,7 +227,6 @@ async function processScenarioView (scenario, variantOrScenarioLabelSafe, scenar timeout: readyTimeout }); } - // // --- DELAY --- if (scenario.delay > 0) { diff --git a/core/util/runPuppet.js b/core/util/runPuppet.js index 2a8e93bef..5b3c7491f 100644 --- a/core/util/runPuppet.js +++ b/core/util/runPuppet.js @@ -21,10 +21,7 @@ const DOCUMENT_SELECTOR = 'document'; const NOCLIP_SELECTOR = 'body:noclip'; const VIEWPORT_SELECTOR = 'viewport'; -module.exports = function (args) { - const scenario = args.scenario; - const viewport = args.viewport; - const config = args.config; +module.exports = function ({ scenario, viewport, config }) { const scenarioLabelSafe = engineTools.makeSafe(scenario.label); const variantOrScenarioLabelSafe = scenario._parent ? engineTools.makeSafe(scenario._parent.label) : scenarioLabelSafe; @@ -53,6 +50,18 @@ function loggerAction (action, color, message, ...rest) { } async function processScenarioView (scenario, variantOrScenarioLabelSafe, scenarioLabelSafe, viewport, config, logger) { + const { scenarioDefaults = {} } = config; + + /** + * @type {Object} + * @description Spread `scenarioDefaults` into the scenario. + * @default `scenario` + */ + scenario = { + ...scenarioDefaults, + ...scenario + }; + if (!config.paths) { config.paths = {}; }