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
Implement BrowserType.connect
#800
Conversation
I have a comment about propagating the Should WDYT @inancgumus @ankur22 ? |
46ccdd1
to
09a53b1
Compare
I've been mulling over what makes an error internal again. Thinking back to how I might design errors in a backend service:
So, it feels that we should be thinking about errors as whether they are temporary or permanent. In xk6-browser:
So the question now is, are the There are two actions from my viewpoint:
I think we should get the conversation started and see if others agree. |
I agree on this approach, there can be cases where there is not a clear cut, but agree on the concept.
I believe right now we are using it interchangeably. Personally I'm not sure the Would it make sense to remove this error, and change the implementations to provide context to the errors where these are defined (as you would normally do)? We could then define specific errors for specific use cases, for example a timeout error. Or for example in our case a In regards of this PR, do you think it makes sense to take this discussion away from it, move forward to it and then tackle all this error/panic handling in a separate PR, possibly the one related with issue #688 ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, @ka3de, thanks for the PR, a long-awaited feature! 👏 I'll review this tomorrow 👍 Before that, I did the following and came up with panic: GoError: creating a new page: canceled
errors. You might be interested.
diff --git a/tests/test_browser.go b/tests/test_browser.go
index eef335f..856f51a 100644
--- a/tests/test_browser.go
+++ b/tests/test_browser.go
@@ -118,6 +118,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser {
if !ok {
panic(fmt.Errorf("testBrowser: unexpected browser %T", b))
}
+ b = bt.Connect(cb.WsURL(), rt.ToValue(launchOpts))
tb.Cleanup(func() {
select {
Thanks for the heads up! I didn't want to convert the |
@ka3de, sure! 👍 But I wasn't suggesting testing it like that and putting it into this PR 😅 I've shared the diff in case we discover some edge cases where |
Yep, sure, I understood the purpose 😄 |
Hey @inancgumus , so I ran the tests with this "hack" to use the I was taking a look and this is "expected" due to the hack. What is happening is that the "log hook" set up here for the test browser will end up collecting the logs for the "initial browser" created through If you check the logs, there are references for both generated iteration IDs, one for the "initial browser" through |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work 👏 Overall, LGTM. I have some suggestions and mostly questions to consider.
👍 |
Define constants for the different modes for both flags so it improves readability. Resolves: #800 (comment). Resolves: #800 (comment).
7ee3d92
to
c421358
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicely done 🙂 I just have some minor suggestions.
The one refactor suggestion (which might be too late now) is that it could have been a good opportunity to move browserprocess
into its own package like we tried here.
common/browser_options.go
Outdated
@@ -56,13 +57,14 @@ type LaunchPersistentContextOptions struct { | |||
} | |||
|
|||
// NewLaunchOptions returns a new LaunchOptions. | |||
func NewLaunchOptions(onCloud bool) *LaunchOptions { | |||
func NewLaunchOptions(onCloud, isRemoteBrowser bool) *LaunchOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could create a new function NewRemoteBrowserLaunchOptions
where it will set isRemoteBrowser
to true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is another option, personally I prefer to keep a single "generic" constructor to which pass the appropriate flags, but no strong opinion. If we merge both flags and create this new constructor we can get rid of the constants defined in e24732b. But again, I prefer to keep a single constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for adding another constructor: Clearer, more readable, and we can eliminate the constants. On the other hand, there is this, and it hiddenly implies it's better to have constants and a single constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer a new constructor, but happy to leave it as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm changing this, but the code in BrowserType
is a little bit "uglier" when calling to init
method due to the lack of constants, as we have to pass true
or false
values. Another alternative is to create the launchOptions
in connect
and launch
methods and then provide that to init
method so its modified in it. But don't really like it. Will push the first approach and we can discuss.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See: 4d5a38f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to go this way, I suggest moving option parsing to Connect
and Launch
and passing the parsed launch options to init()
. This way:
- We can eliminate the ugly local/remote bool.
- More importantly. We'll move Goja stuff to the mapping layer (Separate Goja and k6 logic from the module logic #271), and I wouldn't bury it in
init()
. Soon, options parsing will hopefully be in the mapping layer as it will deal with Goja values. Consider leaving it at the exported methods while changing this part of the code, OR just skip this comment since you already dealt with this PR a lot :)—we can take care of it later as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, as discussed, I think this is a good idea, but it's probably a considerable refactor to include it in this PR, so probably better to tackle it separately once this is merged 👍
I guess this can be done.. But I would prefer to tackle it in a separate PR if we really want to do it 😅 |
I wouldn't do that in this PR as it's unrelated to its purpose. Another PR is welcome if we export only a few fields, unlike before, and develop a nicer solution (no pun intended! :)). FYI: @grafana/k6-browser |
Define constants for the different modes for both flags so it improves readability. Resolves: #800 (comment). Resolves: #800 (comment).
c421358
to
e24732b
Compare
Define constants for the different modes for both flags so it improves readability. Resolves: #800 (comment). Resolves: #800 (comment).
e24732b
to
e1ca87c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, and again, nice work 👏
Removes the cloud flag as it's overlapped by the remote flag. Resolves: #800 (comment)
e1ca87c
to
4d5a38f
Compare
Removes the cloud flag as it's overlapped by the remote flag. Resolves: #800 (comment)
12ab5bc
to
8a331f2
Compare
Removes the data dir property from BrowserType as it's unnecessary as a property for the struct, instead we can define it when launching the browser. This will allows the possibility of having two different implementations of storage.Dir, one local and one remote.
Adds a new component BrowserProcessMeta in order to abstract the handling of the browser process handle and the browser data directory. This is defined as an interface that will support implementations for a local browser process and for a remote browser process and therefore minimizing changes in the rest of the implementation, as well as protecting the process and data dir properties from possible NPE errors.
Modify BrowserProcess to use the BrowserProcessMeta abstraction. With the current implementation only LocalBrowserProcessMeta applies as right now the browser to connect to is always launched from our implementation.
This commit provides an abstraction for BrowserProcess cleanup and modifies Browser implementation in order to use it and in this way avoids reaching the internal BrowserProcess property 'meta' from it.
Fixes the BrowserType.Connect method signature in order to receive as input the WS URL to connect to, and return a Browser instance.
Removes the logger instance held by BrowserType as it's unnecessary. Instead a local logger is created and passed along during the launch process. Also removes the unnecessary BrowserProcess.AttachLogger method as the logger was already passed to the constructor and could be set from there. This will allow to not tight the loggers used for BrowserType.Launch and BrowserType.Connect methods.
Defines possible browser launch options as constants instead of string literals. This will be useful when adding additional parsing methods to handle the differences for BrowserType launch and connect methods.
Modifies browser options in order to support parsing for options when the browser is indicated to be in a remote machine.
This allows to differentiate the constructor for a local browser process and a remote browser process.
Extract common code for browser process termination handling in its own function.
This is for readability purposes, so launch and connect implementations are together, as they share common behaviors.
Refactos Launch and Connect methods in order to extract common implementation parts (mainly initialization procedures) into a single function, reduce code repetition and simplify the main methods flow.
Also adds a new method in common.Browser API so we can get the underlying browser process WS URL. This is required in order to reuse the current TestBrowser implementation to launch a new browser and connecto to it through BrowserType.Connect.
Removes the cloud flag as it's overlapped by the remote flag. Resolves: #800 (comment)
Modifies the Browser.Close implementation in order to avoid sending the Close CDP cmd when the browser is being executed remotely.
8a331f2
to
1bbf855
Compare
This PR implements the
BrowserType.Connect
method for chromium browser type in order to, instead of launching a browser from our implementation, establish the WS connection to a remote running browser.The approach taken has been to minimize changes in the current implementation/flow. For that:
BrowserProcessMetadata
has been introduced in order to abstract the elements of the currentBrowserProcess
implementation that differ for a local/remote browser process [*].BrowserProcess
properties from other components (mainly fromBrowser
implementation).BrowserProcess
struct that references a remote browser.BrowserType.Connect
method following behavior defined by the currentlaunch
method.launch
andconnect
methods.[*] There were alternatives other than creating a new interface to abstract these differences, for example to add a
flag
inBrowserProcess
that would indicate if theos.Process
property held byBrowserProcess
was valid or not (due to being remote). But I believe the new interface definition abstracts this in a better way and prevents possible panics due toNPE
, especially considering that there is not an abstraction in place for the wholeBrowserProcess
element, and this is reachable from other elements implementations, such asBrowser
.Closes: #17.