Skip to content

Commit

Permalink
Merge pull request #157 from storybookjs/feat/add-console-logs-on-fai…
Browse files Browse the repository at this point in the history
…lure

feat: add console logs on test failure
  • Loading branch information
yannbf committed Aug 5, 2022
2 parents 561bc7a + 66af119 commit 847a1c7
Showing 1 changed file with 79 additions and 8 deletions.
87 changes: 79 additions & 8 deletions src/setup-page.ts
Expand Up @@ -50,26 +50,70 @@ export const setupPage = async (page) => {

await page.addScriptTag({
content: `
// colorizes the console output
const bold = (message) => \`\\u001b[1m\${message}\\u001b[22m\`;
const magenta = (message) => \`\\u001b[35m\${message}\\u001b[39m\`;
const blue = (message) => \`\\u001b[34m\${message}\\u001b[39m\`;
const red = (message) => \`\\u001b[31m\${message}\\u001b[39m\`;
const yellow = (message) => \`\\u001b[33m\${message}\\u001b[39m\`;
// removes circular references from the object
function serializer(replacer, cycleReplacer) {
let stack = [],
keys = [];
if (cycleReplacer == null)
cycleReplacer = function (_key, value) {
if (stack[0] === value) return '[Circular]';
return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']';
};
return function (key, value) {
if (stack.length > 0) {
let thisPos = stack.indexOf(this);
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value);
} else {
stack.push(value);
}
return replacer == null ? value : replacer.call(this, key, value);
};
}
function safeStringify(obj, replacer, spaces, cycleReplacer) {
return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces);
}
function composeMessage(args) {
if (typeof args === 'undefined') return "undefined";
if (typeof args === 'string') return args;
return safeStringify(args);
}
function truncate(input, limit) {
if (input.length > limit) {
return input.substring(0, limit) + '…';
}
return input;
}
class StorybookTestRunnerError extends Error {
constructor(storyId, errorMessage) {
constructor(storyId, errorMessage, logs) {
super(errorMessage);
this.name = 'StorybookTestRunnerError';
const storyUrl = \`${referenceURL || targetURL}?path=/story/\${storyId}\`;
const finalStoryUrl = \`\${storyUrl}&addonPanel=storybook/interactions/panel\`;
const separator = '\\n\\n--------------------------------------------------';
const extraLogs = logs.length > 0 ? separator + "\\n\\nBrowser logs:\\n\\n"+ logs.join('\\n\\n') : '';
this.message = \`\nAn error occurred in the following story. Access the link for full output:\n\${finalStoryUrl}\n\nMessage:\n \${truncate(errorMessage,${debugPrintLimit})}\`;
this.message = \`\nAn error occurred in the following story. Access the link for full output:\n\${finalStoryUrl}\n\nMessage:\n \${truncate(errorMessage,${debugPrintLimit})}\n\${extraLogs}\`;
}
}
async function __throwError(storyId, errorMessage) {
throw new StorybookTestRunnerError(storyId, errorMessage);
async function __throwError(storyId, errorMessage, logs) {
throw new StorybookTestRunnerError(storyId, errorMessage, logs);
}
async function __waitForElement(selector) {
Expand Down Expand Up @@ -118,18 +162,45 @@ export const setupPage = async (page) => {
'The test runner could not access the Storybook channel. Are you sure the Storybook is running correctly in that URL?'
);
}
// collect logs to show upon test error
let logs = [];
const spyOnConsole = (method, name) => {
const originalFn = console[method];
return function () {
const message = [...arguments].map(composeMessage).join(', ');
const prefix = \`\${bold(name)}: \`;
logs.push(prefix + message);
originalFn.apply(console, arguments);
};
};
// console methods + color function for their prefix
const spiedMethods = {
log: blue,
warn: yellow,
error: red,
trace: magenta,
group: magenta,
groupCollapsed: magenta,
}
Object.entries(spiedMethods).forEach(([method, color]) => {
console[method] = spyOnConsole(method, color(method))
})
return new Promise((resolve, reject) => {
channel.on('${renderedEvent}', () => resolve(document.getElementById('root')));
channel.on('storyUnchanged', () => resolve(document.getElementById('root')));
channel.on('storyErrored', ({ description }) => reject(
new StorybookTestRunnerError(storyId, description))
new StorybookTestRunnerError(storyId, description, logs))
);
channel.on('storyThrewException', (error) => reject(
new StorybookTestRunnerError(storyId, error.message))
new StorybookTestRunnerError(storyId, error.message, logs))
);
channel.on('storyMissing', (id) => id === storyId && reject(
new StorybookTestRunnerError(storyId, 'The story was missing when trying to access it.'))
new StorybookTestRunnerError(storyId, 'The story was missing when trying to access it.', logs))
);
channel.emit('setCurrentStory', { storyId, viewMode: '${viewMode}' });
Expand Down

0 comments on commit 847a1c7

Please sign in to comment.