UNPKG

html-reporter

Version:

Html-reporter and GUI for viewing and managing results of a tests run. Currently supports Testplane and Hermione.

185 lines (182 loc) 9.28 kB
'use strict'; const path = require('path'); const fs = require('fs-extra'); const _ = require('lodash'); const chalk = require('chalk'); const Promise = require('bluebird'); const { cliCommands } = require('../index'); const { logger } = require('../../../common-utils'); const { ToolName } = require('../../../constants'); const { REMOVE_UNUSED_SCREENS: commandName } = cliCommands; // TODO: remove hack after add ability to add controllers from plugin in silent mode function proxyTool() { const proxyHandler = { get(target, prop) { return prop in target ? target[prop] : new Proxy(() => { }, this); }, apply() { return new Proxy(() => { }, this); } }; global.hermione = global.testplane = new Proxy(global.testplane || global.hermione || {}, proxyHandler); } module.exports = (program, toolAdapter) => { const { toolName } = toolAdapter; program .command(commandName) .description('remove screenshots which were not used when running tests') .option('-p, --pattern <pattern>', 'pattern for searching screenshots on the file system', collect) .option('--skip-questions', 'do not ask questions during execution (default values will be used)') .on('--help', () => logger.log(getHelpMessage(toolName))) .action((options) => removeUnusedScreens(options, toolAdapter)); }; async function removeUnusedScreens(options, toolAdapter) { const { toolName } = toolAdapter; if (toolName !== ToolName.Testplane) { throw new Error(`CLI command "${commandName}" supports only "${ToolName.Testplane}" tool`); } const ora = require('ora'); const { getTestsFromFs, findScreens, askConfirm } = require('./utils'); try { proxyTool(); const { pattern: userPatterns } = options; if (_.isEmpty(userPatterns)) { throw new Error(`option "pattern" is required. See examples of usage: ${chalk.green(`npx ${toolName} ${commandName} --help`)}`); } const userScreenPatterns = transformPatternOption(userPatterns); const spinner = ora({ spinner: 'point' }); spinner.start(`Reading ${toolName} tests from file system`); const fsTests = await getTestsFromFs(toolAdapter); spinner.succeed(); logger.log(`${chalk.green(fsTests.count)} uniq tests were read in browsers: ${[...fsTests.browserIds].join(', ')}`); spinner.start(`Searching reference images on the file system by specified patterns:\n${userScreenPatterns.join('\n')}`); const foundScreenPaths = await findScreens(userScreenPatterns); spinner.succeed(); logger.log(`Found ${chalk.green(foundScreenPaths.length)} reference images`); const shouldIdentifyOutdated = await askConfirm({ message: 'Identify outdated reference images (tests for them are removed)?', default: true }, options); if (shouldIdentifyOutdated) { await handleOutdatedScreens(foundScreenPaths, fsTests.screenPatterns, { spinner, cliOpts: options }); } const shouldIdentifyUnused = await askConfirm({ message: 'Identify unused reference images (tests passed successfully for them, but they were not used during execution)?', default: true }, options); if (shouldIdentifyUnused) { await handleUnusedScreens(foundScreenPaths, fsTests, { toolAdapter, spinner, cliOpts: options }); } process.exit(0); } catch (err) { logger.error(err.stack || err); process.exit(1); } } async function handleOutdatedScreens(screenPaths, screenPatterns, opts = {}) { const { identifyOutdatedScreens } = require('./utils'); const { spinner } = opts; spinner.start(`Identifying outdated reference images (tests for these images do not exist)`); const outdatedScreenPaths = identifyOutdatedScreens(screenPaths, screenPatterns); spinner.succeed(); await handleScreens(screenPaths, { paths: outdatedScreenPaths, type: 'outdated' }, opts); } async function handleUnusedScreens(screenPaths, fsTests, opts = {}) { const { identifyUnusedScreens } = require('./utils'); const { DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME } = require('../../../constants/database'); const { toolAdapter, spinner, cliOpts } = opts; const { reporterConfig } = toolAdapter; const mainDatabaseUrls = path.resolve(reporterConfig.path, DATABASE_URLS_JSON_NAME); const isReportPathExists = await fs.pathExists(reporterConfig.path); if (!isReportPathExists) { throw new Error(`Can't find html-report in "${reporterConfig.path}" folder. You should run tests or download report from CI`); } spinner.start('Loading databases with the test results in order to identify unused screenshots in tests'); const dbPaths = await toolAdapter.htmlReporter.downloadDatabases([mainDatabaseUrls], { pluginConfig: reporterConfig }); spinner.succeed(); if (_.isEmpty(dbPaths)) { throw new Error(`Databases were not loaded from "${mainDatabaseUrls}" file`); } const mergedDbPath = path.resolve(reporterConfig.path, LOCAL_DATABASE_NAME); const srcDbPaths = dbPaths.filter((dbPath) => dbPath !== mergedDbPath); if (!_.isEmpty(srcDbPaths)) { spinner.start('Merging databases'); await toolAdapter.htmlReporter.mergeDatabases(srcDbPaths, reporterConfig.path); spinner.succeed(); logger.log(`${chalk.green(srcDbPaths.length)} databases were merged to ${chalk.green(mergedDbPath)}`); } spinner.start(`Identifying unused reference images (tests passed successfully for them, but they were not used during execution)`); const unusedScreenPaths = await identifyUnusedScreens(fsTests, { toolAdapter, mergedDbPath }); spinner.succeed(); await handleScreens(screenPaths, { paths: unusedScreenPaths, type: 'unused' }, { spinner, cliOpts }); } async function handleScreens(allScreenPaths, screensToRemove, { spinner, cliOpts } = {}) { const filesize = require('filesize'); const { removeScreens, askConfirm } = require('./utils'); const existentScreenPaths = await Promise.filter(screensToRemove.paths, async (screenPath) => { try { await fs.access(screenPath); return true; } catch (err) { if (err.code === 'ENOENT') { logger.warn(chalk.red(`Screen by path: "${screenPath}" is not found in your file system. ` + 'Try to rebase your branch or download more recent report from CI.')); return false; } throw err; } }); const screenPathsToRemoveLen = existentScreenPaths.length; logger.log(`Found ${chalk[screenPathsToRemoveLen > 0 ? 'red' : 'green'](screenPathsToRemoveLen)} ` + `${screensToRemove.type} reference images out of ${chalk.bold(allScreenPaths.length)}`); if (_.isEmpty(existentScreenPaths)) { return; } spinner.start(`Calculating total size of ${screensToRemove.type} reference images`); const bytes = (await Promise.map(existentScreenPaths, (screenPath) => fs.stat(screenPath))).reduce((acc, { size }) => acc + size, 0); spinner.succeed(); logger.log(`Total size of ${screensToRemove.type} reference images = ${chalk.red(filesize(bytes))}`); const shouldShowList = await askConfirm({ message: `Show list of ${screensToRemove.type} reference images? (${screenPathsToRemoveLen} paths)`, default: false }, cliOpts); if (shouldShowList) { logger.log(`List of ${screensToRemove.type} reference images:\n${existentScreenPaths.map((p) => chalk.red(p)).join('\n')}`); } const shouldRemove = await askConfirm({ message: `Remove ${screensToRemove.type} reference images?`, default: true }, cliOpts); if (!shouldRemove) { return; } spinner.start(`Removing ${screensToRemove.type} reference images`); await removeScreens(existentScreenPaths); spinner.succeed(); logger.log(`${chalk.green(screenPathsToRemoveLen)} reference images were removed`); } function collect(newValue, array = []) { return array.concat(newValue); } function getHelpMessage(toolName) { const rmUnusedScreens = `npx ${toolName} ${commandName}`; return ` Example of usage: Specify the folder in which all reference screenshots are located: ${chalk.green(`${rmUnusedScreens} -p '${toolName}-screens-folder'`)} Specify the mask by which all reference screenshots will be found: ${chalk.green(`${rmUnusedScreens} -p 'screens/**/*.png'`)} Specify few masks by which all reference screenshots will be found: ${chalk.green(`${rmUnusedScreens} -p 'screens/**/chrome/*.png' -p 'screens/**/firefox/*.png'`)} Don't ask me about anything and just delete unused reference screenshots: ${chalk.green(`${rmUnusedScreens} -p '${toolName}-screens-folder' --skip-questions`)}`; } function transformPatternOption(patterns) { return patterns.map(p => { const resolvedPattern = path.resolve(process.cwd(), p); return resolvedPattern.endsWith('.png') ? resolvedPattern : path.join(resolvedPattern, '*.png'); }); } //# sourceMappingURL=index.js.map