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.

707 lines (706 loc) 29.2 kB
#!/usr/bin/env node "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.startWebTest = exports.getDiffsForWeb = exports.getVersion = void 0; const fs = require("fs"); const path = require("path"); const child_process_1 = require("child_process"); const readline = require("readline"); const cli_1 = require("./cli"); const server_1 = require("./server"); const diff_assessment_web_1 = require("./diff-assessment-web"); const utils_1 = require("./utils"); const diff_assessment_terminal_1 = require("./diff-assessment-terminal"); const summarize_1 = require("./summarize"); const defaultBrowser = 'electron'; const configPath = path.join(utils_1.projectRoot, 'visreg.config.json'); let visregConfig = {}; if ((0, utils_1.pathExists)(configPath)) { const fileContent = fs.readFileSync(configPath, 'utf-8'); try { visregConfig = JSON.parse(fileContent); } catch (e) { } } let failed = false; let userTerminatedTest = false; let cypressSummary = {}; let testAgenda = []; const endpointTestResults = { passing: [], failing: [], skipped: [], unchanged: [], }; const typesList = [ { name: 'Full', slug: 'full-test', description: 'Run a full visual regression test of all endpoints and viewports (previous diffs are deleted)' }, { name: 'Retest diffs only', slug: 'diffs-only', description: 'Run only the tests which failed in the last run' }, { name: 'Targetted', slug: 'targetted', description: 'Run a test for a specific endpoint and/or viewport' }, { name: 'Assess diffs', slug: 'assess-existing-diffs', description: 'Assess the existing diffs (no tests are run)' }, { name: 'Lab', slug: 'lab', description: 'Run the tests in lab mode' }, ]; const getVersion = () => { // Read the "version" field from package.json and print it out: const packageJsonPath = path.join(__dirname, '..', 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8') || '{}'); return packageJson.version; }; exports.getVersion = getVersion; // Print config path if ((0, utils_1.pathExists)(configPath)) { (0, utils_1.printColorText)(`\nProject config: ${configPath}`, '2'); } // Print header (0, utils_1.printColorText)(`\n _ _ __ ____ ____ ____ ___ \n/ )( \\( )/ ___)( _ \\( __)/ __)\n\\ \\/ / )( \\___ \\ ) / ) _)( (_ \\ \n \\__/ (__)(____/(__\\_)(____)\\___/ \x1b[2mv${(0, exports.getVersion)()}\x1b[0m\n`, '36;1'); const promptForEndpointTitle = () => __awaiter(void 0, void 0, void 0, function* () { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); yield new Promise((resolve) => { if (cli_1.programChoices.targetEndpointTitles.length) resolve(); rl.question('Enter endpoint title (replace spaces with "-"): ', (endpointTitle) => { cli_1.programChoices.targetEndpointTitles = [endpointTitle]; resolve(); }); }); rl.close(); }); const promptForViewport = () => __awaiter(void 0, void 0, void 0, function* () { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); yield new Promise((resolve) => { if (cli_1.programChoices.targetViewports.length) resolve(); rl.question('Enter viewport (e.g. 1920,1080, or iphone-6): ', (viewport) => { const parsedViewport = (0, utils_1.parseViewport)(viewport) || []; cli_1.programChoices.targetViewports = [parsedViewport]; resolve(); }); }); rl.close(); }); const startLabMode = (programChoices) => __awaiter(void 0, void 0, void 0, function* () { const { targetEndpointTitles, targetViewports } = programChoices; const requiredTargets = targetEndpointTitles.length && targetViewports.length; if (!requiredTargets) { (0, utils_1.printColorText)('Lab mode requires both endpoint title and viewport to be specified\n', '2'); yield promptForEndpointTitle(); yield promptForViewport(); if (!requiredTargets) { (0, utils_1.printColorText)('Lab mode requires both endpoint title and viewport to be specified\n', '31'); return; } } yield runCypressTest(); if (programChoices.gui) { process.exit(); } (0, utils_1.printColorText)(`Lab mode summary\n`, '4'); (0, utils_1.printColorText)(`Duration: ${cypressSummary.duration} seconds`, '2'); (0, utils_1.printColorText)('GUI: off', '2'); (0, utils_1.printColorText)(`Snapshots: ${programChoices.snap ? 'on' : 'off'}`, '2'); (0, utils_1.printColorText)(`\n(Tip: use the GUI for hot-reloading!)\n`, '2'); process.exit(); }); const main = () => __awaiter(void 0, void 0, void 0, function* () { yield selectSuite(); yield selectType(); const { testType } = cli_1.programChoices; if (testType === 'lab') { startLabMode(cli_1.programChoices); return; } if (testType === 'assess-existing-diffs') { assessExistingDiffImages((0, utils_1.getAllDiffingFiles)()); return; } if (testType === 'diffs-only') { exitIfNoDIffs(); } const allCurrentDiffs = createTemporaryDiffList(); backupDiffs(); backupReceived(); const testResultDiffs = yield runCypressTest(allCurrentDiffs); if (testResultDiffs) { assessExistingDiffImages(testResultDiffs); } return; }); const selectSuite = () => __awaiter(void 0, void 0, void 0, function* () { const suites = (0, utils_1.getSuites)(); if (suites.length === 0) { (0, utils_1.printColorText)('No test suites found - see README', '31'); process.exit(1); } if (suites.length === 1) { cli_1.programChoices.suite = suites[0]; return; } if (cli_1.programChoices.suite && suites.includes(cli_1.programChoices.suite)) { return; } console.log('Select suite:\n'); suites.forEach((suite, index) => { console.log(`${index + 1} ${suite}`); }); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); yield new Promise((resolve) => { rl.question('\nEnter number of suite: ', (targetNum) => { cli_1.programChoices.suite = suites[parseInt(targetNum) - 1]; resolve(); }); }); rl.close(); }); const selectType = () => __awaiter(void 0, void 0, void 0, function* () { const specifiedType = typesList.find(type => type.slug === cli_1.programChoices.testType); if (specifiedType) { return; } console.log('\nSelect type of test:\n'); typesList.forEach((type, index) => { if (cli_1.programChoices.containerized) { if (type.slug === 'lab' || type.slug === 'assess-existing-diffs') return; } (0, utils_1.printColorText)(`${index + 1} ${type.name}\x1b[2m - ${type.description}\x1b[0m`, '0'); }); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); yield new Promise((resolve) => { rl.question('\nEnter number of type: ', (id) => { const slug = typesList[parseInt(id) - 1].slug; cli_1.programChoices.testType = slug; resolve(); }); }); rl.close(); }); const prepareConfig = (diffList) => { const suiteRoot = path.join(utils_1.suitesDirectory, cli_1.programChoices.suite || ''); const suiteConfigPath = path.join(suiteRoot, 'visreg.config.json'); let suiteConfig = {}; if ((0, utils_1.pathExists)(suiteConfigPath)) { const fileContent = fs.readFileSync(suiteConfigPath, 'utf-8'); try { suiteConfig = JSON.parse(fileContent); } catch (e) { } } (0, utils_1.pathExists)(suiteConfigPath) && (0, utils_1.printColorText)(`\nSuite config: ${suiteConfigPath}`, '2'); Object.assign(visregConfig, suiteConfig); const { screenshotOptions, comparisonOptions, requestOptions, visitOptions, maxViewport, browser, } = visregConfig; const snapshotSettings = Object.assign(Object.assign({ failureThreshold: 0.001, failureThresholdType: 'percent', capture: 'fullPage', disableTimersAndAnimations: false }, screenshotOptions), comparisonOptions); const visitSettings = Object.assign({ scrollDuration: 750, devicePixelRatio: 1, waitForNetworkIdle: true }, visitOptions); const requestSettings = Object.assign({}, requestOptions); process.env.CYPRESS_VISREG_SETTINGS = JSON.stringify({ maxViewport }); process.env.CYPRESS_SNAPSHOT_SETTINGS = JSON.stringify(snapshotSettings); process.env.CYPRESS_VISIT_SETTINGS = JSON.stringify(visitSettings); process.env.CYPRESS_REQUEST_SETTINGS = JSON.stringify(requestSettings); process.env.SEND_SUITE_CONF = 'false'; const specPath = (0, utils_1.getSuiteDirOrFail)(cli_1.programChoices.suite); const testSettings = { testType: cli_1.programChoices.testType, suite: cli_1.programChoices.suite, diffList: diffList || [], targetViewports: cli_1.programChoices.targetViewports, targetEndpointTitles: cli_1.programChoices.targetEndpointTitles, noSnap: !cli_1.programChoices.snap, }; const labMode = cli_1.programChoices.testType === 'lab'; const nonOverridableSettings = { suitesDirectory: utils_1.suitesDirectory, useRelativeSnapshotsDir: true, storeReceivedOnFailure: true, snapFilenameExtension: labMode ? '.lab' : '.base', customSnapshotsDir: labMode ? 'lab' : '', }; process.env.CYPRESS_failOnSnapshotDiff = 'false'; process.env.CYPRESS_updateSnapshots = labMode ? 'true' : 'false'; process.env.CYPRESS_TEST_SETTINGS = Buffer.from(JSON.stringify(testSettings)).toString('base64'); process.env.CYPRESS_NON_OVERRIDABLE_SETTINGS = JSON.stringify(nonOverridableSettings); return { specPath, browser, }; }; const getInitMessage = (labModeOn, browser) => { let labModeText = '- lab mode'; labModeText += (cli_1.programChoices === null || cli_1.programChoices === void 0 ? void 0 : cli_1.programChoices.gui) ? ' (GUI)' : ''; labModeText += !(cli_1.programChoices === null || cli_1.programChoices === void 0 ? void 0 : cli_1.programChoices.snap) ? ' (no snapshot)' : ''; if (labModeOn) { return `Starting Cypress ${labModeText}`; } return `Starting Cypress (${browser || defaultBrowser})`; }; const runCypressTest = (diffList) => __awaiter(void 0, void 0, void 0, function* () { return new Promise((resolve) => { var _a; const conf = prepareConfig(diffList); process.chdir(__dirname); const labModeOn = cli_1.programChoices.testType === 'lab'; const message = getInitMessage(labModeOn, conf.browser); (0, utils_1.printColorText)(`\n${message}\n`, '2'); let cypressCommand; if (labModeOn && cli_1.programChoices.gui) { cypressCommand = 'npx cypress open --env HEADED=true'; } else { cypressCommand = `npx cypress run --env CLI=true --spec "${conf.specPath}" ${conf.browser ? `--browser ${conf.browser}` : defaultBrowser}`; } const parts = cypressCommand.split(' '); const command = parts[0]; const args = parts.slice(1); const child = (0, child_process_1.spawn)(`DEBUG=cypress ${command}`, args, { shell: true }); (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => onTerminalDataOut(data, diffList)); child.on('error', (error) => console.error(`exec error: ${error}`)); child.on('close', (code) => { if (labModeOn) { resolve(); return; } const testDiffList = onTerminalCypressClose(); resolve(testDiffList); }); }); }); const onTerminalDataOut = (data, diffList) => { const dataString = data.toString(); if (dataString.includes('✓')) { const passing = (0, utils_1.createPassingEndpointTestResult)(dataString); endpointTestResults.passing.push(passing); (0, utils_1.printColorText)(`${dataString}`, '32'); return; } const failedRegexp = new RegExp(`(\\d+\\)\\s*${cli_1.programChoices.suite})`, 'g'); // e.g. 1) suiteSlug if (dataString.match(failedRegexp)) { const { userTerminated, failingEndpoints } = (0, utils_1.createFailingEndpointTestResult)(dataString, failedRegexp); userTerminatedTest = userTerminated; endpointTestResults.failing = [...endpointTestResults.failing, ...failingEndpoints]; (0, utils_1.printColorText)(`${dataString}`, '31'); return; } const failedInline = new RegExp(`(\\d+\\)\\s*[\\w\\s-]+\\s*\\@\\s*[\\w\\s-]+)`, 'g'); // e.g. 1) Start page @ samsung-s10 if (dataString.match(failedInline)) { (0, utils_1.printColorText)(`${dataString}`, '31'); return; } if (dataString.includes('visreg-test-agenda')) { if (!testAgenda.length) { if (cli_1.programChoices.testType === 'diffs-only') { testAgenda = (diffList === null || diffList === void 0 ? void 0 : diffList.map(diff => diff.replace('.diff.png', ''))) || []; return; } testAgenda = (0, utils_1.parseAgenda)(dataString); } return; } if (dataString.includes('Spec Ran')) { cypressSummary = (0, utils_1.parseCypressSummary)(dataString); // console.log(`${data}`) (0, utils_1.printColorText)(`${dataString}`, '2'); return; } if (dataString.match(/Spec\s+Tests\s+Passing\s+Failing\s+Pending\s+Skipped/)) { // We don't want to show Cypress' summary because we interpret and summarize the results ourselves return; } console.log(`${dataString}`); }; const onTerminalCypressClose = () => { endpointTestResults.skipped = (0, utils_1.getSkippedEndpoints)(endpointTestResults, testAgenda); endpointTestResults.unchanged = (0, utils_1.getUnchangedEndpoints)(endpointTestResults); const testDiffList = (0, utils_1.getDiffingFilesFromTestResult)(); restoreBackups(); const allDiffList = (0, utils_1.getAllDiffingFiles)(); const summary = { name: 'visreg-summary', testType: cli_1.programChoices.testType, testDiffList, allDiffList, userTerminatedTest, endpointTestResults, programChoices: cli_1.programChoices, cypressSummary, testAgenda, createdAt: new Date(), terminated: userTerminatedTest }; (0, utils_1.printColorText)(`\nTest duration: ${cypressSummary.duration} seconds\n`, '2'); (0, utils_1.printColorText)('\nTested:', '2'); summary.testAgenda.forEach(testTitle => { (0, utils_1.printColorText)(`${testTitle}`, '0'); }); if (summary.endpointTestResults.failing.length) { (0, utils_1.printColorText)('\nError:', '2'); summary.endpointTestResults.failing.forEach(endpoint => { (0, utils_1.printColorText)(`${endpoint.testTitle}`, '31'); }); } if (testDiffList.length) { (0, utils_1.printColorText)('\nDiffs:', '2'); testDiffList.forEach(diff => { (0, utils_1.printColorText)(`${diff}`, '33'); }); } if (summary.endpointTestResults.unchanged.length) { (0, utils_1.printColorText)('\nNo change:', '2'); summary.endpointTestResults.unchanged.forEach(endpoint => { (0, utils_1.printColorText)(`${endpoint.testTitle}`, '32'); }); } return testDiffList; }; const onDataOut = (data, ws, diffList) => { // Remove ASCII escape codes const dataString = data .toString() .replace(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/g, ''); const dataPackage = { name: '', type: 'text', stdout: dataString, color: '' }; if (dataString.includes('✓')) { const passing = (0, utils_1.createPassingEndpointTestResult)(dataString); endpointTestResults.passing.push(passing); dataPackage.color = '#749C75'; ws.send(JSON.stringify(dataPackage)); return; } const failedRegexp = new RegExp(`(\\d+\\)\\s*Suite: "${cli_1.programChoices.suite}")`, 'g'); // e.g. 1) Suite: "suiteSlug" if (dataString.match(failedRegexp)) { const { userTerminated, failingEndpoints } = (0, utils_1.createFailingEndpointTestResult)(dataString, failedRegexp); userTerminatedTest = userTerminated; endpointTestResults.failing = [...endpointTestResults.failing, ...failingEndpoints]; return; } const failedInline = /^\s*(\d+\)\s*[\w\s-]+\s*\@\s*[\w\s-]+)/; // e.g. 1) Start page @ samsung-s10 if (dataString.match(failedInline)) { dataPackage.color = '#FE4A49'; ws.send(JSON.stringify(dataPackage)); return; } if (dataString.includes('visreg-test-agenda')) { if (!testAgenda.length) { if (cli_1.programChoices.testType === 'diffs-only') { testAgenda = (diffList === null || diffList === void 0 ? void 0 : diffList.map(diff => diff.replace('.diff.png', ''))) || []; return; } testAgenda = (0, utils_1.parseAgenda)(dataString); } return; } if (dataString.includes('Spec Ran')) { cypressSummary = (0, utils_1.parseCypressSummary)(dataString); return; } if (dataString.match(/(Results)/)) { return; } if (dataString.match(/Spec\s+Tests\s+Passing\s+Failing\s+Pending\s+Skipped/)) { // We don't want to show Cypress' summary because we interpret and summarize the results ourselves return; } ws.send(JSON.stringify(dataPackage)); }; const onErrOut = (data, ws) => { if (data.includes('DevTools listening on ws://127.0.0.1') || data.includes('NSApplicationDelegate')) return; ws.send(JSON.stringify({ type: 'error', payload: `${data}` })); }; const onCypressClose = (ws, resolve) => { endpointTestResults.skipped = (0, utils_1.getSkippedEndpoints)(endpointTestResults, testAgenda); endpointTestResults.unchanged = (0, utils_1.getUnchangedEndpoints)(endpointTestResults); const testDiffList = (0, utils_1.getDiffingFilesFromTestResult)(); restoreBackups(); const allDiffList = (0, utils_1.getAllDiffingFiles)(); const summary = JSON.stringify({ type: 'data', payload: { name: 'visreg-summary', testType: cli_1.programChoices.testType, testDiffList, allDiffList, userTerminatedTest, endpointTestResults, programChoices: cli_1.programChoices, cypressSummary, testAgenda, createdAt: new Date(), terminated: userTerminatedTest, } }); (0, utils_1.printColorText)(`Test complete\n`, '2'); ws.send(summary); resolve(); return; }; const runWebCypressTest = (ws, diffList) => __awaiter(void 0, void 0, void 0, function* () { return new Promise((resolve, reject) => { var _a, _b; const conf = prepareConfig(diffList); process.chdir(__dirname); const initMessage = `Starting Cypress (${conf.browser || defaultBrowser})`; (0, utils_1.printColorText)(`\n${initMessage}\n`, '2'); const initMessagePackage = { name: '', type: 'text', stdout: initMessage, color: '#7D7D7D' }; ws.send(JSON.stringify(initMessagePackage)); let cypressCommand; cypressCommand = `npx cypress run --spec "${conf.specPath}" ${conf.browser ? `--browser ${conf.browser}` : defaultBrowser}`; const parts = cypressCommand.split(' '); const command = parts[0]; const args = parts.slice(1); const child = (0, child_process_1.spawn)(`DEBUG=cypress ${command}`, args, { shell: true }); (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => onDataOut(data, ws, diffList)); (_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => onErrOut(data, ws)); child.on('error', (error) => console.log(`exec error: ${error}`)); child.on('close', (code) => onCypressClose(ws, resolve)); }); }); const isNotTargetOfTest = (fileName) => { const res = !(0, utils_1.includedInTarget)(fileName); return res; }; const couldNotTest = (fileName, notTested) => { if (notTested.length === 0) return false; const untested = notTested.some(endpoint => (fileName.includes(endpoint.endpointTitle) && fileName.includes(endpoint.viewport))); return untested; }; const restoreBackups = () => { const backupDiffDir = (0, utils_1.BACKUP_DIFF_DIR)(); const backupReceivedDir = (0, utils_1.BACKUP_RECEIVED_DIR)(); const skipped = (0, utils_1.getSkippedEndpoints)(endpointTestResults, testAgenda); const notTested = [...endpointTestResults.failing, ...skipped]; const restoreCondition = (fileName) => (isNotTargetOfTest(fileName) || couldNotTest(fileName, notTested)); if ((0, utils_1.pathExists)(backupDiffDir)) { fs.readdirSync(backupDiffDir) .filter(restoreCondition) .forEach(fileName => { fs.renameSync(path.join(backupDiffDir, fileName), path.join((0, utils_1.DIFF_DIR)(), fileName)); }); } if ((0, utils_1.pathExists)(backupReceivedDir)) { fs.readdirSync(backupReceivedDir) .filter(restoreCondition) .forEach(fileName => { fs.renameSync(path.join(backupReceivedDir, fileName), path.join((0, utils_1.RECEIVED_DIR)(), fileName)); }); } (0, utils_1.cleanUp)(); }; const exitIfNoDIffs = () => { if (cli_1.programChoices.testType === 'lab') { process.exit(); } if (!(0, utils_1.pathExists)((0, utils_1.DIFF_DIR)()) || !(0, utils_1.hasFiles)((0, utils_1.DIFF_DIR)())) { (0, summarize_1.summarizeResultsAndQuit)([], [], failed); process.exit(); } }; const getDiffsForWeb = (suiteSlug, conf) => { cli_1.programChoices.suite = suiteSlug; cli_1.programChoices.testType = 'assess-existing-diffs'; cli_1.programChoices.webTesting = true; visregConfig = conf || visregConfig; let files = (0, utils_1.getDiffingFilesFromTestResult)(); const diffFiles = files.map((file, index) => { return (0, diff_assessment_web_1.processImageViaWeb)(file, index, files.length, suiteSlug); }); return diffFiles; }; exports.getDiffsForWeb = getDiffsForWeb; const resetPreviousTest = () => { testAgenda = []; failed = false; cypressSummary = {}; userTerminatedTest = false; cli_1.programChoices.targetViewports = []; cli_1.programChoices.targetEndpointTitles = []; endpointTestResults.passing = []; endpointTestResults.failing = []; endpointTestResults.skipped = []; endpointTestResults.unchanged = []; }; const startWebTest = (ws, progChoices, conf) => __awaiter(void 0, void 0, void 0, function* () { var _a; if (!progChoices.suite || !progChoices.testType) { return; } resetPreviousTest(); cli_1.programChoices.suite = progChoices.suite; cli_1.programChoices.testType = progChoices.testType; cli_1.programChoices.webTesting = true; visregConfig = conf || visregConfig; if (cli_1.programChoices.testType === 'targetted') { const parsedViewports = progChoices.targetViewports ? (_a = progChoices.targetViewports) === null || _a === void 0 ? void 0 : _a.map(vp => (0, utils_1.parseViewport)(vp)) : []; cli_1.programChoices.targetViewports = parsedViewports || []; cli_1.programChoices.targetEndpointTitles = progChoices.targetEndpointTitles || []; } const diffList = createTemporaryDiffList(); backupDiffs(); backupReceived(); yield runWebCypressTest(ws, diffList); }); exports.startWebTest = startWebTest; const backupDiffs = () => { const dir = (0, utils_1.DIFF_DIR)(); const backupDir = (0, utils_1.BACKUP_DIFF_DIR)(); if ((0, utils_1.pathExists)(dir) && (0, utils_1.hasFiles)(dir)) { if (!(0, utils_1.pathExists)(backupDir)) { fs.mkdirSync(backupDir); } const files = fs.readdirSync(dir); files.forEach(file => { fs.renameSync(path.join(dir, file), path.join(backupDir, file)); }); } }; const backupReceived = () => { const dir = (0, utils_1.RECEIVED_DIR)(); const backupDir = (0, utils_1.BACKUP_RECEIVED_DIR)(); if ((0, utils_1.pathExists)(dir) && (0, utils_1.hasFiles)(dir)) { if (!(0, utils_1.pathExists)(backupDir)) { fs.mkdirSync(backupDir); } const files = fs.readdirSync(dir); files.forEach(file => { fs.renameSync(path.join(dir, file), path.join(backupDir, file)); }); } }; const createTemporaryDiffList = () => { if (!(0, utils_1.pathExists)((0, utils_1.DIFF_DIR)())) return []; return fs.readdirSync((0, utils_1.DIFF_DIR)()).filter(file => file.endsWith('.diff.png')); }; const selectWhereToAssess = () => __awaiter(void 0, void 0, void 0, function* () { console.log(`Press SPACE to assess diffs in the browser`); console.log('...or ENTER to continue in the terminal\n'); const rl = readline.createInterface({ input: process.stdin, }); let answer; while (true) { answer = yield new Promise(resolve => { // Enable raw mode to get individual keypresses readline.emitKeypressEvents(process.stdin); if (process.stdin.isTTY) process.stdin.setRawMode(true); process.stdin.on('keypress', (str, key) => { if (key.name === 'space') { resolve('web'); } if (key.name === 'return') { resolve('cli'); } if (key.ctrl && key.name === 'c') { process.exit(); } }); }); if (answer === 'web' || answer === 'cli') { break; } } if (process.stdin.isTTY) process.stdin.setRawMode(false); rl.close(); return answer; }); const getTargetText = () => { if (!cli_1.programChoices.targetViewports.length && !cli_1.programChoices.targetEndpointTitles.length) { return ''; } let targetText = ' from test (limited to '; targetText += cli_1.programChoices.targetEndpointTitles.length ? `"${cli_1.programChoices.targetEndpointTitles.join(', ')}"` : ''; targetText += cli_1.programChoices.targetViewports.length ? ' @ ' + cli_1.programChoices.targetViewports.join(', ') : ''; targetText += ')'; return targetText; }; const assessExistingDiffImages = (files) => __awaiter(void 0, void 0, void 0, function* () { if (cli_1.programChoices.testType === 'lab') { process.exit(); } if (files.length === 0) { (0, summarize_1.summarizeResultsAndQuit)([], [], failed); process.exit(); } console.log('\n\n'); const targetText = getTargetText(); (0, utils_1.printColorText)(`🚨 Detected ${files.length} diffs${targetText}\n`, '33'); const answer = yield selectWhereToAssess(); if (answer === 'web') { (0, diff_assessment_web_1.assessInWeb)({ files, failed }); return; } if (cli_1.programChoices.containerized) { (0, utils_1.printColorText)(`Assess the changes by running: \x1b[4mnpx visreg-test -a ${cli_1.programChoices.suite}\x1b[0m (i.e. not from the container)`, '33'); return; } (0, diff_assessment_terminal_1.assessInCLI)({ files, targetText, failed, visregConfig }); }); process.on('SIGINT', () => { // console.log('\n\nTerminated by user'); // console.log('Restoring backups\n'); restoreBackups(); process.exit(); }); // Only start the server if the user has specified the --server-start flag (this is just as I'm working on it) if (cli_1.programChoices.serverStart) { (0, server_1.startServer)(cli_1.programChoices); } else { main(); }