-
Notifications
You must be signed in to change notification settings - Fork 289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Take screenshot when a test fails #131
Comments
This is exactly the same as #130 (from Jest's perspective), right? Whether you take a screenshot or not shouldn't matter, as long as you can intercept the error reporting. Unless I'm misunderstanding? |
Here is how I did it: export const registerScreenshotReporter = () => {
/**
* jasmine reporter does not support async.
* So we store the screenshot promise and wait for it before each test
*/
let screenshotPromise = Promise.resolve();
beforeEach(() => screenshotPromise);
afterAll(() => screenshotPromise);
/**
* Take a screenshot on Failed test.
* Jest standard reporters run in a separate process so they don't have
* access to the page instance. Using jasmine reporter allows us to
* have access to the test result, test name and page instance at the same time.
*/
jasmine.getEnv().addReporter({
specDone: async result => {
if (result.status === 'failed') {
screenshotPromise = screenshotPromise
.catch()
.then(() => takeScreenshot(result.fullName));
}
},
});
}; |
+1 for a much needed feature |
The |
@testerez Can you tell me where you're calling that code from? I was trying to do it the way jest-screenshot-reporter does, but it seems like the page gets closed before I can take the screenshot. |
@jacobweber I just call |
@testerez Thanks! Got it working. |
@testerez On using your custom reporter, I get an error that eg. I am able to make this work with puppeteer screenshot method (using that in custom defined method); but would prefer to use the in-built support for screenshot available with jasmine reporter, if any. |
@ricwal-richa yes, import path from 'path';
import mkdirp from 'mkdirp';
const screenshotsPath = path.resolve(__dirname, '../testReports/screenshots');
const toFilename = (s: string) => s.replace(/[^a-z0-9.-]+/gi, '_');
export const takeScreenshot = (testName: string, pageInstance = page) => {
mkdirp.sync(screenshotsPath);
const filePath = path.join(
screenshotsPath,
toFilename(`${new Date().toISOString()}_${testName}.png`),
);
return pageInstance.screenshot({
path: filePath,
});
};
export const registerScreenshotReporter = () => {
/**
* jasmine reporter does not support async.
* So we store the screenshot promise and wait for it before each test
*/
let screenshotPromise: Promise<any> = Promise.resolve();
beforeEach(() => screenshotPromise);
afterAll(() => screenshotPromise);
/**
* Take a screenshot on Failed test.
* Jest standard reporters run in a separate process so they don't have
* access to the page instance. Using jasmine reporter allows us to
* have access to the test result, test name and page instance at the same time.
*/
(jasmine as any).getEnv().addReporter({
specDone: async (result: any) => {
if (result.status === 'failed') {
screenshotPromise = screenshotPromise
.catch()
.then(() => takeScreenshot(result.fullName));
}
},
});
}; |
Did you ever see blank screenshots and get around that issue? Thank you for sharing! |
It looks like a lot of people are close to the solution. Could someone try to submit a PR to add screenshot? 🙏 |
@testerez Thanks for the code! However, I cannot I get undefined error for the pageInstance variable. I just put all the code (In javascript) in my setupTestFrameworkScriptFile file + a call to registerScreenshotReporter(). I modified the declaration for Vainilla as such:
|
To come back at this issue, before I found this issue I had several attempts at this myself. const path = require('path');
const fs = require('fs').promises;
const hash = (str) => {
if (str.length === 0) {
return 0;
}
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return hash;
};
const origIt = global.it;
global.it = (description, test, timeout) => {
const result = origIt(description, async () => {
const basePath = path.join(result.result.testPath, '..', '__screenshots__');
try {
await fs.mkdir(basePath, { recursive: true });
await page.screenshot({ path: path.join(basePath, `before-${hash(result.result.fullName)}.png`) });
} finally {
try {
return await test();
} finally {
await page.screenshot({ path: path.join(basePath, `after-${hash(result.result.fullName)}.png`) });
}
}
}, timeout);
return result;
}; Re-defining it is kinda ugly but it works better than all my other versions. Also I ignored |
|
Use this package: |
It is amazing to see that everyone has a solution and nobody wants to make a PR, welcome in Wordpress world 🤗 |
Incase it's helpful, It seemed for me like the page was closed by the time jasmine reporter was called when using one of the above solutions, and overriding |
@testerez where would u even call |
@rostgoat you can call it in your |
is this how your jest.config.js file looks @testerez?
how would this call the |
|
Thought I'd share my solution to this, using jest-circus and extending the jest-puppeteer environment. It's a little messy with the sleeps, I want to find a better way to handle that, but it works for now. Maybe jest-puppeteer can integrate with jest-circus out of the box once circus has more adoption and make this better.
|
@AlexMarchini thanks for that! Perfect, I wanted to use jest-circus as well. If anyone else is wondering (or anyone has suggestions) like I was how to actually use this this is what my
So I removed And that seems to be working.. with jest-circus as the runner. (Note just run Hope this helps someone. |
Previously I commented here that we had a solution for work. The short version is that it was based on @testerez work. Sadly for whatever reason that was a moment of glory yesterday evening as when I started to get it into our CI/CD pipeline this morning it all crashed and burned and even refused to work locally so I deleted that comment. That said, I ended up completely rewriting the library I made now relying on a solution very similar to @AlexMarchini / @allimuu thus relying on Jest Circus and at least it seems to work properly now. For those interested you can find it here (bumped to v2.0.0 as it broke previous releases). Instructions and example on how to use the library are included in the README you can read on the yarnpkg / npm site. This includes a maybe more detailed (due to including code sample) version of @allimuu 's contribution. And as before here is the actual source code behind it |
* fix(jest-environment-puppeteer): Class & Exports This makes the typings actually compatible to create your own test environment and not just as part of the bigger jest-puppeteer package. How to do this is pretty much what the test covers. One of the reasons to do this is to write a handler where Puppeteer takes screenshots after each failed test, as discussed [here](argos-ci/jest-puppeteer#131) * Change to `export =` syntax Co-Authored-By: Pranav Senthilnathan <[email protected]> * Fix the last stuff requested
This was not as straightforward as I thought. I found in the above methods Monkey patching the Jest.it method. // jest.config.js
module.exports = {
...
setupFilesAfterEnv: [
'<rootDir>/setupFilesAfterEnv.js',
]
}; // setupFilesAfterEnv.js
// Wrapper function that wraps the test function
// to take a screenshot on failure
const wrappedTest = (test, description) => {
return Promise.resolve()
.then(test)
.catch(async (err) => {
// Assuming you have a method to take a screenshot
await takeScreenshot('some file name');
throw err;
});
};
// Make a copy of the original function
const originalIt = global.it;
// Modify `it` to use the wrapped test method
global.it = function it(description, test, timeout) {
// Pass on the context by using `call` instead of directly invoking the method.
return originalIt.call(this, description, wrappedTest.bind(this, test, description), timeout);
};
// Copy other function properties like `skip`, `only`...
for (const prop in originalIt) {
if (Object.prototype.hasOwnProperty.call(originalIt, prop)) {
global.it[prop] = originalIt[prop];
}
}
// Monkey patch the `only` method also to use the wrapper method
global.it.only = function only(description, test, timeout) {
return originalIt.only.call(this, description, wrappedTest.bind(this, test, description), timeout);
}; |
I think this pull request jestjs/jest#9397 may come to the rescue if you are struggling with asynchronicity. |
For future readers, if you wanna get #131 (comment) to work, please make sure BOTH of your Our project updated https://github.com/facebook/jest/pull/9397/files#diff-e77e110fa517918f09bcd38e70996fafR164 Note to self that |
In case someone wants to know how to get the test describe name when using
I had to spend some time searching in the source code how |
I have somehow got video recording of test runs and screenshots of tests before and after each test working using Jest and Puppeteer. Please have a look if you guys find it helpful... |
This is all quite helpful, it would be even better if somehow log would be able to generate with information and saved in directory on test failure, along with screenshot. Any help\suggestions around that? |
Hi Stefan – Thanks for quick reply!
No, I meant some kind of information that you see on terminal when test fails (ran by Jest>> npm test), just like in code below where screenshot is being captured and saved in the path directory.
I am trying to build a code that will grab fail test results and put it in directory that can be shared with DevOps during CI.
async handleTestEvent(event) {
if (event.name === 'test_done' && event.test.errors.length > 0) {
const parentName = event.test.parent.name.replace(/\W/g, '-')
const specName = event.test.name.replace(/\W/g, '-')
await this.global.page.screenshot({
path: `screenshots/${parentName}_${specName}.png`, fullPage: true,
})
}
Hope this helps.
From: Stefan Teixeira <[email protected]>
Reply-To: smooth-code/jest-puppeteer <[email protected]>
Date: Wednesday, August 12, 2020 at 4:50 PM
To: smooth-code/jest-puppeteer <[email protected]>
Cc: "Khobragade, Ajay" <[email protected]>, Mention <[email protected]>
Subject: Re: [smooth-code/jest-puppeteer] Take screenshot when a test fails (#131)
@ajaykhobragade<https://github.com/ajaykhobragade> you mean saving the console log output in a file? If so, you can get console output from Puppeteer like this: https://stackoverflow.com/a/46245945
Then you can save that output to a file in a directory alongside screenshots.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<#131 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AOSVEZJ66XGYYPNXBXA527LSAMFCNANCNFSM4FWW7K6Q>.
|
Whats the status no PR's for taking screenshot ? |
Hey @testerez where we can call that? on protractor.conf.js ? |
Hi everyone. https://github.com/andredesousa/essential-react-scaffold Basically, I cretead a new Test Environment:
...and I imported it into my
Thus, when the test fails, a screenshot is attached to the report. |
@jpourdanis I am trying to use this code (typescript) along with jest-cucumber, but it gives me error saying.
|
Maybe the PR to WordPress Gutenberg by @kevin940726 : ...would provide a basis for a PR to the |
In my use case, I would like to be able to take screenshots for all open pages of puppeteer when a test fail. Because my test needs to have some background pages running. |
@lucassardois PR was done but I had to migrate to TypeScript before. Will be done in the next weeks. |
As suggested in #43, we would like to have a screenshot when a test fail, it is the same idea as #130.
Technical detail
.jest-puppeteer/screenshots
folder.${fileName}-${describe}-${it}-${timestamp}.png
We could redefine
it
andtest
but I think this is not the cleanest method. Any help from Jest team to do it properly is welcome @SimenB.The text was updated successfully, but these errors were encountered: