-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Calling puppeteer from cypress #2427
Comments
This isn't a bug. Puppeteer is a node module that cannot be run in the browser. Move this to your |
Hi, What can be the usage of puppeteer into cypress? Puppeteer is a test framework and cypress is an other too. |
Have you successfully made it work? I couldn't launch Chrome. Error: Failed to launch chrome! TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md
|
@debbyca I launched puppeteer from chrome... however with mixed results... not sure the code is still working. Also this is only an example for asp.net. Might help you as a starting point at least. The puppeteer code for the login page is custom of course. You might also want to check out https://gauge.org/ which takes a bit a different approach. on("task", {
doLogin: (args) => {
return (async () => {
expect.setDefaultOptions({ timeout: 10000 });
let cookie = process.env.ASPCOOKIE;
if (!cookie) {
const browser = await puppeteer.launch({ headless: true })
const page = await browser.newPage()
await page.goto('https://<your site with login')
await page.click('a.btn.btn-link')
await page.waitForNavigation();
await page.waitFor('[name="loginfmt"]');
await expect(page).toFill('[name="loginfmt"]', args.username);
await expect(page).toClick('input.btn-primary')
//wait for the redirect
await page.waitForNavigation();
await page.waitFor('input#passwordInput');
await expect(page).toFill('input#passwordInput', args.password);
await expect(page).toClick('#submitButton');
//wait for the redirect
await page.waitForNavigation();
//the stay signed in button
await page.waitFor('input.btn-primary');
await expect(page).toClick('input.btn-primary');
await page.waitForNavigation();
//cookie consent
await page.waitFor('button.navbar-btn');
await expect(page).toClick('button.navbar-btn');
let cookies = await page.cookies();
cookie = cookies.find(o => o.name === '.AspNetCore.Cookies')
process.env.ASPCOOKIE = JSON.stringify(cookie);
}
else {
cookie = JSON.parse(cookie)
}
return cookie;
})();
}
}); and in the cypress test describe('Homepage', () => {
beforeEach(() => {
cy.task("doLogin", { username: Cypress.env("username"), password: Cypress.env("password") }).then(cookie => {
console.log(cookie.domain)
cy.setCookie(cookie.name, cookie.value,
{ domain: cookie.domain, secure: true, sameSite: 'Lax' });
cy.setCookie(cookie.name, cookie.value,
{ domain: '<your domain>' });
});
}); |
@gregorybleiker Thanks a lot! I made it work after following your example. |
Is there a way to connect to the same browser instance that cypress uses from puppeteer? The only way I found to link these together is to add a remote port for a browser launch to cypress plugins file: on('before:browser:launch', (browser = {}, args) => {
if ( browser.name === 'chrome' || browser.name === 'chromium' )
args.push('--remote-debugging-port=9222');
... then in a task you can use puppeteer.connect({ browserURL: 'http://localhost....' }) but I'm not sure that's the best way to manage these |
@blackorion yes, I did something similar. The only "issue" would be running cypress on CI, as you would need to run chrome headless, something that cypress doesn't support yet. In case anyone reaches this looking for a way to do this: on('before:browser:launch', (browser = {}, args) => {
if (browser.name === 'chrome' || browser.name === 'chromium') {
args.push('--remote-debugging-port=9222');
}
return args;
});
on('task', {
doSomething: args => (async () => {
const browser = await puppeteer.connect({
browserURL: 'http://localhost:9222',
}); Then of course in your spec you would call at some point: Alternatives are needed regarding running cypress in CI, but the options appear to be slim: Chrome doesn't support running extensions (cypress) when headless and cypress doesn't support headless chrome yet. Anyway, my 2 cts for anyone passing by. |
@peterfiorito I have been trying your approach without much success. In my app there are some components that do not react as expected when clicked with the normal Cypress mechanism, so I tried to integrate Puppeteer for those particular interactions. However, when I invoke the task, it seems as if the contents of my page are not available to Puppeteer: I can do things like const browser = await puppeteer.connect({browserURL: "http://localhost:9222"});
const pages = await browser.pages();
const page = pages[0];
await page.screenshot({path: 'screenshot.png'});
const contents = await page.content();
await page.waitForSelector("div.container"); where |
@Tormenta74 I assume that you already found the workaround, sorry for the delay but this is my personal github and I use another account for my work. const elementHandle = await page.$(
'iframe[src="https://localwebsite.com/index"]',
);
const frame = await elementHandle.contentFrame();
// now target stuff inside the frame (which is actually your page)
// target a selector inside your page that you want to do something with
await frame.$("selector"); Hope that makes sense. |
@peterfiorito I totally missed the iframes when I was inspecting the cypress page. That clue was it for me, thanks a lot! |
@peterfiorito it seems like OOTB solution is coming soon. Including headless Chrome according to the notes |
@peterfiorito Is there any sample working repo for calling puppeteer form cypress. |
@JayeshRGujar I did this while working for a company, so the repo where the full working example lives is private. I did outline how to do the whole setup in the comments. If you have any particular questions just ask, I am sure that we can figure it out. |
I would love to see some examples of this! |
Yep, me too! |
@borecz I updated the quick repo you shared with me so that it connects to the running cypress instance. Cypress > 3.5For anyone getting here, this thread is from 2018 and the solution as posted was BEFORE 3.5. So bear in mind that a couple of months, let alone a year is a long time in tech/dev and a lot of releases will probably happen. const { myService, setDebuggingPortMyService } = require('./myService');
if (browser.name === 'chrome' || browser.name === 'chromium') {
const existing = args.find(
(arg) => arg.slice(0, 23) === '--remote-debugging-port',
);
// Here you will need to persist the port to your plugins, whatever they may be
setDebuggingPortMyService(existing.split('='));
return args;
}
on('task', { MyService });
const puppeteer = require('puppeteer');
module.exports = {
debuggingPort: '',
setDebuggingPortMyService(port) {
[, debuggingPort] = port;
return null;
},
async myService({ currentPage }) {
const browser = await puppeteer.connect({
browserURL: `http://localhost:${debuggingPort}`,
});
}
} For now, this approach works, but as the project evolves it's very likely you will need to make updates to it. |
@peterfiorito What you mention above is for connecting the two browsers? I couldn't follow up and understand what you're doing. |
@ilianabe I would say that is very specific to your test/use case. Hope that makes sense. |
btw, since I was pulled back to this issue again, there is a minor adjustment needed if you upgrade to cypress @4.2.0, as per the new docs Cypress v4.2.0 on('before:browser:launch', (browser = { headless: true }, launchOptions) => {
if (browser.name !== 'electron' || browser.name === 'chromium') {
const existing = launchOptions.args.find(
(arg) => arg.slice(0, 23) === '--remote-debugging-port',
);
// Here you will need to persist the port to your plugins, whatever they may be
setDebuggingPortMyService(existing.split('='));
}
return launchOptions;
}); Mainly, just that now we have launchOptions as a param, and you will need to call args.find from it and return the launchOptions instead of args as we used to do. |
I have been thinking about this problem lately. Maybe it would be better to try and mock all the calls to the auth service (if you're using oidc for instance everything that is specified in |
So here I have my solution but it feels incomplete, so any help in the matter will be appreciated. index.js const { playwright } = require('../config/playwright')
let debuggingPort
on('before:browser:launch', (browser = {}, launchOptions) => {
if (browser.family === 'chromium' && browser.name !== 'electron') {
// auto open devtools
launchOptions.args.push('--auto-open-devtools-for-tabs')
const existing = launchOptions.args.find(
arg => arg.slice(0, 23) === '--remote-debugging-port',
)
debuggingPort = existing.split('=')[1]
}
})
on('task', {
async play() {
console.log('Debugging port is: ' + debuggingPort)
return await playwright(debuggingPort)
},
}) playwright.js const { chromium, firefox, webkit, devices } = require('playwright')
exports.playwright = async function playwright(debuggingPort) {
const browser = await chromium.launch({
headless: false,
devtools: true,
args: [`--remote-debugging-port=${debuggingPort}`],
})
const page = await browser.newPage()
await page.goto('http://aboutyou.de/' + debuggingPort)
await await page.waitFor(10000)
// other actions...
await browser.close()
return null
} .spec it('Playwright at work', () => {
cy.log('This should be after split... in the spec file!' + Cypress.env().test)
cy.log('Attempt to change' + Cypress.env('whichport'))
cy.task('play')
}) The question here is I see another browser instance spinning off, is it expected behaviour? execution here: https://share.getcloudapp.com/BluZQDX0 |
@borecz yeah that is not what we aimed at all in this thread/issue. |
It depends on the requirements and how much flexibility you get around them. In my case, this was done for a company and they had specific requirements about how this was to be done... I did suggest workarounds, but they didn't really cover what the company needed at the time. |
@peterfiorito Were you able to get this running on CI? |
Yep, we had it running on a gocd pipeline |
@peterfiorito Bootstrap-like alerts are not being displayed when I run my tests in Cypress with multiple 'it' blocks but when I copy all the code in a single 'it' block, the alerts are displayed just fine. I wrote a sample script in NightWatch.js to see if the problem exists in other frameworks but it works fine. So, my best guess is it has something to do with the way cookies are handled by Cypress. Can I use this approach to let puppeteer take care of login and cookie handling entirely instead of Cypress? Any thoughts/suggestions would be greatly appreciated. Note: I tried all approaches to resolve this issue like using preserveOnce and whitelisting the entire cookie. I even tried cypress-localstorage-commands to see if saving and restoring the local storage would resolve this issue but no luck. |
@gmkumar08 I guess you could, but it seems like a lot of work for just getting the cookies to work as expected. |
Hi @peterfiorito I read the whole thread as I am doing exact same thing, Trying to configure Puppeteer (10.2) in Cypress(8.2). Reason is I am trying to automate authentication flow using bank id in application. Norway/ Sweden BankId loads inside our application in iframe. Cypress dont work well with these iframe for click and type event. So trying to configure puppeteer for that iframe event handling. I managed to configure puppeteer (Thanks for your earlier info). Now my objective is to get control of same page running in cypress when puppeteer task is invoked from cypress test. So I see there are 3 iframes inside each other and I want to go to last iframe where there is button which I want to click. `const puppeteer = require('puppeteer'); module.exports = {
I put console.log but they are also not printed when this code get invoked. So not sure where exactly it is breaking. Kindly help incase you are aware about this issue. Thanks |
We've released an official Puppeteer plugin, to make puppeteer calls from within Cypress: https://github.com/cypress-io/cypress/tree/develop/npm/puppeteer Please leave any feedback here: #28410 |
I'm trying to call puppeteer from cypress to do some login stuff
Current behavior:
There is an exception before any test starts
Desired behavior:
be able to use puppeteer in setup code
Steps to reproduce:
include puppeteer in your test project and create a test spec and include
Versions
Cypress 3.1, win10
The text was updated successfully, but these errors were encountered: