Skip to content
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

[Question] Attaching playwright to an existing browser window? #1985

Closed
benjamingr opened this issue Apr 25, 2020 · 44 comments
Closed

[Question] Attaching playwright to an existing browser window? #1985

benjamingr opened this issue Apr 25, 2020 · 44 comments

Comments

@benjamingr
Copy link

benjamingr commented Apr 25, 2020

Hey,

First of all thank you for this wonderful library <3

I'm working on a feature for playwright recorder to let you more easily run playwright tests from the browser.

I want to attach playwright to a context that is already running (opened with ChromeDriver if that matters):

    const browser = await playwright.chromium.connect({
        wsEndpoint: cdpUrl,
    });
    const contexts = browser.contexts(); // empty

    // how do I get the existing already open pages?

    const context = await browser.newContext({viewport: null })
    const pages = await context.pages(); // empty

I checked puppeteer and it was easy to get working like this:

    const browser = await puppeteer.connect({
        browserWSEndpoint: cdpUrl,
        defaultViewport: null, // disable puppeteer resize
        product: 'chrome', 
    });
    const pages = await browser.pages();

    const page = last(pages);

Is there an official method to obtain existing browser contexts?

@pavelfeldman
Copy link
Member

No official method. And I am not sure we would want to support something like this. Curious what you are doing though!

@benjamingr
Copy link
Author

I have a browser I launched with ChromeDriver (the Testim editor) and I want it to run a test authored in playwright in the same browser.

In puppeteer this is pretty easy but in playwright I don't have a way to ask for an existing context.

What if I hit the json endpoint at /json and not /json/version and get the debugger URL for the page? Is there a way to get playwright to talk to that webaocket URL?

@benjamingr
Copy link
Author

I'll try to describe the use case:

  • Imagine I already have a large codebase written in Selenium.
  • I would like to author new features with playwright instead or to leverage a library written in playwright.
  • When launching the selenium test I get the debugger URL from ChromeDriver after launching my browser and connect playwright to it.
  • I can now author new features with Playwright - and for example port the function that logs me into the website being tested from selenium to playwright.

The feature I'm asking for enables mixing and matching playwright with selenium or puppeteer more easily. I specifically need it to run the playwright playground (mentioned above) on tabs controlled inside ChromeDriver

@erichaus
Copy link

hey I'll add to this as a new playwright user, I'm wondering if it's possible to run the tests in a real browser and e.g. throw a debugger to pause the test and inspect it etc? With selenium I know this is possible, and in other testing frameworks while running render tests it's possible to pause and view the browser window etc... Just curious as right now I'm building tests and running them but it's effectively in the dark, I don't know how to pause and inspect what the test is doing (also might just be that I'm uninformed on this subject!).

@benjamingr
Copy link
Author

I don't know how to pause and inspect what the test is doing (also might just be that I'm uninformed on this subject!).

When you launch a browser with playwright - you can attach to it using the open debugging port. If you're using Chrome you can open a new tab and navigate to chrome://inspect or if you're using VSCode you can get the the vscode chrome debug extension which can attach to a running browser controlled by playwright.

This ticket is about playwright itself attaching to a session created with another tool - for example imagine being able to write new code for your selenium tests with playwright and mix the two :]

@erichaus
Copy link

@benjamingr ah ok gotcha! I figured it was just my own lack of knowledge. I'd be interested to learn more about how to do this when developing using e.g. mocha. Mixing playwright and selenium sounds interesting, I could see how it'd be useful if you have an existing large selenium codebase and don't want to port it over entirely... cheers!

@tymfear
Copy link

tymfear commented May 2, 2020

Probably I got issue related to this one.
I have Selenoid cluster deployed in cloud and would like to use it as infrastructure for PW tests.
So I create chrome instance on the cluster with plain HTTP call an trying to connect to that session like:

 const sessionId = await createSession();
 const browser = await chromium.connect({wsEndpoint: `ws://localhost:4444/devtools/${sessionId}`});
const page = await browser.newPage();

And as a result I'm getting an error

  Error {
    message: 'Protocol error (Browser.setDownloadBehavior): \'Browser.setDownloadBehavior\' wasn\'t found',
  }

So is that related to the fact that there is no way to connect to existing session or I just doing something wrong?

@benjamingr
Copy link
Author

So is that related to the fact that there is no way to connect to existing session or I just doing something wrong?

You are not doing something wrong. This is the same issue. I ran into the same issue when evaluating selenoid.

Your only option is to not control the grid with a tool like selenoid and inverse who creates the session. That is: create the session with playwright, create chromedriver on the same port and have it attach to the session.

Note that this is not a technical limitation. It's a choice the developers are making to not support this use case (for now).

I would write code to bypass it (it's possible to "hack around it" as it's not a protocol limitation) but I'm trying to see if:

  • We can get a supported way to do it.
  • If there is a legitimate concern and reason to not allow this that I don't understand - and if there is, how can I do this safely.

@mxschmitt
Copy link
Member

The issue is that for WebKit and Firefox custom patched versions are used so for them it would be quite hard. Chromium related changes are not custom patched, but they are made directly upstream in Chromium, so in the end if you are using Chromium Canary (latest build) it should work.

Have you tried it with the latest Chromium Canary?

(To connect to existing browsers, launchServer can be used, but thats probably not what you want since you have ChromeDriver based instances.)

@benjamingr
Copy link
Author

@mxschmitt browserType.launchServer looks cool regardless - is there a tutorial about it?

You are correct that this is not a limitation of the Chrome version itself. Playwright happily creates new sessions after connecting to the browser. It's just not able to find existing contexts and pages.

This is not a protocol limitation but an API choice I'm hoping you can relax.

I am fine with this working only in Chromium initially - in particular even if this only works with Chromium with this revision that's fine :]

@AutoSponge
Copy link

I tried this today. The problem I'm seeing is that connecting to an existing wsEndpoint does not attach to the existing page targets.

I started playwright with a browserServer. Then I started another instance of playwright and connected to the existing wsEndpoint.

const context = await browser.newContext(contextConfig);
const pages = await context.pages();
pages.length // 0;

Yet, the code in CRBrowser appears to try attaching to existing pages. However, I think the logic only fires on Target.targetCreated event in response to the CDP method. There's no Target.getTargets then a loop over the existing targets to create PageDelegates.

@tymfear
Copy link

tymfear commented May 7, 2020

