- A Playwright Test Reporter (Node.js)
- Shows Suites/Cases/Steps with Tree Style
- Custom annotations with Markdown
- Custom Columns and Formatters (extra information for suite/case/step)
- Custom Data Collection Visitor
- Output Report Data and Summary (json)
- Console Logs in Order (log/error/warn/debug/info)
- Export Data (json)
- Timeline Workers Graph
- Monitor CPU and Memory Usage
- Metadata Report
- Searchable Fields
- Style Tags
- Merge Shard Reports
- Send Email with nodemailer (attachments/html)
- Testrail Integration with testrail-api
- Slack Integration with slack-sdk
https://cenfun.github.io/monocart-reporter
npm i monocart-reporter
// playwright.config.js
module.exports = {
reporter: [
['list'],
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html'
}]
]
};
Playwright Docs https://playwright.dev/docs/test-reporters
- path-to/your-filename.html
Single HTML file (data compressed), easy to transfer/deploy or open directly anywhere
Note that test attachments (screenshots images/videos) are not included but linked with relative path in report. All attachments will be found in playwrightConfig.outputDir
// playwright.config.js
// attachments outputDir and report outputFile used same folder
const date = new Date().toISOString().slice(0, 10); //2022-10-10
const outputDir = `./test-results/${date}`;
module.exports = {
outputDir: outputDir,
reporter: [
['monocart-reporter', {
name: `My Test Report ${date}`,
outputFile: `${outputDir}/index.html`
}]
]
};
// deploy the folder to your report site and easy checking online
// http://your.report.site/test-results/2022-10-10
- path-to/your-filename.json
Separated metadata file (Already included in the above HTML and compressed, it can be deleted). Can be used for debugging or custom data collection.
{
// the report name
name: '',
// the output file path (relative process.cwd)
outputFile: './test-results/report.html',
// custom attachment path. default is relative to output file
attachmentPath: null,
// attachmentPath: (currentPath, extras) => `https://cenfun.github.io/monocart-reporter/${currentPath}`,
// custom tags style
tags: null,
// tags: {
// smoke: {
// 'background': '#6F9913'
// },
// sanity: {
// 'background': '#178F43'
// }
// },
// custom columns
columns: null,
// columns: (defaultColumns) => {},
// additional custom visitor for columns
visitor: null,
// visitor: (data, metadata, collect) => {},
// async hook after report data generated
onEnd: null
// onEnd: async (reportData, capability) => {}
}
See options.js
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
// custom columns
columns: (defaultColumns) => {
// insert custom column(s) before a default column
const durationColumnIndex = defaultColumns.findIndex((column) => column.id === 'duration');
defaultColumns.splice(durationColumnIndex, 0, {
// define the column in reporter
id: 'owner',
name: 'Owner',
align: 'center',
searchable: true,
styleMap: {
'font-weight': 'normal'
}
}, {
// another column for JIRA link
id: 'jira',
name: 'JIRA Key',
width: 100,
searchable: true,
styleMap: 'font-weight:normal;',
formatter: (valueFormatted, rowItem, columnItem) => {
const key = rowItem[columnItem.id];
return `<a href="https://your-jira-key-link.com/${key}" target="_blank">${valueFormatted}</a>`;
}
});
}
}]
]
};
Collect Data from Comments (similar to JsDoc)
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
// additional custom visitor for columns
visitor: (data, metadata, collect) => {
// auto collect data from comments
const comments = collect.comments();
if (comments) {
Object.assign(data, comments);
}
}
}]
]
};
Compared to importing external libraries and calling its interface, Comments is a better way, no dependence, cleaner, easy to read, and never break the existing code.
- Case
/**
* add extra information for case
* @owner Kevin
* @jira MCR-16888
*/
test('case title', () => {
});
/**
* @description multiple lines text description
multiple lines text description
multiple lines text description
* @jira MCR-16888
*/
test('case description', () => {
});
- Describe
/**
* add extra information for describe
* @owner Mark
* @jira MCR-16900
*/
test.describe('suite title', () => {
});
- Step
test('case title', ({ browserName }, testInfo) => {
/**
* override assert step title "expect.toBe" to
* @title my custom assert step title
* @annotations important
*/
expect(testInfo).toBe(test.info());
// @owner Steve
await test.step('step title', () => {
});
});
- Hooks
/**
* override "beforeAll hook" title to
* @title do something before all
*/
test.beforeAll(() => {
});
/**
* override "beforeEach hook" title to
* @title do something before each
*/
test.beforeEach(() => {
});
- File
/**
* add extra information for file in the first line
* @owner FO
*/
const { test, expect } = require('@playwright/test');
- Project (Can't use comments)
// playwright.config.js
module.exports = {
projects: [
{
name: 'Desktop Chromium',
// add extra information for project with metadata
metadata: {
owner: 'PO'
}
}
]
};
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
visitor: (data, metadata, collect) => {
// remove secrets and sensitive data from reporter
if (data.type === 'step') {
// step title before:
// locator.type(input[type=password], mysecretpassword)
// apiRequestContext.get(https://api.npmjs.org/?token=myapitoken)
const mySecrets = [process.env.LOGIN_PASSWORD, process.env.API_TOKEN];
mySecrets.forEach((secret) => {
data.title = data.title.replace(secret, '***');
});
// step title after:
// locator.type(input[type=password], ***)
// apiRequestContext.get(https://api.npmjs.org/?token=***)
}
}
}]
]
};
- add metadata to config
// playwright.config.js
module.exports = {
globalSetup: require.resolve('./common/global-setup.js'),
metadata: {
// test home page object model
url: 'https://www.npmjs.org/package/monocart-reporter',
// test addInitScript
clientPath: 'tests/common/client.js'
},
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html'
}]
]
- collect metadata in global setup
// ./common/global-setup.js
import { chromium } from '@playwright/test';
export default async (config) => {
const metadata = config.metadata;
// collect data and save to metadata
const browser = await chromium.launch();
const chromiumVersion = await browser.version();
metadata.chromiumVersion = chromiumVersion;
};
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
columns: (defaultColumns) => {
const locationColumn = defaultColumns.find((column) => column.id === 'location');
locationColumn.searchable = true;
}
}]
]
- Add tag to test title (starts with @)
test('test title @smoke @critical', () => { ... });
- Custom tag style
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
tags: {
smoke: {
style: {
background: '#6F9913'
},
description: 'This is Smoke Test'
},
critical: {
background: '#c00'
}
}
}]
]
};
There will be multiple reports to be generated if Playwright test executes in sharding mode. for example:
npx playwright test --shard=1/3
npx playwright test --shard=2/3
npx playwright test --shard=3/3
There are 3 reports will be generated. Using MonocartReporter.merge()
API to merge all reports into one.
import MonocartReporter from 'monocart-reporter';
const reportDataList = [
// json file path
'path-to/shard1/index.json',
'path-to/shard2/index.json',
// or JSON data
JSON.parse(fs.readFileSync(path.resolve('path-to/shard3/index.json')))
];
await MonocartReporter.merge(reportDataList, {
name: 'My Merged Report',
outputFile: 'merged-report/index.html',
attachmentPath: (currentPath, extras) => {
// return `https://cenfun.github.io/monocart-reporter/${currentPath}`;
},
onEnd: async (reportData, capability) => {
}
});
example: merged report
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
// async hook after report data generated
onEnd: async (reportData, capability) => {
//await myAsyncFunction();
}
}]
]
};
Check example: send-email.js
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
onEnd: async (reportData, capability) => {
const sendEmail = require('./common/send-email.js');
await sendEmail(reportData, capability);
}
}]
]
};
Check example: testrail.js
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
onEnd: async (reportData, capability) => {
const testrail = require('./common/testrail.js');
await testrail(reportData, capability);
}
}]
]
};
- Simply send message with @slack/webhook, example: slack-webhook.js
// playwright.config.js
module.exports = {
reporter: [
['monocart-reporter', {
name: "My Test Report",
outputFile: './test-results/report.html',
onEnd: async (reportData, capability) => {
const slackWebhook = require('./common/slack-webhook.js');
await slackWebhook(reportData, capability);
}
}]
]
};
- Post chat message or upload report file with @slack/web-api, example: slack-web-api.js
Report UI packages/app
- Base on Vue 3
- Lightweight UI components vine-ui
- High Performance Grid turbogrid
- JSON compress/decompress with lz-utils
nmls -p
┌────────────────────────────┬─────────┬──────────┬──────┬───────────┬────────┐
│ Name │ Version │ Size │ Deps │ Deps Size │ Nested │
├────────────────────────────┼─────────┼──────────┼──────┼───────────┼────────┤
│ └ monocart-reporter │ 1.6.3 │ 598.5 KB │ 18 │ 2.50 MB │ 1 │
│ ├ dependencies │ │ │ │ │ │
│ │ ├ @babel/code-frame │ 7.18.6 │ 6.82 KB │ 10 │ 153.0 KB │ 0 │
│ │ ├ @babel/parser │ 7.21.3 │ 1.79 MB │ 0 │ 0 B │ 0 │
│ │ ├ console-grid │ 2.0.1 │ 36.9 KB │ 0 │ 0 B │ 0 │
│ │ ├ eight-colors │ 1.0.3 │ 14.9 KB │ 0 │ 0 B │ 0 │
│ │ ├ lz-utils │ 1.0.6 │ 26.5 KB │ 0 │ 0 B │ 0 │
│ │ ├ nodemailer │ 6.9.1 │ 476.0 KB │ 0 │ 0 B │ 0 │
│ │ └ stack-utils │ 2.0.6 │ 14.3 KB │ 1 │ 3.18 KB │ 1 │
├────────────────────────────┼─────────┼──────────┼──────┼───────────┼────────┤
│ └ packages in workspaces │ │ │ │ │ │
│ └ monocart-reporter │ 1.6.3 │ 534.4 KB │ 0 │ 0 B │ 0 │
└────────────────────────────┴─────────┴──────────┴──────┴───────────┴────────┘