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

Evaluate move of slowMo and timeout browser options to browser context #857

Closed
Tracked by #854
ka3de opened this issue Apr 19, 2023 · 13 comments
Closed
Tracked by #854

Evaluate move of slowMo and timeout browser options to browser context #857

ka3de opened this issue Apr 19, 2023 · 13 comments
Assignees
Labels
poc Proof of concept team/k6browser To distinguish the issue on project boards.

Comments

@ka3de
Copy link
Collaborator

ka3de commented Apr 19, 2023

Based on the work done in #856 , slowMo and timeout are the parameters from the current browser options that have a direct impact in the test execution and therefore which its definition should be kept in the test script itself.

At the same time, these options effect is not translated to the browser instance and instead is part of our own k6 browser implementation (handled in the Go code). Therefore the goal of this issue is to evaluate if slowMo and timeout parameters are a better fit to be defined at the browser or browserContext level. This can include considerations on, but not limited to:

  • Usability / Use cases (taking into account the differences in scope between browser and browserContext).
  • Implementation/modification from the current code base.

Related: #856

@ka3de ka3de self-assigned this Apr 19, 2023
@ka3de ka3de changed the title Evaluate "migration" of slowMo and timeout browser options to browser context Evaluate move of slowMo and timeout browser options to browser context Apr 19, 2023
@ka3de ka3de added team/k6browser To distinguish the issue on project boards. poc Proof of concept labels Apr 19, 2023
@ka3de
Copy link
Collaborator Author

ka3de commented Apr 20, 2023

A clarification on browser timeout option:

As it's indicated in the documentation, the browser timeout option applies for:

  • "Various actions": Which is not very well defined, but taking a look at the source code we can see a few examples for things such as allocating a new browser process when using BrowserType.Launch method, waiting for the creation of a new page in context.
  • Navigation: Used as default TO for navigation events. This TO can be overwritten later on at the browserContext (through setDefaultTimeout or setDefaultNavigationTimeout) and page (through goto options) levels.

Taking this into account, if we want to keep supporting definition for the "various actions" TO defined in the previous point, we have to keep a timeout parameter at the browser level. Another option, which I'm personally in favor of, is to not allow specifying the TO for these actions. I don't think the user is interested in specifying a TO for browser process allocation (for example), and this is something that we should set up sensibly based on the implementation. This will allow us to possibly move the timeout browser option to browserContext if desired, or at least narrow it's scope to only apply to navigations, which I would guess it's the main goal of users.

Moving slowMo to browserContext level:

If we focus on the criteria of "usability / use cases", the difference between keeping slowMo option at the browser level or moving it to the browserContext level is clear, and is about the "scope of action".

Keeping the current implementation, slowMo is defined during the browser creation process, whether that's through launch or connect methods. Therefore it applies to the browser component throughout its "life span" and it's also "inherited" by every element created from that browser object, such as browserContext, page, frame, etc.

If we decide to move this option to the browserContext level instead, it would apply only to the browserContext scope and will be inherited by the elements depending on this. For example page and frame.

Therefore, we can argue that moving these options to the browserContext level will provide more flexibility, as different option values could be set for the same browser used throughout the test.

If we focus on the implementation, the current approach could not be reused for browser context without a considerable refactor. The following diagram tries to explain the current "flow of actions" of how slowMo is set as part of a Go context created when initializing the browser, and how this is propagated through the different components that compound the k6 browser implementation. Afterwards, when slowMo action has to be applied, a common function applySlowMo is called with the passed browser context, which is used to retrieve the timeout to apply:
Screenshot from 2023-04-20 10-58-48

Therefore, if my understanding is correct, in order to not do an even bigger refactor, maybe the easiest alternative could be to listen to new "page attached events" from browserContext implementation, so we can propagate the modified context (once we know what is the slowMo value set in the browserContext constructor options). Other alternatives could be implemented also.

My conclusion:

Based on the previous points, my preference would be to take the following actions:
In regards of timeout browser option:

  • Keep this option as supported at the browser level.
    This would act as the default timeout and, if required, it can be overwritten at the browserContext or page level (as mentioned in the first point of this comment).
  • Do not apply this timeout to any event that is not a navigation.
    Personally I don't think it makes sense to let the user define a TO that is related to the browser process allocation or other similar actions which are based on our implementation.
  • Modify the browserContext constructor in order to support the definition of timeout.
    This is not a must, but would be a nice to have, so the user can define it "directly" in the constructor instead of having to make an extra call to browserContext.setDefaultTimeout or browserContext.setDefaultNavigationTimeout.

In regards of slowMo browser option:

  • Keep this option as supported at the browser level.
    Moving this to the browserContext level would require some refactor for an unknown benefit. In theory we can argue that it would provide more flexibility for the slowMo functionality, but it's not clear (at least for me) if this is something that users actually want. Therefore I think the safest approach by now is to keep the current implementation.
    If we are unsure about this decision in the long term, another option would be to not support this by now, in order to minimize breaking changes in the future, meanwhile a final decision is taken.

@ankur22
Copy link
Collaborator

ankur22 commented Apr 20, 2023

Great work analysing the two browser properties!

DISCLAIMER Ignore this comment and read this comment.

Timeout

Keep this option as supported at the browser level.

So change this to an env var? I think it could still be useful to set a timeout depending on the machine that is running the browser. Although 30seconds (I think this is the default) should be enough in most cases.

Do not apply this timeout to any event that is not a navigation.

I don't think this browser level timeout should be inherited to anywhere else in the application and definitely shouldn't affect the navigation. I think once we abstract away the browser, then the timeout that users should interact with are through the browserContext or other elements that users work with.

I think the browser level timeout should only affect the browser specific actions, such as launching/connecting to a browser and creating a new context (so browserType and browser).

If we allowed the browser level timeout to alter the timeout in browserContext, page, frame etc, then the behaviour of a test will differ depending on which environment that the test is ran in.

Modify the browserContext constructor in order to support the definition of timeout.

I like this idea.

slowmo

Keep this option as supported at the browser level.

I think delaying this is a good short term solution, and we could convert it to an env var.

I think working with the go context to change the behaviour of the application is a bad smell, and also not what the context is supposed to be used for (IMO), so i would prefer a well thoughtout refactor where we avoid working with the go context where the option can be safely moved to the browserContext level.

maybe the easiest alternative could be to listen to new "page attached events" from browserContext implementation

Did you get a chance to try this out and see if it worked? Or are there lots more work to be done for this to work?

@ka3de
Copy link
Collaborator Author

ka3de commented Apr 20, 2023

We seem to have quite different opinions on this 😄

Timeout

So change this to an env var?

Personally, following the criteria mentioned in #856 , I think it's best to keep the timeout option defined in the source code. For me this is a parameter that can have an effect in the test run, and I can see users wanting to "hardcode" so it's more easily "reproducible" instead of having to define it in the environment.

I think it could still be useful to set a timeout depending on the machine that is running the browser. Although 30seconds (I think this is the default) should be enough in most cases.

I personally don't think there is a need for that, and as you said, keeping a default of 30s for that should be more than enough. But no strong opinion. What I don't like of the current implementation is that the browser timeout is set for both, "browser process allocation" TO and also default navigation TO. Which does not seem to make much sense 🤷‍♂️

I don't think this browser level timeout should be inherited to anywhere else in the application and definitely shouldn't affect the navigation.

Completely agree with this 👍

I think once we abstract away the browser, then the timeout that users should interact with are through the browserContext or other elements that users work with.
I kind of agree with this, but personally I would prefer to be able to set a default global timeout for navigation TO in the scenario params if desired, instead of having to define it individually for each browserContext [for example if I want to set the same value].

