Skip to content

[java] implement Sauce Configs for Chrome #243

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

Closed
wants to merge 2 commits into from
Closed

Conversation

titusfortner
Copy link
Collaborator

Ok, I was definitely making this too hard... We were trying to decide between option 4 & option 6 from this list, and this PR I think takes the best from each of them and does it in a rather light weight solution.

The point of these changes is to provide a way for a user to only have access to the methods/parameters that make sense for the type of test being run. (e.g. Chrome tests can't set IE Driver Version, etc)
This isn't as much of an issue with just browser implementations, but will be essential for dealing with Mobile & Visual, etc.

So, SauceOptions is going to store the state of *everything, but access to the everything is going to be controlled by the config classes.

Config classes will only have setters, no getters and is only going to store state within a SauceOptions instance (which means we can't use lombok setters since we aren't setting state on fields directly). We're also using generics to return an instance of the subclass rather than having it return the superclass. Then the user calls build() to generate the SauceOptions instance, so they don't ever need to know which class of configs they are using under the hood.

I've marked the configs class constructor as "Deprecated" to encourage users to use it from the static method in SauceOptions, but if someone *does use the config class directly, or doesn't call build() on it, the code still lets them pass it into a SauceSession constructor and the constructor will just call the build method on it.

The capabilities are defined based on: https://docs.google.com/spreadsheets/d/1u_28C3lqLlmZElBDb6KuG6qSYuCEK32R40RyjOvWnBE/edit

Hierarchy:
SauceConfigs
----> SauceConfigsBrowser
--------> SauceConfigsChrome

There isn't any way that we're going to be able to avoid all duplication going forward (especially once we add in mobile things), but this approach should do a fair job of removing most of the duplication.

Next steps:

  1. Implement Edge, Firefox, IE & Safari based on which of these are allowed per browser:
seleniumVersion
chromedriverVersion
iedriverVersion
geckodriverVersion
avoidProxy
extendedDebugging
  1. Override methods in subclass to ensure incompatible values aren't being set (e.g. setPlatformName(SaucePlatform:MAC_SIERRA) on a SauceConfigsChrome instance).
  2. Add Visual support in SauceOptions and SauceBrowserConfigs
  3. Add Mobile support in SauceOptions and new subclasses of SauceConfigs

Considerations:

  1. We could deprecate the other constructors of SauceOptions to ensure everyone goes through the Configs classes
  2. We could put the static methods in a SauceOptionsFactory class instead of SauceOptions if that makes more sense

@titusfortner titusfortner added the java Java Bindings label Nov 30, 2020
@titusfortner
Copy link
Collaborator Author

Ok, actually we might want to update this implementation to distinguish w3c from sauce settings like #237 , which will be useful when we add visual and mobile. I think we can literally merge the implementation of 237 and then add this on top

sauceOptions = new SauceOptions(chromeOptions);
}

public SauceConfigsChrome setCapturePerformance(Boolean bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did document a bug related to this #241

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

latest commit throws an exception if setCapturePerformance() is called before setName() we can remove this if/when we implement the automatic naming idea for the test runners.

}


public T setVideoUploadOnPass(Boolean bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is the time to do it, but I've been wondering if it makes sense to be passing Booleans in our setters. All of these capabilities are either enabled or disabled by default. So our methods only really do one of the options. . Does it make sense for a user to do setVideoUploadOnPass(true) when the only reasonable option is disableVideoUploadOnPass()?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I like this! Passing in Boolean parameters has always seemed clunky to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, updated the code to use disable when the default is true and set when the default is false; neither takes an argument

Copy link
Contributor

@nadvolod nadvolod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I think that this makes sense. I've left a few minor comments unrelated to the main API.

import java.time.Duration;
import java.util.Map;

public class SauceConfigsBrowser<T extends SauceConfigsBrowser<T>> extends SauceConfigs<T> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you all think about calling this BrowserConfiguration and child classes ChromeConfiguration...?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I named them before I put them in a separate configs package, so I wanted them all grouped together...
Rethinking from big picture, this is probably how to break it up to best fit the capabilities spreadsheet:
Either Configs or Configurations is fine for me. It probably isn't the biggest issue either way since we theoretically don't want users to even know about the names of the classes. :)