Well, @pavelfeldman could you provide some info on Playwright team's view on that feature.
This is a big thing for projects with existing infrastructure and large codebase.
At least that would help to understand if it worth expecting such feature at all, or should I (and others who has existing infra and wouldn't want to get rid of it) stop waiting for it?
(I'm checking this ticket daily, but it's a bit annoying)

@pavelfeldman
Copy link
Member

Sorry, I dropped the ball on this one. Let me try to answer the questions and explain the rationale. I'll go down the thread and try to address everything.

@benjamingr

Imagine I already have a large codebase written in Selenium. I would like to author new features with playwright instead or to leverage a library written in playwright.

Playwright relies upon owning and configuring the browser contexts to ensure that the emulation and other parameters survive site isolation and apply to popup windows. When you say your context is offline, we want to make sure that pages opened with window.open from yours are also offline. When you set up a route to block certain sites, we want that to apply to popups or when you navigate and switch underlying process. To achieve that, we throttle the renderer creation inside the browser to make sure that by the time it runs, it already has all the emulation bits set up. Selenium can't do that, it does not have these capabilities, so in your story Playwright needs to be the primary driver since it needs to control more environment.

Sadly, Selenium will jump in and since it also assumes it is the only driver, it will start messing with the Playwright's viewport emulation settings.

The feature I'm asking for enables mixing and matching playwright with selenium or puppeteer more easily. I specifically need it to run the playwright playground (mentioned above) on tabs controlled inside ChromeDriver

It sounds like you only want to show case the basic page.* methods in your playground and don't care about the emulation. Basically a lightweight version of the API. Unfortunately, there is no fine boundary like that and as soon as you do something with the file chooser, the drivers will fight.

@pavelfeldman
Copy link
Member

@burritoIand

I'm wondering if it's possible to run the tests in a real browser and e.g. throw a debugger to pause the test and inspect it etc?

Launch the browser with the { headless: false } parameter, pause in your test, open the devtools to debug. Basically same story, you can open DevTools along with the test in Chromium and Firefox. Opening WebKit Web Inspector will terminate the test, but it'll let you poke around the page anyways - you would just need to restart the test.

@pavelfeldman
Copy link
Member

@tymfear

I have Selenoid cluster deployed in cloud and would like to use it as infrastructure for PW tests.

Playwright is picky about the version of Chromium it runs against, it is only guaranteed to work against the one it downloads itself. You point at a Chromium instance that is too old and does not have a capability Playwright uses.

@pavelfeldman
Copy link
Member

@AutoSponge

I tried this today. The problem I'm seeing is that connecting to an existing wsEndpoint does not attach to the existing page targets.

Yes, this is deliberate, we consider web socket clients to be isolated tenants. They don't see each other contexts, they basically use browser as a factory of these contexts for users connected over the wire.

@pavelfeldman
Copy link
Member

@tymfear

At least that would help to understand if it worth expecting such feature at all, or should I (and others who has existing infra and wouldn't want to get rid of it) stop waiting for it?

Mixing the two drivers will not work with the present scope of Playwright capabilities. But there might be a reasonable variation / compromise that we could achieve. For that, we need to better understand the end result you are trying to achieve.

Another aspect of targeting existing Selenium clouds is that Playwright follows the "tests are a part of the CI" paradigm. We imply that you are using Travis / GitHub / Circle / in-house CI infrastructure that runs the tests for your commits. In this story, test is located next to the running browser and we can be pretty chatty in terms of the CDP messages. Doing what Selenium does and making each CDP request fly over the network would significantly contribute to the latency and flakiness. In order to mitigate that, we would need to (re)invent a higher-level protocol and seed our own agent in the Selenium cloud, at which point it stops being an "existing infrastructure".

(I'm checking this ticket daily, but it's a bit annoying)

😢sorry about that

@pavelfeldman
Copy link
Member

All in all, eager to help reusing the existing infrastructures, but we need to basically start with understanding the constraints to make sure what we do makes sense.

@benjamingr
Copy link
Author

Sadly, Selenium will jump in and since it also assumes it is the only driver, it will start messing with the Playwright's viewport emulation settings.

I can probably take care of that (either by PRs into ChromeDriver, Marionette or other tools) if that is the case. I think it's fair for automation tools to assume they are getting a "fresh context".

Selenium can't do that, it does not have these capabilities, so in your story Playwright needs to be the primary driver since it needs to control more environment.

The use cases I have are pretty simple and share a common theme:

  • There is a large selenium code base or infrastructure. This can be automation projects in companies, grids or root cause analysis tooling.
  • I want to convince the user to use playwright for new automation (for example to record or write their new "login" function with playwright).

It sounds like you only want to show case the basic page.* methods in your playground and don't care about the emulation. Basically a lightweight version of the API.

Yes exactly. I am fine if emulation doesn't work in that particular area, it would be better if it did (since I mean, both drivers are just CDP wrappers and execute the same Emulation.setDeviceMetricsOverride in all likelihood, even in Selenium we would just connect with chrome.debugger and execute the same command).

To be clear: all this works (pretty well) with Puppeteer, we mix and match puppeteer and selenium all the time and it's this (and admittedly - the change makes sense) shift in APIs that makes this impossible.

@benjamingr
Copy link
Author

benjamingr commented May 14, 2020

Since it's unrelated:

Another aspect of targeting existing Selenium clouds is that Playwright follows the "tests are a part of the CI" paradigm. We imply that you are using Travis / GitHub / Circle / in-house CI infrastructure that runs the tests for your commits. In this story, test is located next to the running browser and we can be pretty chatty in terms of the CDP messages. Doing what Selenium does and making each CDP request fly over the network would significantly contribute to the latency and flakiness. In order to mitigate that, we would need to (re)invent a higher-level protocol and seed our own agent in the Selenium cloud, at which point it stops being an "existing infrastructure".

This made me laugh out loud. I argued a bunch for this at Testim and I lost the argument. We have a mode where we use puppeteer-web and custom chrome.debugger transport to execute puppeteer code on the same browser from inside the browser. Alternatively we run our own automation using chrome.debugger (in certain modes) with close to zero latency (over the regular debugger and not remote debugger protocol).

It's not safe but it has zero latency and certainly lower than running playwright/puppeteer from a different machine.

I have found making each CDP request "fly over the network" to not that big of a deal in practice (eventhough for example a "click" Selenium commands is 3 CDP commands). I've found the overhead of network to only be a big deal if grids are far from your CI.

The current model (no grids) makes it very hard to scale playwright and puppeteer and I believe we're going to see many grid vendors provide playwright grids in the future.

(Also, there are grid vendors where the model is that you ship your Node code to the device - so there is very little latency.)

@benjamingr
Copy link
Author

(and by the way - playwright will happily connect to the debugger in the browser and create a new context which selenium will happily find later on - so my current bad "workaround" is to connect playwright, open the tab with it and then use selenium to enter that tab)

@benjamingr
Copy link
Author

An unrelated note: as an open source maintainer myself:

Sorry, I dropped the ball on this one. Let me try to answer the questions and explain the rationale. I'll go down the thread and try to address everything.

I really don't believe you owe me or anyone else here answers for this in a short manner. Especially not after answering the initial question within a day. I know some other comments sounded angry or disappointed but it's a tricky question and just because I asked for something or raised a challenging issue doesn't mean it should be a priority for you.

If this was posted in one of the repos I maintain:

(I'm checking this ticket daily, but it's a bit annoying)

I can only hope that I would have responded as charitably as you did (but probably not) :]

So thank you ❤️

@tymfear
Copy link

tymfear commented May 14, 2020

@pavelfeldman - do I understand it right, that if I find suitable chromedriver version (which was chrome v 81 I guess), I will be able to connect to existing browser? I don't use selenium at all, Selenoid cluster is handy for starting docker instances with chrome and providing the WS endpoint.
So basically, what I want is the following scenario

  • Using http client create docker container with chrome running there via Selenoid cluster
  • Connect Playwright to the socket of chromedriver inside docker container
  • Do stuff with Playwright
  • Using http client kill the session via Selenoid cluster

Is that something that could be achieved by suitable chromedriver version?

Or, maybe, you have some examples of infra that will allow 100+ tests running in parallel? Maybe someone shared some examples?

@benjamingr
Copy link
Author

@tymfear I can confirm that I have done that particular thing (selenoid with playwright) successfully. The only limitation is controlling existing tabs (and not creating a new context). Playwright will connect to the CDP url selenoid exposes.

You can just try it out, you still have to take care of session management but it's just wrapping things in a few more calls.

@pipi1007
Copy link
Member

I am having the same problem as @tymfear . I am getting below the exception when I launch a preinstalled Chrome (latest stable version) from playwright:

Exception has occurred: Error
Error: Protocol error (Browser.setDownloadBehavior): 'Browser.setDownloadBehavior' wasn't found

I am using the executablePath in launchOptions. Is this supported by playwright, or I am doing something wrong?

@pavelfeldman
Copy link
Member

@tymfear, @pipi1007 : Current stable (Chrome 81) is too old for Playwright 1.0. It requires at least Chrome 84 to pass the complete test suite. It will run against 83, but some of the features (downloads) won't be working. Chrome 83 goes stable next week, while Chrome 84 is planned for Jul 14, 2020.

@pavelfeldman
Copy link
Member

@benjamingr

To be clear: all this works (pretty well) with Puppeteer, we mix and match puppeteer and selenium all the time and it's this (and admittedly - the change makes sense) shift in APIs that makes this impossible.

That makes sense. Puppeteer does not have a notion of the context-wide emulation, so it does not throttle targets. This bit is particularly risky since ChromeDriver will detect target as created while Playwright will be throttling it. It is likely to work for simple scenarios, but since we never targeted and/or tested this scenario, I'd be hesitant recommending it.

There is nothing impossible about it altogether, we just need to prioritize it and get you a 'Playwright lite' that can co-exist with Selenium.

This made me laugh out loud. I argued a bunch for this at Testim and I lost the argument.

Oh, too bad, tell them the guy that did CDP tells them that you were right. In all seriousness, I'd like to hold Playwright to a higher standard. If we engage into operation against the grid, we will eliminate chattiness. We are against flakes and latency after all. Operation against the grid also correlates with picking up other languages and that's where the higher level protocol becomes also handy.

The current model (no grids) makes it very hard to scale playwright and puppeteer and I believe we're going to see many grid vendors provide playwright grids in the future.

This one is particularly interesting. So far, we've been focusing on making Playwright a part of the local workflow and CI/CD. Our philosophy is that tests sit next to the runtime and there is no difference between your functional and e2e tests. You don't use the grid for your Jest / JSDom tests and we want e2e to feel just as easy and lightweight.

But at the same time, we don't want to be detached from the reality. So if we find this hybrid model that you are describing to be wide spread and actually enabling WebDriver community to improve the quality of their tests, we will obviously do what we can to support it.

@smyth64
Copy link

smyth64 commented May 24, 2020

First I was really excited about using playwright instead of puppeteer. But not being able to connect to existing browsers and using their context/pages really disappoints me ☹️

@arjunattam
Copy link
Contributor

Thanks for the feedback @smyth64! We are actively exploring this space. Can you please elaborate on your use-case/goals? How would this feature help your goals?

@smyth64
Copy link

smyth64 commented May 25, 2020 via email

@GkqqNkKC
Copy link
Contributor

Sry for bumping in, but this problem (not being able to access contexts not made by playwright) hits me hard too. I was a puppeteer user similar to @smyth64 . I came here as I was looking for an alternative as puppteer had some issues and the people behind it doesn't pay too much attention to the issues opened by users

playwright seems wonderful, that's the reason I'm here. Currently I use these remote controllers to automate jobs, and doing pentesting in particular. Both tasks requires to use the already loaded context (as I'm logged in accounts; cookies are in place; sometimes I need to start the automation script mid-session)

I understand that playwright shouldn't manage contexts not made by it. In this case what about mark it as an experimental feature? The user is responsible of it's usage. I also deduce from here that puppeteer is not safe with its control

@benjamingr
Copy link
Author

@GkqqNkKC as a workaround (I hope they support this feature obviously :]) you can open Chrome with playwright (that opens it with a debugging port) and then control the session with selenium (You can attach to an existing session).

(Also you can also pretty quickly patch playwright's code to use the already existing Context, but I would not recommend that route :])

@GkqqNkKC
Copy link
Contributor

@benjamingr , thx for workarounds. I will make use of them until new solutions arrive

@pavelfeldman
Copy link
Member

I'll tentatively close this as won't fix. It is essential for Playwright to be able to run the browser in the controlled environment. There are a lot of good hints here on how this can be worked around, but it is unlikely that they get into the official Playwright API.

@cybairfly
Copy link

Well, this feature would be really helpful along with the PW CLI since it creates a separate browser instance and context of its own. Any way to access and control the context of the CLI tool codegen?

@jancurn
Copy link

jancurn commented Feb 18, 2021

+1

@benjamingr
Copy link
Author

If anyone cares (I'm not with Testim.io anymore so I won't work on tools relating to this most likely) there is code at https://github.com/testimio/root-cause that achieves this with the internal API IIRC, note it's locked to a specific playwright version range (look at the history in order to see how it changes).

@cybairfly
Copy link

Thanks a lot for sharing! @benjamingr will sure have a look.

It would be still great for PW to provide access to the context handle directly.

@dmitrysteblyuk
Copy link

This is possible with connectOverCdp() since v1.9.0.

The default context is accessible now and it has pages there too! 🎉

I wish I also knew the difference between connectOverCdp and connnect (when connecting to chromium) and why it's working for one and not the other, but the documentation doesn't say much about this (and it's not clear from looking into the code too).

@mxschmitt
Copy link
Member

browserType.connectOverCDP = uses Chrome CDP protocol and works by that only with Chromium based browsers
browserType.connect = uses Playwright own protocol and works with all Playwright browsers

@dmitrysteblyuk
Copy link

dmitrysteblyuk commented Aug 18, 2021

browserType.connect = uses Playwright own protocol and works with all Playwright browsers

this is the most confusing part. Do you mean Playwright browsers (which are just latest builds according to docs) implement Playwright own protocol? This doesn't seem likely.

@mxschmitt
Copy link
Member

connect is the connection side where you can connect to a server which got launched via launchServer, see here as an example. It works with Chromium, Firefox, and WebKit: https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server

Their version depends on which version you have installed. Its always using the pinned version from the NPM package and release-notes.

@dmitrysteblyuk
Copy link

But this is perfect, thank you so much! Now it's all clear.
So I can connect multiple playwright clients to the same playwright server (even if the latter is launched with remote-debugging-pipe).
Can I use different playwright versions for instantiating clients, and a different version of playwright for launching the server?
Will they always work together if they have the same major version?

@mxschmitt
Copy link
Member

mxschmitt commented Dec 14, 2021

Can I use different playwright versions for instantiating clients, and a different version of playwright for launching the server?

We only guarantee that the same Playwright client version works with the same Playwright server version. Usually if they are the same minor version they work together.


Locking the issue for now to reduce the noise for the other folks, for further questions, please create new individual issues.

@microsoft microsoft locked and limited conversation to collaborators Dec 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests