UNPKG

suitest-js-api

Version:

Suitest is a test automation and device manipulation tool for living room devices and web browsers.

175 lines (154 loc) 5.88 kB
/* eslint-disable max-len */ const {EOL} = require('os'); const {path, has} = require('ramda'); const t = require('../texts'); const chainUtils = require('./chainUtils'); const {stripAnsiChars} = require('./stringUtils'); const {getFirstStackLine} = require('./stackTraceParser'); /** Description of the function * @name ToStringFunc * @function * @param {Object} jsonMessage can be generated socket message or raw lined definition json * @param {boolean=} [nameOnly] * @returns {string} */ /** Error map handler * @name ErrorMapHandler * @function * @param {Object} data * @param {ToStringFunc} data.toString - chain toString function which accept socketMessage or json lined definition and return human readable representation * @param {Object} data.response - websocket message * @param {Object} data.jsonMessage - socket message or raw test command json definition * @returns {string} */ const responseMessageCode = response => path(['message', 'code'], response); const responseMessageInfo = response => path(['message', 'info'], response); const jsExpressionErrorType = 'jsExpressionError'; const testSnippetError = 'testSnippetError'; /** * Get snippet lines output recursively * @param {Object} data * @param {string} data.testId - id of root test * @param {Object} data.definitions - test definitions by id * @param {Array} data.results - results for current runSnippet line * @param {number} data.level - level for indentation, initially 1 * @param {string} data.verbosity - string verbosity level * @param {function} translate - added as function argument for testing */ const getSnippetLogs = ({testId, definitions, results, level, verbosity}, translate = chainUtils.translateLineResult) => { const indent = ' '.repeat(level); // Indent all text by indent constant const applyIndent = (str) => str.split('\n').map(l => indent + l).join('\n'); // array of definition without comments to get right def based on lineId in results const resultLines = definitions[testId].filter(l => l.type !== 'comment'); // Array of lines results with number of line in def without comments const linesNumbersWithResults = results.map(res => ({lineNumber: res.lineId.split('-').map(Number)[level] - 1, result: res})); // array of lines that have result const linesWithResults = linesNumbersWithResults.map(({lineNumber, result}) => ({def: resultLines[lineNumber], result})); // creating a list of lines and their results (if it hase it) const definitionsWithResults = definitions[testId].map(def => { if (def.type === 'openApp') { return; } // looking for lines in lines with results array const resultLine = linesWithResults.find(l => l.def === def); if (def.type === 'comment') { return def.val ? {def} : undefined; } else if (resultLine) { return resultLine; } // lines that does not have result return {def}; }).filter(Boolean); return definitionsWithResults.reduce((logs, {def, result}) => { logs.push(applyIndent(translate(def, verbosity, result))); if (result && (result.results || result.loopResults)) { const getLoopLogs = loopRes => getSnippetLogs({ testId: def.val, definitions, results: loopRes, level: level + 1, verbosity, }, translate); if (result.loopResults) { result.loopResults.forEach((loop, idx) => { logs.push(indent + `- loop count: ${idx + 1}`); logs.push(getLoopLogs(loop.results)); }); } else { logs.push(getLoopLogs(result.results)); } } return logs; }, []).filter(Boolean).join('\n'); }; const commandErrors = { 'notSupportedPlatform': () => t['commandError.notSupportedPlatform'](), 'timeout': () => t['commandError.timeout'](), 'generalError': () => t['commandError.generalError'](), 'notSupportedDriver': () => t['commandError.notSupportedDriver'](), 'screenshotFailed': () => t['commandError.screenshotFailed'](), 'notSupportedConfiguration': () => t['commandError.notSupportedConfiguration'](), }; /** * @description check that response error is related to "command" (not "line") execution. * For now it is related only for taking screenshots * @param response */ function isCommandError(response) { return response.result === 'error' && has(response.errorType, commandErrors); } /** * @description Create human readable error message from suitest error response * @param verbosity * @param {*} response websocket message * @param {Object} jsonMessage * @param {Array} snippets * @returns {string} */ function getErrorMessage({response, jsonMessage, verbosity, snippets}) { if (response.results && snippets) { return getSnippetLogs( {verbosity, definitions: snippets, results: response.results, testId: jsonMessage.request.val, level: 1}, ); } if (isCommandError(response)) { return commandErrors[response.errorType](); } return chainUtils.translateLineResult(jsonMessage, verbosity, response); } /** * @description Normalize errorType * @param {*} response webscoket message * @returns {string} */ function normalizeErrorType(response) { if (response.executeThrowException || response.matchJSThrowException) { return jsExpressionErrorType; } if (response.results) { return testSnippetError; } return response.errorType || response.executionError; } module.exports = { getErrorMessage, /** * @description Create human readable error message for real time info logs * @param {string} message * @param {string} prefix * @param {Object} res * @param {string} [stack] * @returns {string} */ getInfoErrorMessage: (message, prefix = '', res, stack) => { const msg = prefix + stripAnsiChars(message); const firstStackLine = stack && getFirstStackLine(stack); const nl = firstStackLine && msg.endsWith(EOL) ? '' : EOL; return msg + nl + firstStackLine; }, responseMessageCode, responseMessageInfo, normalizeErrorType, getSnippetLogs, };