UNPKG

3.7 kBJavaScriptView Raw
1const isObject = require('lodash.isobject');
2const isEmpty = require('lodash.isempty');
3const chalk = require('chalk');
4const stringify = require('json-stringify-safe');
5
6const errorPrefix = 'Error adding context:';
7const ERRORS = {
8 INVALID_ARGS: `${errorPrefix} Invalid arguments.`,
9 INVALID_TEST: `${errorPrefix} Invalid test object.`,
10 INVALID_CONTEXT: ctx => {
11 const expected =
12 'Expected a string or an object of shape { title: string, value: any } but saw:';
13 return `${errorPrefix} ${expected}\n${stringify(
14 ctx,
15 (key, val) => (val === undefined ? 'undefined' : val),
16 2
17 )}`;
18 },
19};
20
21/**
22 * HELPER FUNCTIONS
23 */
24
25/* istanbul ignore next */
26function log(msg, level) {
27 const logMethod = console[level] || console.log;
28 let out = msg;
29 if (typeof msg === 'object') {
30 out = stringify(msg, null, 2);
31 }
32 logMethod(`[${chalk.gray('mochawesome')}] ${out}\n`);
33}
34
35function _isValidContext(ctx) {
36 /*
37 * Context is valid if any of the following are true:
38 * 1. Type is string and it is not empty
39 * 2. Type is object and it has properties `title` and `value` and `title` is not empty
40 */
41 if (!ctx) return false;
42 return (
43 (typeof ctx === 'string' && !isEmpty(ctx)) ||
44 (Object.hasOwnProperty.call(ctx, 'title') &&
45 !isEmpty(ctx.title) &&
46 Object.hasOwnProperty.call(ctx, 'value'))
47 );
48}
49
50/**
51 * Add context to the test object so it can
52 * be displayed in the mochawesome report
53 *
54 * @param {Object} test object
55 * @param {String|Object} context to add
56 * If context is an object, it must have the shape:
57 * {
58 * title: string that is used as context title in the report
59 * value: the context that is to be added
60 * }
61 *
62 * Usage:
63 *
64 * it('should test something', function () {
65 * someFunctionThatTestsCode();
66 *
67 * addContext(this, 'some context to add');
68 *
69 * addContext(this, {
70 * title: 'Expected number of something'
71 * value: 42
72 * });
73 *
74 * assert('something');
75 * });
76 *
77 */
78
79const addContext = function (...args) {
80 // Check args to see if we should bother continuing
81 if (args.length !== 2 || !isObject(args[0])) {
82 log(ERRORS.INVALID_ARGS, 'error');
83 return;
84 }
85
86 const ctx = args[1];
87
88 // Ensure that context meets the requirements
89 if (!_isValidContext(ctx)) {
90 log(ERRORS.INVALID_CONTEXT(ctx), 'error');
91 return;
92 }
93
94 /* Context is valid, now get the test object
95 * If `addContext` is called from inside a hook the test object
96 * will be `.currentTest`, and the hook will be `.test`.
97 * Otherwise the test is just `.test` and `.currentTest` is undefined.
98 */
99 const currentTest = args[0].currentTest;
100 const activeTest = args[0].test;
101
102 /* For `before` and `after`, add the context to the hook,
103 * otherwise add it to the actual test.
104 */
105 const isEachHook =
106 currentTest && /^"(?:before|after)\seach"/.test(activeTest.title);
107 const test = isEachHook ? currentTest : activeTest;
108
109 if (!test) {
110 log(ERRORS.INVALID_TEST, 'error');
111 return;
112 }
113
114 /* If context is an object, and value is `undefined`
115 * change it to 'undefined' so it can be displayed
116 * correctly in the report
117 */
118 if (ctx.title && ctx.value === undefined) {
119 ctx.value = 'undefined';
120 }
121
122 // Test doesn't already have context -> set it
123 if (!test.context) {
124 test.context = ctx;
125 } else if (Array.isArray(test.context)) {
126 // Test has context and context is an array -> push new context
127 test.context.push(ctx);
128 } else {
129 // Test has context and it is not an array -> make it an array, then push new context
130 test.context = [test.context];
131 test.context.push(ctx);
132 }
133};
134
135module.exports = addContext;