Configs
----> VDCConfigs
--------> DesktopConfigs
------------> ChromeConfigs, EdgeConfigs, SafariConfigs, IEConfigs, FirefoxConfigs
--------> EmulatorConfigs, SimulatorConfigs
----> RDCConfigs
--------> AndroidConfigs, iOSConfigs

@titusfortner titusfortner requested a review from diemol December 1, 2020 17:01
Copy link
Member

@diemol diemol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few comments, sorry for not being more helpfult but I probably miss a lot of context.

* @deprecated Not for public use
* Use: {@link SauceOptions#chrome()}} or {@link SauceOptions#chrome(ChromeOptions)}}
*/
@Deprecated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you leave only with package visibility? Instead of deprecating it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diemol I don't understand what the impact of that is. The idea of deprecating it is so that people can still use it but know they need to update their behavior before the next major release. What does changing the package visibility do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I moved SauceOptions into a new package, which I think is *bad. I think it is ok if we release this as a 2.0?

@@ -129,6 +130,22 @@
knownCITools.put("TeamCity", "TEAMCITY_PROJECT_NAME");
}

/**
* @return SauceConfigsChrome,
* note: constructor implementation is deprecated for public use, but usage here is intentional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can leave the constructor protected or package visibility to avoid the need of deprecating it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, I see what you are saying. We're going to deprecate it for the next release, then change it to protected/private for next major release. Wanted to give a warning about it first.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, this is a new method. got it. (trying to remember what I was doing 2 months ago is hard)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and... it's because I have it in a separate package (com.saucelabs.saucebindings.configs vs com.saucelabs.saucebindings. Any advice on a better way to organize this? I feel like everything should probably be in an options package, but I also think it is considered bad to change the package from one version to another? :) Would it make sense to have everything in the main package?

@@ -20,6 +21,10 @@ public SauceSession() {
this(new SauceOptions());
}

public SauceSession(SauceConfigs configs) {
this(configs.build());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is not really following the builder patter, where one of the ideas is that calling the .build() returns an instanciated object.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of a hack in case people "do it wrong." We want them to build it themselves and pass it in, but we were concerned that they would get a weird error message if they didn't, and it didn't seem that hard to just add a constructor to make it just work.

return (T) this;
}

public SauceOptions build() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels you'd like to follow the same structure of browser options in Selenium (where EdgeOptions extends ChromiumOptions, etc...), so maybe you can get rid of this build method and only pass instanciated objects?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal is to only show users allowed capabilities. So SauceOptions allows everything, and the config class is restricted to only applicable values, but rather than a bunch of other constructors, we just convert the configs class into a sauce options and have that get passed into the session.

@diemol
Copy link
Member

diemol commented Jan 15, 2021

Also, the Google Docs link is only accessible to Sauce employees, which is not OSS friendly, please consider making it either read only for everyone or add that information to the repo's Wiki.

@titusfortner
Copy link
Collaborator Author

Well, the second commit here is bigger than the first one, because I rearranged the packages to make more sense. Except it isn't backward compatible, so maybe I'll want to put everything back, or maybe we push forward and do a 2.0 release next and hope we don't upset too many people already. :-/

  • I added a ScreenResolution enum (the names are correct but not super useful, but better than nothing?)
  • I renamed the config classes to make more sense (BaseConfigurations, VDCConfigurations & ChromeConfigurations). Superclasses are abstract so they can't be instantiated directly
  • I converted Integer parameters to Duration (same thing Selenium is moving to)
  • Methods taking boolean values changed to allow overriding the default without needing arguments

This was referenced Jan 23, 2021
@titusfortner
Copy link
Collaborator Author

This is subsumed by #246

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
java Java Bindings
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants