{"id":15285260,"url":"https://github.com/jparkerweb/bivariate","last_synced_at":"2025-04-12T23:55:25.567Z","repository":{"id":50839567,"uuid":"62421939","full_name":"jparkerweb/Bivariate","owner":"jparkerweb","description":"An opinionated interface for writing, running, and saving BackstopJS tests","archived":false,"fork":false,"pushed_at":"2023-05-25T14:33:06.000Z","size":6924,"stargazers_count":21,"open_issues_count":5,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-12T23:55:11.205Z","etag":null,"topics":["backstopjs","browser-automation","css","javascript","layout","regression-testing","visualtesting"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jparkerweb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-07-01T22:01:01.000Z","updated_at":"2025-03-29T01:59:53.000Z","dependencies_parsed_at":"2024-08-23T14:32:33.330Z","dependency_job_id":null,"html_url":"https://github.com/jparkerweb/Bivariate","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jparkerweb%2FBivariate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jparkerweb%2FBivariate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jparkerweb%2FBivariate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jparkerweb%2FBivariate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jparkerweb","download_url":"https://codeload.github.com/jparkerweb/Bivariate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248647232,"owners_count":21139083,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backstopjs","browser-automation","css","javascript","layout","regression-testing","visualtesting"],"created_at":"2024-09-30T15:03:57.114Z","updated_at":"2025-04-12T23:55:25.533Z","avatar_url":"https://github.com/jparkerweb.png","language":"JavaScript","readme":"# ![Bivariate](bivariate.png)\n\n***An opinionated interface for writing, running, and saving BackstopJS tests***\n  \n## Goal\n\n**Bivariate**'s goal is to allow for an approachable Visual Regression Testing suite that can be organized to accommodate small and large projects without overwhelming complexity.\n  \n![Bivariate App](./documentation/menu.gif)  \n\nThis goal is achieved by enforcing an opinionated grouping structure, providing a method to easily write tests via manageable object files, as well as allowing for all of BackstopJS's commands to be run from an interface.\n  \n## Installation\n\n**Bivariate** runs in [Node](https://nodejs.org).\n\n* Install [NodeJS](https://nodejs.org)\n\n* Install the Latest version of Bivariate via NPM.  \nIt is *recommended* to install Bivariate *globally*, but it can run locally if required:  \n  \n  global install (*recommended*):  \n  `npm install bivariate -g`\n\n  local install:  \n  `npm install bivariate`\n\n* Ensure you have version 59 or greater of [Chrome](https://www.google.com/chrome/browser/) installed.\n  Bivariate utilizes headless Chrome which started shipping in Chrome v59\n\n* From your project directory, run Bivariate:  \n  if installed globally:  \n  `bivariate`\n\n  if only installed locally:  \n  `npx bivariate`\n  \n* Generate `bivariate_data`:  \n  If Bivariate doesn't detect any existing Bivarte tests it will ask you would like to generate the starting configuration files.  \n\n  ![Bivariate App](./documentation/first-run.gif)  \n  \n## Folder Structure\n\n*All tests, scripts, and configuration files are stored in the `bivariate_data` parent folder.*\n\n`bivariate_data`  \n|  \n+---- `test_scripts` holds user defined configuration and tests used to instruct BackstopJS  \n|  \n+---- `engine_scripts` holds user defined Puppeteer scripts for interacting with the Chrome DOM before saving a screen shot  \n|  \n+---- `bitmaps_reference_archive` holds archived *references* that can be restored and tested against  \n  \n### test_scripts\n\nOut of the box, BackstopJS gets all of its config and test data from a single JSON file, which isn't very maintainable over time.  Luckily, **Bivariate** takes advantage of Node's module system to break this all apart and just return what is needed (*a simple array of objects*).\n\n#### Configuration files\n\nAll configuration files are prefixed with a double underscore: \\_\\_  \n\n##### \\_\\_config-baseURLs.js\n\nholds the *base* URLs for all References and Tests to be run.  \n\n```js\n...\n  // do not use a trailing slash in the base URLs\n  theURLS.baseURL = \"http://your-base-url\";\n  theURLS.baseRefURL = \"http://your-base-reference-url\";\n...\n```\n\n##### \\_\\_config-common.js  \n\nset of *common* config values (rendering engine, ports, etc.) that shouldn't need to be adjusted in most cases.\n\n##### \\_\\_config-viewports.js  \n\n configure any number of *viewports* to test against (this can include any number of defined screen resolutions).\n  \n#### Individual Tests  \n\nAll individual tests are prefixed with a single underscore: \\_  \n\nUse the example tests as a template to create your own.  Tests are easy to setup and for the most part only require you to fill out the value for a few variables:\n\n* label\n* route\n* selectors\n\n`_example-test--home.js` :\n\n````js\n// -------------------\n// - test definition -\n// -------------------\n\n// * tests should be saved as: '_test-name.js'\n//   if you have a lot of tests you can store\n//   related tests in named subdirectories\n//   for better organization\n\nvar label = \"Example Test - Home Page\"\nvar route = \"/index.html\"\nvar readySelector = \"\"\nvar hideSelectors = []\nvar removeSelectors = []\nvar selectors = [ \"document\", \"h1\", \".hero\", \".nav\", \".body-content\" ]\nlet hoverSelector = null\nlet hoverSelectors = []\nlet clickSelector = null\nlet clickSelectors = []\nlet postInteractionWait = 100\nlet scrollToSelector = null\nlet delay = 300\nvar onBeforeScript = 'onBefore-Example.js'\nvar onReadyScript = 'onReady-Example.js'\nlet viewports = []\n\n// ---------\n// - label -\n// ---------\n// [required]\n// used on reports and screen shot file names (should be unique between tests)\n\n// ---------\n// - route -\n// ---------\n// [required]\n// the route for this test (start with a \"/\")\n\n// -----------------\n// - readySelector -\n// -----------------\n// Selector to look for before continuing\n\n// -----------------\n// - hideSelectors -\n// -----------------\n// hide elements from view by changing its \"visibility\" to \"hidden\"\n\n// -------------------\n// - removeSelectors -\n// -------------------\n// remove elements from the DOM before screen capture\n\n// -------------\n// - selectors - (array or strings)\n// -------------\n// selectors for elements to be \"captured\" (CSS selector syntax)\n\n// -----------------\n// - hoverSelector -\n// -----------------\n// Move the pointer over the specified DOM element prior to screen shot.\n\n// ------------------\n// - hoverSelectors - (array)\n// ------------------\n// *Puppeteer only* takes array of selectors -- simulates multiple\n// sequential hover interactions.\n\n// -----------------\n// - clickSelector -\n// -----------------\n// Click the specified DOM element prior to screen shot.\n\n// ------------------\n// - clickSelectors - (array)\n// ------------------\n// *Puppeteer only* takes array of selectors -- simulates\n// multiple sequential click interactions.\n\n// -----------------------\n// - postInteractionWait -\n// -----------------------\n// Wait for a selector after interacting with hoverSelector or clickSelector\n// (optionally accepts wait time in ms. Idea for use with a click or hover\n// element transition. available with default onReadyScript)\n\n// --------------------\n// - scrollToSelector -\n// --------------------\n// Scrolls the specified DOM element into view prior to screen shot\n// (available with default onReadyScript)\n\n// ---------\n// - delay -\n// ---------\n// Wait for x milliseconds\n\n// ------------------\n// - onBeforeScript -\n// ------------------\n// Runs before each scenario\n// use for setting cookies or other env state\n// (.js suffix is optional / looks for file in \"engine_scripts\" dir)\n\n// -----------------\n// - onReadyScript -\n// -----------------\n// Runs after onReady event on all scenarios\n// use for simulating interactions\n// (.js suffix is optional / looks for file in \"engine_scripts\" dir)\n\n// -------------\n// - viewports -\n// -------------\n// overwrite array of viewports\n// example:\n// let viewports = [\n//    {\n//        name: \"huge-vertical-space\",\n//        width: 1920,\n//        height: 4500\n//    }\n// ]\n  \n\n// -------------------------------------------------------------------\n// - advanced options can be overwritten in the options object below -\n// -------------------------------------------------------------------\nmodule.exports = function(baseURLs) {\n\tvar url = (baseURLs.baseURL + route)\n\tvar referenceUrl = baseURLs.baseRefURL === null ? null : (baseURLs.baseRefURL + route)\n\tvar options = {\n\t\t\"label\": label,\t\t\t\t\t\t\t\t\t// [required] Tag saved with your reference images\n\t\t\"url\": url,\t\t\t\t\t\t\t\t\t\t// [required] Tag saved with your reference images\n\t\t\"referenceUrl\": referenceUrl,\t\t\t\t\t// Specify a different state or environment when creating reference.\n\t\t\"readySelector\": readySelector,\t\t\t\t\t// Wait until this selector exists before continuing.\n\t\t\"hideSelectors\": hideSelectors,\t\t\t\t\t// Array of selectors set to visibility: hidden\n\t\t\"removeSelectors\": removeSelectors,\t\t\t\t// Array of selectors set to display: none\n\t\t\"selectors\": selectors,\t\t\t\t\t\t\t// Array of selectors to capture. Defaults to document if omitted. Use \"viewport\" to capture the viewport size.\n\t\t\"selectorExpansion\": true,\t\t\t\t\t\t// If you want BackstopJS to find and take screenshots of all matching selector instances then set to true.\n\t\t\"readyEvent\": null,\t\t\t\t\t\t\t\t// Wait until this string has been logged to the console.\n\t\t\"hoverSelector\": hoverSelector,\t\t\t\t\t// Move the pointer over the specified DOM element prior to screen shot.\n\t\t\"hoverSelectors\": hoverSelectors,\t\t\t\t// *Puppeteer only* takes array of selectors -- simulates multiple sequential hover interactions.\n\t\t\"clickSelector\": clickSelector,\t\t\t\t\t// Click the specified DOM element prior to screen shot.\n\t\t\"clickSelectors\": clickSelectors,\t\t\t\t// *Puppeteer only* takes array of selectors -- simulates multiple sequential click interactions.\n\t\t\"postInteractionWait\": postInteractionWait,\t\t// Wait for a selector after interacting with hoverSelector or clickSelector (optionally accepts wait time in ms. Idea for use with a click or hover element transition. available with default onReadyScript)\n\t\t\"scrollToSelector\": scrollToSelector,\t\t\t// Scrolls the specified DOM element into view prior to screen shot (available with default onReadyScript)\n\t\t\"delay\": delay,\t\t\t\t\t\t\t\t\t// Wait for x milliseconds\n\t\t\"misMatchThreshold\": 0.1,\t\t\t\t\t\t// Percentage of different pixels allowed to pass test\n\t\t\"onBeforeScript\": onBeforeScript,\t\t\t\t// Used to set up browser state e.g. cookies.\n\t\t\"onReadyScript\": onReadyScript,\t\t\t\t\t// After the above conditions are met -- use this script to modify UI state prior to screen shots e.g. hovers, clicks etc.\n\t\t\"requireSameDimensions\": false,\t\t\t\t\t// If set to true -- any change in selector size will trigger a test failure.\n\t\t\"viewports\": viewports\t\t\t\t\t\t\t// An array of screen size objects your DOM will be tested against. This configuration will override the viewports property assigned at the config root.\n\t}\n\n\tif (baseURLs.baseRefURL === null) {\n\t\tdelete options.referenceUrl\n\t}\n\n\treturn options\n}\n````\n\n#### Test Groups  \n\nBivariate presents and runs tests using a grouping concept.  A `test group` is a collection of `tests` that are run together.  A test group is a .js file that does not start with any underscores.  \n\nUse the provide file `example-test-group.js` as a template for your own.  Note that all that is required is to fill in the `Scenarios` section to include which tests you want run.\n\n`example-test-group.js` :\n\n````js\n// ----------------\n// -- Test Group --\n// ----------------\n\nlet mixIn = require(\"./../libs/mout-mixin/mixIn\");\nlet testGroup = __filename.slice(__dirname.length + 1, -3);\nlet configCommon = require('./__config-common')(testGroup);\nlet baseURLs = require(\"./__config-baseURLs\");\n\n\nmodule.exports = mixIn(\n    {\n        // ---------------\n        // -- Scenarios --\n        // ---------------\n        \"scenarios\": [\n            require('./_example-site--home')(baseURLs),\n            require('./_example-site--paints')(baseURLs)\n        ],\n    },\n        configCommon\n);\n````\n  \n### engine_scripts\n  \nengine scripts are used to interact with your web pages using the `before` and `on ready` events.  Each test you create has an optional parameter of `onBeforeScript` \u0026 `onReadyScript`.  These can simply point to script files in the 'engine_scripts' directory.  The two example scripts found in the `engine_scripts` directory should be self explanatory (`onBefore-Example.js` \u0026 `onReady-Example.js`).  In addition you can refer to the [Puppeteer Docs](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md) for more advanced examples.\n  \n### bitmaps_reference_archive\n  \nThe `bitmaps_reference_archive` folder holds archived `references` which can be created, archived, and restored using the **Bivariate** app.\n  \n----\n\n## App\n\nExample run of the test scripts generated by Bivariate:  \n\n![Bivariate App Example Run](./documentation/example-run.gif)  \n  \nFor more info, reference `example-site` README for a walk through example of **Bivariate** in action.  \n\n### [Example Site Docs](./example-site/README.md)\n\nDetailed docs for what the `reference`, `test`, and `approve` commands do under the hood can be found on the BackstopJS Github page: [BackstopJS](https://github.com/garris/BackstopJS).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjparkerweb%2Fbivariate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjparkerweb%2Fbivariate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjparkerweb%2Fbivariate/lists"}