jest-puppeteer icon indicating copy to clipboard operation
jest-puppeteer copied to clipboard

Keep browser opened on failure

Open gregberge opened this issue 6 years ago • 15 comments

As suggested in #38, the idea is to intercept a test failure and stop the browser. It is not an easy task and it needs some research. Jest does not provide any API to intercept a test failure, so we have to hack it.

Maybe Jest team could help us to achieve that? @SimenB

#43 could also help to find a solution for this.

gregberge avatar Sep 23 '18 15:09 gregberge

Jest does not provide any API to intercept a test failure, so we have to hack it.

What do you mean? Right now we just throw (unless you use done callback), so you could just replace test with your own function with built in try-catch I think?

Or do you want us to have a function on e.g. testEnvironment's where it can intercept errors and either forward or swallow them?

SimenB avatar Sep 23 '18 17:09 SimenB

I think it would be better to be able to intercept an error in the test environment. I don’t want to invent another method than « it » and « test ». I could patch them but if every Jest plugin do it, it will become a jungle.

So your advice for now is to patch « it » and « test »?

gregberge avatar Sep 24 '18 06:09 gregberge

For now, yeah. You can open up a feature request with jest if you want, but I'm not sure if it'll be accepted. Some examples of the API you envision would be nice :)

SimenB avatar Sep 27 '18 08:09 SimenB

I've tried to wrap « it » with the following function:

global.makeSure = (description, func) => {
  return it(description, async () => {
    try {
      await func();
    } catch (e) {
      console.log(page);
      await page.screenshot({ path: `screenshot.png` });
    }
  });
};

The page object is accessible but I'm unable to take a screenshot (page._closed is true at this point). Is there any way around it?

klase avatar Nov 06 '18 17:11 klase

// jest.setup.js

global.it = async function(name, func) {
  return await test(name, async () => {
    try {
      await func();
    } catch (e) {
      await fs.ensureDir('e2e/screenshots');
      await page.screenshot({ path: `e2e/screenshots/${name}.png` });
      throw e;
    }
  });
};

celador avatar Mar 04 '19 22:03 celador

We got an traction on this guys?

rodoabad avatar Aug 07 '19 18:08 rodoabad

// jest.setup.js

global.it = async function(name, func) {
  return await test(name, async () => {
    try {
      await func();
    } catch (e) {
      await fs.ensureDir('e2e/screenshots');
      await page.screenshot({ path: `e2e/screenshots/${name}.png` });
      throw e;
    }
  });
};

This works great for taking screenshots! thx @celador

for typescript users:

// jest.setup.ts
import { ensureDir } from "fs-extra";

declare global {
  namespace NodeJS {
    interface Global {
      it: (name: any, func: any) => Promise<void>;
    }
  }
}

global.it = async function(name, func) {
  return await test(name, async () => {
    try {
      await func();
    } catch (e) {
      await ensureDir("screenshots");
      await page.screenshot({ path: `screenshots/${name}.png` });
      throw e;
    }
  });
};

ghost avatar Nov 14 '19 14:11 ghost

That works perfectly fine, the only question I have, after I start using that function for screenshots generation, i can't use it.only in tests, getting error it.only is not a function. Maybe someone knows what's wrong? I'm using Jest

Mahanchello avatar Dec 11 '19 18:12 Mahanchello

That works perfectly fine, the only question I have, after I start using that function for screenshots generation, i can't use it.only in tests, getting error it.only is not a function. Maybe someone knows what's wrong? I'm using Jest

I suppose it is not chaining methods. Try this https://stackoverflow.com/questions/44446626/run-only-one-test-with-jest

pavoltravnik avatar Dec 16 '19 12:12 pavoltravnik

v25.3.0 (released 15 minutes ago) includes https://github.com/facebook/jest/pull/9397 which should unblock both this and #131 without hacks. Note that it only works with jest-circus, not the default jest-jasmine2 runner. We'll be swapping the default in Jest 26, but for now jest-circus is opt-in

SimenB avatar Apr 08 '20 13:04 SimenB

@SimenB is there a solution for this problem as of today? I understand Jest now provides the necessary functionalities but I don't understand if jest-puppeteer makes use of them.

FezVrasta avatar Sep 16 '20 14:09 FezVrasta

@Mahanchello you can do the following instead

global.it = Object.assign(async (name, func, timeout) => {
    return await test(name, async () => {
        try {
            await func();
        } catch (e) {
            if (page) {
                try {
                    await page.screenshot({path: `screenshots/${name}.png`});
                } catch (e) {
                    console.error(e)
                }
            }
            throw e;
        }
    }, timeout);
}, test)

sjurgis avatar Oct 21 '20 01:10 sjurgis

@sjurgis what if page is closed at that time? How can we get the screenshot?

rajeshkumarp avatar Oct 22 '20 08:10 rajeshkumarp

@rajeshkumarp I guess not. Only wanted to try this solution because I was reluctant to try jest-circus. I've now switched to it and it's much easier and works better (we're actually using Playwright, not Puppeteer). It was as easy as adding testEnvironment: "./jest.environment.js", and preset: "jest-playwright-preset", to our jest.config.js and then

const PlaywrightEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvironment').default

class CustomEnvironment extends PlaywrightEnvironment {
    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, '-')
            try {
                const url = this.global.page.url()
                if (this.global.page && url && url !== 'about:blank') {
                    await this.global.page.screenshot({
                        path: `screenshots/${parentName}_${specName}.png`,
                    })
                }
            } catch (e) {
                console.error(e)
            }
        }
    }
}

module.exports = CustomEnvironment

sjurgis avatar Oct 22 '20 23:10 sjurgis

Why not simply use a very long timer to prevent the browser from closing?

saharso avatar Nov 15 '20 08:11 saharso

It will not happen. But we will take a screenshot.

gregberge avatar Feb 03 '23 15:02 gregberge