Skip to content

Commit a5407e5

Browse files
committed
feat(puppeteer): support trace recording
1 parent 94805f1 commit a5407e5

File tree

3 files changed

+106
-4
lines changed

3 files changed

+106
-4
lines changed

docs/helpers/Puppeteer.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ Type: [object][4]
4444
- `disableScreenshots` **[boolean][20]?** don't save screenshot on failure.
4545
- `fullPageScreenshots` **[boolean][20]?** make full page screenshots on failure.
4646
- `uniqueScreenshotNames` **[boolean][20]?** option to prevent screenshot override if you have scenarios with the same name in different suites.
47+
- `trace` **[boolean][20]?** record [tracing information][25] with screenshots.
48+
- `keepTraceForPassedTests` **[boolean][20]?** save trace for passed tests.
4749
- `keepBrowserState` **[boolean][20]?** keep browser state between tests when `restart` is set to false.
4850
- `keepCookies` **[boolean][20]?** keep cookies between tests when `restart` is set to false.
4951
- `waitForAction` **[number][11]?** how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
@@ -55,11 +57,19 @@ Type: [object][4]
5557
- `userAgent` **[string][6]?** user-agent string.
5658
- `manualStart` **[boolean][20]?** do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
5759
- `browser` **[string][6]?** can be changed to `firefox` when using [puppeteer-firefox][2].
58-
- `chrome` **[object][4]?** pass additional [Puppeteer run options][25].
60+
- `chrome` **[object][4]?** pass additional [Puppeteer run options][26].
5961
- `highlightElement` **[boolean][20]?** highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
6062

6163

6264

65+
#### Trace Recording Customization
66+
67+
Trace recording provides complete information on test execution and includes screenshots, and network requests logged during run.
68+
Traces will be saved to `output/trace`
69+
70+
- `trace`: enables trace recording for failed tests; trace are saved into `output/trace` folder
71+
- `keepTraceForPassedTests`: - save trace for passed tests
72+
6373
#### Example #1: Wait for 0 network connections.
6474

6575
```js
@@ -2263,4 +2273,6 @@ Returns **[Promise][7]<void>** automatically synchronized promise through #re
22632273
22642274
[24]: https://codecept.io/react
22652275
2266-
[25]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions
2276+
[25]: https://pptr.dev/api/puppeteer.tracing
2277+
2278+
[26]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions

lib/helper/Puppeteer.js

+44-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const fsExtra = require('fs-extra');
44
const path = require('path');
55

66
const Helper = require('@codeceptjs/helper');
7+
const { v4: uuidv4 } = require('uuid');
78
const Locator = require('../locator');
89
const recorder = require('../recorder');
910
const store = require('../store');
@@ -19,6 +20,7 @@ const {
1920
fileExists,
2021
chunkArray,
2122
toCamelCase,
23+
clearString,
2224
convertCssPropertiesToCamelCase,
2325
screenshotOutputFolder,
2426
getNormalizedKeyAttributeValue,
@@ -57,6 +59,8 @@ const consoleLogStore = new Console();
5759
* @prop {boolean} [disableScreenshots=false] - don't save screenshot on failure.
5860
* @prop {boolean} [fullPageScreenshots=false] - make full page screenshots on failure.
5961
* @prop {boolean} [uniqueScreenshotNames=false] - option to prevent screenshot override if you have scenarios with the same name in different suites.
62+
* @prop {boolean} [trace=false] - record [tracing information](https://pptr.dev/api/puppeteer.tracing) with screenshots.
63+
* @prop {boolean} [keepTraceForPassedTests=false] - save trace for passed tests.
6064
* @prop {boolean} [keepBrowserState=false] - keep browser state between tests when `restart` is set to false.
6165
* @prop {boolean} [keepCookies=false] - keep cookies between tests when `restart` is set to false.
6266
* @prop {number} [waitForAction=100] - how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
@@ -92,6 +96,14 @@ const config = {};
9296
*
9397
* <!-- configuration -->
9498
*
99+
* #### Trace Recording Customization
100+
*
101+
* Trace recording provides complete information on test execution and includes screenshots, and network requests logged during run.
102+
* Traces will be saved to `output/trace`
103+
*
104+
* * `trace`: enables trace recording for failed tests; trace are saved into `output/trace` folder
105+
* * `keepTraceForPassedTests`: - save trace for passed tests
106+
*
95107
* #### Example #1: Wait for 0 network connections.
96108
*
97109
* ```js
@@ -281,8 +293,9 @@ class Puppeteer extends Helper {
281293
}
282294
}
283295

284-
async _before() {
296+
async _before(test) {
285297
this.sessionPages = {};
298+
this.currentRunningTest = test;
286299
recorder.retry({
287300
retries: 3,
288301
when: err => {
@@ -646,6 +659,13 @@ class Puppeteer extends Helper {
646659
this.isAuthenticated = true;
647660
}
648661
}
662+
const fileName = `${`${global.output_dir}${path.sep}trace${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.json`;
663+
const dir = path.dirname(fileName);
664+
if (!fileExists(dir)) fs.mkdirSync(dir);
665+
if (this.options.trace) {
666+
await this.page.tracing.start({ screenshots: true, path: fileName });
667+
this.currentRunningTest.artifacts.trace = fileName;
668+
}
649669

650670
await this.page.goto(url, { waitUntil: this.options.waitForNavigation });
651671

@@ -1898,8 +1918,30 @@ class Puppeteer extends Helper {
18981918
}
18991919
}
19001920

1901-
async _failed() {
1921+
async _failed(test) {
19021922
await this._withinEnd();
1923+
1924+
if (this.options.trace) {
1925+
await this.page.tracing.stop();
1926+
const _traceName = this.currentRunningTest.artifacts.trace.replace('.json', '.failed.json');
1927+
fs.renameSync(this.currentRunningTest.artifacts.trace, _traceName);
1928+
test.artifacts.trace = _traceName;
1929+
}
1930+
}
1931+
1932+
async _passed(test) {
1933+
await this._withinEnd();
1934+
1935+
if (this.options.trace) {
1936+
await this.page.tracing.stop();
1937+
if (this.options.keepTraceForPassedTests) {
1938+
const _traceName = this.currentRunningTest.artifacts.trace.replace('.json', '.passed.json');
1939+
fs.renameSync(this.currentRunningTest.artifacts.trace, _traceName);
1940+
test.artifacts.trace = _traceName;
1941+
} else {
1942+
fs.unlinkSync(this.currentRunningTest.artifacts.trace);
1943+
}
1944+
}
19031945
}
19041946

19051947
/**

test/helper/Puppeteer_test.js

+48
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ const path = require('path');
44

55
const puppeteer = require('puppeteer');
66

7+
const fs = require('fs');
78
const TestHelper = require('../support/TestHelper');
89
const Puppeteer = require('../../lib/helper/Puppeteer');
910

1011
const AssertionFailedError = require('../../lib/assert/error');
1112
const webApiTests = require('./webapi');
1213
const FileSystem = require('../../lib/helper/FileSystem');
1314
const Secret = require('../../lib/secret');
15+
const Playwright = require('../../lib/helper/Playwright');
16+
const { deleteDir } = require('../../lib/utils');
1417
global.codeceptjs = require('../../lib');
1518

1619
let I;
@@ -1037,3 +1040,48 @@ describe('Puppeteer (remote browser)', function () {
10371040
});
10381041
});
10391042
});
1043+
1044+
describe('Puppeteer - Trace', () => {
1045+
const test = { title: 'a failed test', artifacts: {} };
1046+
before(() => {
1047+
global.codecept_dir = path.join(__dirname, '/../data');
1048+
global.output_dir = path.join(`${__dirname}/../data/output`);
1049+
1050+
I = new Puppeteer({
1051+
url: siteUrl,
1052+
windowSize: '500x700',
1053+
show: false,
1054+
trace: true,
1055+
});
1056+
I._init();
1057+
return I._beforeSuite();
1058+
});
1059+
1060+
beforeEach(async () => {
1061+
webApiTests.init({
1062+
I, siteUrl,
1063+
});
1064+
deleteDir(path.join(global.output_dir, 'trace'));
1065+
return I._before(test).then(() => {
1066+
page = I.page;
1067+
browser = I.browser;
1068+
});
1069+
});
1070+
1071+
afterEach(async () => {
1072+
return I._after();
1073+
});
1074+
1075+
it('checks that trace is recorded', async () => {
1076+
await I.amOnPage('/');
1077+
await I.dontSee('this should be an error');
1078+
await I.click('More info');
1079+
await I.dontSee('this should be an error');
1080+
await I._failed(test);
1081+
assert(test.artifacts);
1082+
expect(Object.keys(test.artifacts)).to.include('trace');
1083+
1084+
assert.ok(fs.existsSync(test.artifacts.trace));
1085+
expect(test.artifacts.trace).to.include(path.join(global.output_dir, 'trace'));
1086+
});
1087+
});

0 commit comments

Comments
 (0)