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
JavaScript
;
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;