UNPKG

mochawesome

Version:
146 lines (130 loc) 4.08 kB
const stringify = require('json-stringify-safe'); // Gray the log prefix without importing `node:util`. `addContext` is bundled // for the browser (e.g. Cypress/webpack), and a `node:` builtin anywhere in // its require graph breaks bundling (#425). Only emit ANSI codes for a TTY so // browsers/CI logs get plain text instead of raw escape sequences. /* c8 ignore start */ const gray = str => typeof process !== 'undefined' && process.stdout?.isTTY ? `${str}` : str; /* c8 ignore stop */ const errorPrefix = 'Error adding context:'; const ERRORS = { INVALID_ARGS: `${errorPrefix} Invalid arguments.`, INVALID_TEST: `${errorPrefix} Invalid test object.`, INVALID_CONTEXT: ctx => { const expected = 'Expected a string or an object of shape { title: string, value: any } but saw:'; return `${errorPrefix} ${expected}\n${stringify( ctx, (key, val) => (val === undefined ? 'undefined' : val), 2 )}`; }, }; /** * HELPER FUNCTIONS */ /* c8 ignore start */ function log(msg, level) { const logMethod = console[level] || console.log; let out = msg; if (typeof msg === 'object') { out = stringify(msg, null, 2); } logMethod(`[${gray('mochawesome')}] ${out}\n`); } /* c8 ignore stop */ function _isValidContext(ctx) { /* * Context is valid if any of the following are true: * 1. Type is string and it is not empty * 2. Type is object and it has properties `title` and `value` and `title` is not empty */ if (!ctx) return false; return ( typeof ctx === 'string' || (Object.hasOwn(ctx, 'title') && typeof ctx.title === 'string' && ctx.title.length > 0 && Object.hasOwn(ctx, 'value')) ); } /** * Add context to the test object so it can * be displayed in the mochawesome report * * @param {Object} test object * @param {String|Object} context to add * If context is an object, it must have the shape: * { * title: string that is used as context title in the report * value: the context that is to be added * } * * Usage: * * it('should test something', function () { * someFunctionThatTestsCode(); * * addContext(this, 'some context to add'); * * addContext(this, { * title: 'Expected number of something' * value: 42 * }); * * assert('something'); * }); * */ const addContext = function (...args) { // Check args to see if we should bother continuing if (args.length !== 2 || typeof args[0] !== 'object' || args[0] === null) { log(ERRORS.INVALID_ARGS, 'error'); return; } const ctx = args[1]; // Ensure that context meets the requirements if (!_isValidContext(ctx)) { log(ERRORS.INVALID_CONTEXT(ctx), 'error'); return; } /* Context is valid, now get the test object * If `addContext` is called from inside a hook the test object * will be `.currentTest`, and the hook will be `.test`. * Otherwise the test is just `.test` and `.currentTest` is undefined. */ const currentTest = args[0].currentTest; const activeTest = args[0].test; /* For `before` and `after`, add the context to the hook, * otherwise add it to the actual test. */ const isEachHook = currentTest && /^"(?:before|after)\seach"/.test(activeTest.title); const test = isEachHook ? currentTest : activeTest; if (!test) { log(ERRORS.INVALID_TEST, 'error'); return; } /* If context is an object, and value is `undefined` * change it to 'undefined' so it can be displayed * correctly in the report */ if (ctx.title && ctx.value === undefined) { ctx.value = 'undefined'; } // Test doesn't already have context -> set it if (!test.context) { test.context = ctx; } else if (Array.isArray(test.context)) { // Test has context and context is an array -> push new context test.context.push(ctx); } else { // Test has context and it is not an array -> make it an array, then push new context test.context = [test.context]; test.context.push(ctx); } }; module.exports = addContext;