UNPKG

visreg-test

Version:

A visual regression testing solution that offers an easy setup with simple yet powerful customisation options, wrapped up in a convenient CLI runner to make assessing and accepting/rejecting diffs a breeze.

231 lines (230 loc) 10.6 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runTest = void 0; require("../support/commands"); const command_1 = require("cypress-image-snapshot-fork-2/command"); const local_cypress_1 = require("local-cypress"); (0, command_1.addMatchImageSnapshotCommand)(); local_cypress_1.Cypress.on('uncaught:exception', () => { /** * returning false here prevents Cypress from failing the test if an error * in the console of the website which is being tested is found */ return false; }); const parseSnapConfigFromName = (name, endpoints) => { const divider = '@'; const nameParts = name.split(divider); const title = nameParts[0].trim(); const sizeRaw = nameParts[1].trim().replace('.diff.png', ''); let viewportSize = sizeRaw; if (sizeRaw.includes(',')) { viewportSize = sizeRaw.split(',').map(dimension => parseInt(dimension)); } const endpoint = endpoints.find(endpoint => endpoint.title === title); if (!endpoint) return null; return { path: endpoint.path, viewportSize, title, }; }; const getFullUrl = (props, path) => { const { baseUrl, formatUrl } = props; const sanitizedBaseUrl = baseUrl.replace(/\/$/, ''); const formattedUrl = formatUrl && formatUrl(path); const fullUrl = formattedUrl && typeof formattedUrl === 'string' && formattedUrl !== '' ? formattedUrl : `${sanitizedBaseUrl}${path}`; return fullUrl; }; const takeSnaps = (props, viewport, endpoint, noSnap) => { const { onBeforeVisit: beforeVisitGlobal, onVisit: onVisitGlobal, onAfterVisit: afterVisitGlobal, } = props; const { path, title, elementToMatch, excludeFromTest, onBeforeVisit, onVisit, onAfterVisit, requestOptions, visitOptions } = endpoint, endpointOptions = __rest(endpoint, ["path", "title", "elementToMatch", "excludeFromTest", "onBeforeVisit", "onVisit", "onAfterVisit", "requestOptions", "visitOptions"]); const snapshotSettings = Object.assign(Object.assign(Object.assign({}, local_cypress_1.Cypress.env('NON_OVERRIDABLE_SETTINGS')), local_cypress_1.Cypress.env('SNAPSHOT_SETTINGS')), endpointOptions); const visitOptionsAggregate = Object.assign(Object.assign({}, local_cypress_1.Cypress.env('VISIT_SETTINGS')), visitOptions); const requestOptionsAggregate = Object.assign(Object.assign({}, local_cypress_1.Cypress.env('REQUEST_SETTINGS')), requestOptions); const snapName = `${title} @ ${viewport}`; const fullUrl = getFullUrl(props, path); const context = { endpoint, viewport, cypress: local_cypress_1.Cypress, fullUrl, fullPageCapture: !(elementToMatch || snapshotSettings.capture === 'viewport'), requestOptions: requestOptionsAggregate, visitOptions: visitOptionsAggregate, }; // Always have a callable global hook, to avoid errors when not defined const globalBeforeVisit = beforeVisitGlobal || (() => { }); const globalOnVisit = onVisitGlobal || (() => { }); const globalAfterVisit = afterVisitGlobal || (() => { }); (0, local_cypress_1.it)(snapName, () => { if (excludeFromTest && excludeFromTest(local_cypress_1.cy, context)) return; // Endpoint hooks take precedence over global hooks (gets it passed as a parameter if user wants to call it) onBeforeVisit ? onBeforeVisit(local_cypress_1.cy, context, globalBeforeVisit) : globalBeforeVisit(local_cypress_1.cy, context); local_cypress_1.cy.prepareForCapture({ context, onVisit, globalOnVisit, }); if (!noSnap) { const cyTarget = elementToMatch ? local_cypress_1.cy.get(elementToMatch) : local_cypress_1.cy; cyTarget.matchImageSnapshot(snapName, snapshotSettings); } // Endpoint hooks take precedence over global hooks (gets it passed as a parameter if user wants to call it) onAfterVisit ? onAfterVisit(local_cypress_1.cy, context, globalAfterVisit) : globalAfterVisit(local_cypress_1.cy, context); }); }; const limitMessage = (targetEndpointTitles, targetViewports) => { const eps = targetEndpointTitles && targetEndpointTitles.length > 0 ? targetEndpointTitles.join(', ') : ''; const vps = targetViewports && targetViewports.length > 0 ? targetViewports.join(', ') : ''; const epText = eps ? `"${eps}" ` : ''; const vpText = vps ? `@ ${vps}` : ''; const limitText = epText || vpText ? ` - limiting test to ${epText}${vpText}` : ''; return limitText; }; const allowedViewports = (registeredViewports, targetViewports) => { if (!targetViewports || targetViewports.length === 0) { return registeredViewports; } const matches = registeredViewports.filter(vp => { return targetViewports.find(targetVp => { if (typeof vp === 'string' && typeof targetVp === 'string') { return vp === targetVp; } if (Array.isArray(vp) && Array.isArray(targetVp)) { return vp.toString() === targetVp.toString(); } return false; }); }); if (matches.length === 0) return []; return matches; }; const allowedEndpoints = (endpoints, targetEndpointTitles) => { if (!targetEndpointTitles || targetEndpointTitles.length === 0) { return endpoints; } const matches = endpoints.filter(ep => { return targetEndpointTitles.find(targetTitle => matchingEndpointTitle(ep.title, targetTitle)); }); if (matches.length === 0) return []; return matches; }; const matchingEndpointTitle = (endpoint1, endpoint2) => { if (!endpoint1 || !endpoint2) return ''; const normalized1 = endpoint1.toLowerCase().replace(/ /g, '-'); const normalized2 = endpoint2.toLowerCase().replace(/ /g, '-'); return normalized1 === normalized2; }; const matchingViewport = (viewport1, viewport2) => { if (!viewport1 || !viewport2) return ''; const normalized1 = JSON.stringify(viewport1); const normalized2 = JSON.stringify(viewport2); return normalized1 === normalized2; }; const runTest = (props) => { var _a, _b; const defaultViewports = [ 'iphone-6', 'ipad-2', [1920, 1080], ]; const testSettings = Object.assign({}, JSON.parse(Buffer.from(local_cypress_1.Cypress.env('TEST_SETTINGS'), 'base64').toString('utf8'))); const { suite, targetViewports, targetEndpointTitles, testType, diffList, noSnap, } = testSettings; const { suiteName = (_a = props.suiteName) !== null && _a !== void 0 ? _a : suite, viewports: registeredViewports = (_b = props.viewports) !== null && _b !== void 0 ? _b : defaultViewports, endpoints: registeredEndpoints, } = props; const limitText = limitMessage(targetEndpointTitles, targetViewports); const viewportsToTest = allowedViewports(registeredViewports, targetViewports); const endpointsToTest = allowedEndpoints(registeredEndpoints, targetEndpointTitles); const testAgenda = { message: 'visreg-test-agenda', viewportsToTest, endpointsToTest, }; (0, local_cypress_1.before)(() => { local_cypress_1.cy.log(JSON.stringify(testAgenda)); }); beforeEach(() => { if (local_cypress_1.Cypress.env('HEADED') || local_cypress_1.Cypress.env('CLI')) { return; } local_cypress_1.cy.request('http://localhost:3000/api/test/terminate-json') .then((response) => { if (response.body.terminate) { throw new Error('Terminating tests'); } }); }); (0, local_cypress_1.describe)(`Suite: "${suiteName}"`, () => { if (testType === 'lab') { (0, local_cypress_1.describe)('Lab mode' + limitText, () => { /** * Lab tests require a viewport and endpoint to be specified. * Viewport can be anything in lab mode, but endpoint must still be a valid endpoint. */ const validEndpoint = endpointsToTest.find(ep => matchingEndpointTitle(ep.title, targetEndpointTitles === null || targetEndpointTitles === void 0 ? void 0 : targetEndpointTitles[0])); if (!targetViewports || !validEndpoint) return; takeSnaps(props, targetViewports === null || targetViewports === void 0 ? void 0 : targetViewports[0], validEndpoint, noSnap); }); } if (testType === 'full-test') { (0, local_cypress_1.describe)('Full test' + limitText, () => { viewportsToTest.forEach((vp) => { endpointsToTest.forEach((ep) => { takeSnaps(props, vp, ep, noSnap); }); }); }); } if (testType === 'targetted') { (0, local_cypress_1.describe)('Targetted' + limitText, () => { viewportsToTest.forEach((vp) => { endpointsToTest.forEach((ep) => { takeSnaps(props, vp, ep, noSnap); }); }); }); } if (testType === 'diffs-only') { (0, local_cypress_1.describe)('Diffs only' + limitText, () => { if (!diffList) return; diffList.forEach(diffSnapName => { const config = parseSnapConfigFromName(diffSnapName, endpointsToTest); if (!config) return; const { viewportSize, title } = config; const viewport = viewportsToTest.find(vp => matchingViewport(vp, viewportSize)); const endpoint = endpointsToTest.find(ep => matchingEndpointTitle(ep.title, title)); if (viewport && endpoint) { takeSnaps(props, viewportSize, endpoint, noSnap); } }); }); } }); }; exports.runTest = runTest;