Jest-puppeteer: Keep browser opened on failure

Created on 23 Sep 2018  Â·  15Comments  Â·  Source: smooth-code/jest-puppeteer

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.

enhancement ✨ help wanted 🆘

Most helpful comment

// 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;
    }
  });
};

All 15 comments

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?

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 »?

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 :)

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?

// 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;
    }
  });
};

We got an traction on this guys?

// 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;
    }
  });
};

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

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

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 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.

@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 what if page is closed at that time? How can we get the screenshot?

@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

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

Was this page helpful?
0 / 5 - 0 ratings