I think the browser level timeout should only affect the browser specific actions, such as launching/connecting to a browser and creating a new context (so browserType and browser).

I don't think this should be exposed to the user, but instead use sensible defaults. But no strong opinion.

slowMo

I think delaying this is a good short term solution, and we could convert it to an env var.
I think working with the go context to change the behaviour of the application is a bad smell, and also not what the context is supposed to be used for (IMO), so i would prefer a well thoughtout refactor where we avoid working with the go context where the option can be safely moved to the browserContext level.

We can delay the decision on this indeed, although I don't see this as an ENV var because I see it as an option that can have a direct impact in the test execution.
Yep, I think we should review more carefully the implementation for this, I don't particularly like it, although in this case I don't see it as bad to use the ctx to pass parameters that have an "effect" around different elements of k6 browser.

Did you get a chance to try this out and see if it worked? Or are there lots more work to be done for this to work?

I didn't, it seemed a considerable refactor, but I can take a better look. I did try to implement it following the same approach as we are using now, through context, but for the browserContext parameters (including in these the slowMo and timeout values) and that's when I saw that it was not gonna work due to the "flow" represented in the diagram. See the following diff if you are curious:

diff
diff --git a/common/browser_context.go b/common/browser_context.go
index b98e8a6..1ed7986 100644
--- a/common/browser_context.go
+++ b/common/browser_context.go
@@ -52,13 +52,16 @@ func NewBrowserContext(
 ) (*BrowserContext, error) {
        b := BrowserContext{
                BaseEventEmitter: NewBaseEventEmitter(ctx),
-               ctx:              ctx,
-               browser:          browser,
-               id:               id,
-               opts:             opts,
-               logger:           logger,
-               vu:               k6ext.GetVU(ctx),
-               timeoutSettings:  NewTimeoutSettings(nil),
+               // TODO: Maybe there is no need to set all BC options
+               // and we could just set the slowMo param with a specific
+               // key in context
+               ctx:             WithBrowserContextOptions(ctx, opts),
+               browser:         browser,
+               id:              id,
+               opts:            opts,
+               logger:          logger,
+               vu:              k6ext.GetVU(ctx),
+               timeoutSettings: NewTimeoutSettings(nil),
        }
 
        if opts != nil && len(opts.Permissions) > 0 {
diff --git a/common/browser_context_options.go b/common/browser_context_options.go
index 74c65ef..449e34d 100644
--- a/common/browser_context_options.go
+++ b/common/browser_context_options.go
@@ -3,6 +3,7 @@ package common
 import (
        "context"
        "fmt"
+       "time"
 
        "github.com/grafana/xk6-browser/k6ext"
 
@@ -31,6 +32,8 @@ type BrowserContextOptions struct {
        UserAgent         string            `js:"userAgent"`
        VideosPath        string            `js:"videosPath"`
        Viewport          *Viewport         `js:"viewport"`
+       SlowMo            time.Duration     `js:"slowMo"`
+       Timeout           time.Duration     `js:"timeout"`
 }
 
 // NewBrowserContextOptions creates a default set of browser context options.
@@ -45,6 +48,7 @@ func NewBrowserContextOptions() *BrowserContextOptions {
                ReducedMotion:     ReducedMotionNoPreference,
                Screen:            &Screen{Width: DefaultScreenWidth, Height: DefaultScreenHeight},
                Viewport:          &Viewport{Width: DefaultScreenWidth, Height: DefaultScreenHeight},
+               Timeout:           DefaultTimeout,
        }
 }
 
@@ -127,6 +131,14 @@ func (b *BrowserContextOptions) Parse(ctx context.Context, opts goja.Value) erro
                                        return err
                                }
                                b.Viewport = viewport
+                       case "slowMo":
+                               b.SlowMo, _ = parseTimeOpt(k, opts.Get(k))
+                       case "timeout":
+                               var err error
+                               b.Timeout, err = parseTimeOpt(k, opts.Get(k))
+                               if err != nil {
+                                       b.Timeout = DefaultTimeout
+                               }
                        }
                }
        }
diff --git a/common/context.go b/common/context.go
index 8efe868..cc29008 100644
--- a/common/context.go
+++ b/common/context.go
@@ -10,6 +10,7 @@ const (
        ctxKeyLaunchOptions ctxKey = iota
        ctxKeyHooks
        ctxKeyIterationID
+       ctxKeyBrowserCtxOptions
 )
 
 func WithHooks(ctx context.Context, hooks *Hooks) context.Context {
@@ -47,6 +48,18 @@ func GetLaunchOptions(ctx context.Context) *LaunchOptions {
        return v.(*LaunchOptions)
 }
 
+func WithBrowserContextOptions(ctx context.Context, opts *BrowserContextOptions) context.Context {
+       return context.WithValue(ctx, ctxKeyBrowserCtxOptions, opts)
+}
+
+func GetBrowserContextOptions(ctx context.Context) *BrowserContextOptions {
+       v := ctx.Value(ctxKeyBrowserCtxOptions)
+       if v == nil {
+               return nil
+       }
+       return v.(*BrowserContextOptions)
+}
+
 // contextWithDoneChan returns a new context that is canceled either
 // when the done channel is closed or ctx is canceled.
 func contextWithDoneChan(ctx context.Context, done chan struct{}) context.Context {
diff --git a/common/hooks.go b/common/hooks.go
index abfa220..911e8ed 100644
--- a/common/hooks.go
+++ b/common/hooks.go
@@ -30,7 +30,9 @@ func applySlowMo(ctx context.Context) {
 }
 
 func defaultSlowMo(ctx context.Context) {
-       sm := GetLaunchOptions(ctx).SlowMo
+       // Panic here due to NPE when trying to retrieve
+       // browserContext options from ctx
+       sm := GetBrowserContextOptions(ctx).SlowMo
        if sm <= 0 {
                return
        }

@ankur22
Copy link
Collaborator

ankur22 commented Apr 20, 2023

DISCLAIMER Ignore this comment and read this comment.

Timeout

Personally, following the criteria mentioned in #856 , I think it's best to keep the timeout option defined in the source code. For me this is a parameter that can have an effect in the test run, and I can see users wanting to "hardcode" so it's more easily "reproducible" instead of having to define it in the environment.

I still think having the timeout as an env var is valuable to the user. Although we can set a default timeout, it should be overridable since the environment that the user is running the browser in could require a large timeout than what we provide.

What I don't like of the current implementation is that the browser timeout is set for both, "browser process allocation" TO and also default navigation TO. Which does not seem to make much sense 🤷‍♂️

Agreed.

Slowmo

although I don't see this as an ENV var because I see it as an option that can have a direct impact in the test execution.

Yeah, i agree with this. In that case we have two options:

  1. Refactor to get it working in browserContext;
  2. Unexport the option until we're sure we know what we're doing.

Keep this option as supported at the browser level.

In terms of slowmo, what do you mean by this? How would a user interact with this option? I was under the impression that we wouldn't provide any option in the script, no?

@ankur22
Copy link
Collaborator

ankur22 commented Apr 20, 2023

I think this is a good example as to why setting a timeout at the browser level is confusing for users, since at the moment users can set it there and it will affect all other timeouts, this just looks confusing to me 🙂

@ka3de
Copy link
Collaborator Author

ka3de commented Apr 21, 2023

Timeout

I still think having the timeout as an env var is valuable to the user. Although we can set a default timeout, it should be overridable since the environment that the user is running the browser in could require a large timeout than what we provide.

In this case, are you referring to the TO for "browser initialization" etc? Or to the one that applies to navigation?

slowMo

Yeah, i agree with this. In that case we have two options:

  1. Refactor to get it working in browserContext;
  2. Unexport the option until we're sure we know what we're doing.

I guess my question is. Do we need to support "individual" slowMo values per browserContext? Or can we just define one global value for the whole test run. This is what I mentioned that, considering the refactor effort, I don't think it's worth it right now with an "unknown" benefit.
I can see option 2, but I'm afraid we will be taking away functionality that is currently available for users.

In terms of slowmo, what do you mean by this? How would a user interact with this option? I was under the impression that we wouldn't provide any option in the script, no?

That's something that I would like to rediscuss based on this evaluation. Personally I think it can make sense to keep both of this options at the scenario browser params, as default values for the whole test. Then, for example in the case of timeout (applied to navigation only), it can be overwritten at browserContext or page level if desired. Considering that these options apply only to our implementation, it's safe to have different values per scenario, therefore I don't see any drawback that can come from k6 core team, but we should agree on this also.

In any case, I'm open to other options, I just think this would be the best option to:

  • Be able to provide defaults for the whole test, that can be overwritten at other levels if required (at least for timeout).
  • Do not remove currently available functionalities/options.

@ankur22
Copy link
Collaborator

ankur22 commented Apr 21, 2023

is set as part of a Go context

Aha! I think i've been misunderstanding how these properties are implemented. They're not passed to the actual chromium instance, but they're implemented in the xk6-browser go code in the browser type.

This means that multiple scenarios (can be thought of as multiple vus) have their own instance of browser but can connect to the same instance of chromium. These browser instances can independently work with the two properties (which are under evaluation in this issue) without affecting each other. So there can safely be a many-to-one relation between browser and a single instance of chromium. This changes my stance completely. Now I almost agree with all your conclusions @ka3de 🙂

The one that I'm not completely convinced on still is for timeout where you say:

Do not apply this timeout to any event that is not a navigation.

I still think that the this should only affect the timeout when making the connection against Chromium, and not the navigation. So at the moment I still think that there's a valid reason to move the browser level timeout out of the script and convert it into an env var, which will allow users to:

  1. fine tune the browser timeout so that it suits their local environment needs.
  2. work with the other readily available timeouts in browserContext and page to change the navigation timeouts.
    This would be a breaking change for users who are relying on browser level timeout to alter the navigation timeout.

It's also worth noting that Playwright uses the browser level timeout for connect and launch when launching and connecting to chromium, and doesn't get inherited by anything else (e.g. the navigation timeout).

@ka3de
Copy link
Collaborator Author

ka3de commented Apr 21, 2023

Yep, I think you have convinced me with this last comment about the usage of timeout:

  • Support the browser level timeout, which applies to connect and launch processes as an ENV var (as this is clearly dependent on the environment).
  • Support navigation timeout definition at the browserContext and page levels (as is already done). And maybe a nice to have would be to support the definition in the browserContext constructor.

In regards of slowMo, I guess we have to decide if we support it at the browser level or we don't expose it by now until we take a more clear decision.

@inancgumus
Copy link
Member

Support the browser level timeout, which applies to connect and launch processes as an ENV var (as this is clearly dependent on the environment).
Support navigation timeout definition at the browserContext and page levels (as is already done). And maybe a nice to have would be to support the definition in the browserContext constructor.

@ka3de, great analysis, and nice work, both this issue and the other one (#856). I want to add my two cents here about my observations. In my tests, while connecting to a single browser from multiple tests, I find out that supporting timeout at the browser level and navigation is critical. It's because the browser process may get overloaded at some point, and increasing the timeout allowed tests to continue and finish, although a bit later.

@ka3de
Copy link
Collaborator Author

ka3de commented Apr 24, 2023

Thanks for the input @inancgumus !
Yes, I think we agreed that it makes sense to expose a timeout for browser launch/connect actions, and to keep this as an ENV var, considering that it's mainly related with the test run environment.

Considering that, the points that I think still need agreement are:

  • What should be the naming for the "browser process timeout" option? The one that, for what I understand, we agreed on moving to ENV var.
    Should we keep it as timeout such as K6_BROWSER_TIMEOUT? Should we rename it to something like K6_BROWSER_PROCESS_TIMEOUT?
  • Should we support a global timeout option as the default navigation TO param? Or should we just provide a default one and allow users to change it at the browserContext and page levels (which we already support)?
    If we support it, should this one be defined at the script level?
    My opinion: Provide a sensible default one, and allow setting it at the browserContext and page levels.
  • Should we support slowMo option with its current behavior (defined at the script level)? Or should we not expose it by now until we take a long term decision on its implementation?
    My opinion: I have mixed feelings here. In a way I would like to keep supporting this as it is, to not remove currently available functionality. On the other hand, we might want to review the current implementation before continue to support it, as we have seen it might not be the best approach (e.g.: Difficulties to overwrite default slowMo at the browserContext level).

WDYT @ankur22 , @inancgumus ?

@ankur22
Copy link
Collaborator

ankur22 commented Apr 24, 2023

What should be the naming for the "browser process timeout" option?

How about K6_BROWSER_CONNECT_TIMEOUT? I think it'll only be used for connect and launch. Or K6_BROWSER_PROCESS_CONNECT_TIMEOUT

Provide a sensible default one, and allow setting it at the browserContext and page levels.

Agreed.

Should we support slowMo option with its current behavior...

This one i'm not sure of either. I'd prefer to either remove it from browser and start looking at how we can move it to browserContext; OR leave it in browser; i have a slight preference to removing it and refactoring it. If we left it in then we might need to support it in the long run. If we removed it now then people might not be too happy in the short term. Since we're allowing the behaviour of the tests to be configurable in the test function itself (via browserContext), slomo feels like something that a user would want to also configure in the browserContext and also be able to enable and disable during the test.

@inancgumus
Copy link
Member

inancgumus commented Apr 25, 2023

K6_BROWSER_CONNECT_TIMEOUT can clash with launch for open-source users (the env. variable name). And the launch method is what most will use since we redirect launch to connect. Getting inspired by Ankur's idea and refurbishing it slightly makes more sense to me: K6_BROWSER_LAUNCH_TIMEOUT.

However, if we will remove the browser from the users' access, it makes sense to me to have simply, K6_BROWSER_TIMEOUT. This will be perceived as a global timeout. If we're not after a global timeout, then it makes sense to make it K6_BROWSER_LAUNCH_TIMEOUT because I don't think users should deal with the internals (connect vs. launch). But, I'm OK with Ankur's suggestion too.

Provide a sensible default one, and allow setting it at the browserContext and page levels.

👍

And I would keep slowMo as it is for the issues you guys mentioned.

@ka3de
Copy link
Collaborator Author

ka3de commented Apr 26, 2023

Conclusion:

Based on all comments I believe we have reached a final consensus:

timeout

  • The timeout option, that currently serves for two purposes (browser initialization and default navigation), will no longer apply for the default navigation. Instead a sensible default will be provided for this purpose, and will be able to be overwritten at the browserContext and page levels, as it's currently supported.
  • The "new" timeout option, that will only apply for browser initialization processes, will be defined as an ENV var. The reason for this follows the same criteria specified in Evaluate Browser params #856 , for which every option that is related with "environment" conditions should be set as an environment variable.

slowMo

@ka3de ka3de closed this as completed Apr 28, 2023
ka3de added a commit that referenced this issue May 9, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 9, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 9, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 9, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 9, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 10, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 10, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 10, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 15, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
ka3de added a commit that referenced this issue May 18, 2023
Based on #857, a decision
was made to not expose slowMo browser option by now, and try to move it
to the browser context level instead, which will provide more
flexibility.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
poc Proof of concept team/k6browser To distinguish the issue on project boards.
Projects
None yet
Development

No branches or pull requests

3 participants