-
Notifications
You must be signed in to change notification settings - Fork 10
Run Chrome tests in travis #14
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
Conversation
Or maybe something like feature gates for driver specific tests, https://www.reddit.com/r/rust/comments/3i1nki/how_to_skip_expensive_tests_with_cargo_test/ but it does not seem ideal either. |
3987022
to
dba3984
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.
A few small changes to capabilities API, but this is looking good.
Adding a NewSessionCmd to Driver.session() is a (worthwhile) breaking change, so this release will need to be published as v0.2.0.
src/messages.rs
Outdated
} | ||
|
||
impl NewSessionCmd { | ||
pub fn new() -> Self { | ||
let mut capabilities: Capabilities = Default::default(); | ||
capabilities.alwaysMatch.insert("goog:chromeOptions".to_string(), json!({"w3c": 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.
Can we push goog:chromeOptions
down into a manual implementation of Default
on struct Capabilities
? That seems a little less surprising to me and allows the user to create a session with complete control over capabilities if required.
src/messages.rs
Outdated
#[derive(Serialize)] | ||
pub struct NewSessionCmd { | ||
capabilities: JsonValue, | ||
capabilities: Capabilities, | ||
} | ||
|
||
impl NewSessionCmd { | ||
pub fn new() -> Self { |
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.
Take a Capabilities value as an argument here, I think.
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 the concern is breaking changes then we can also remove new() and add it at a later time, and just leave default.
src/messages.rs
Outdated
} | ||
} | ||
|
||
// TODO firefox specifc prefs | ||
// [moz:firefoxOptions][prefs][name] = value; | ||
|
||
pub fn capability(&mut self, name: &str, value: Option<JsonValue>) { |
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.
Can we move this setter to struct Capabilities
and rename it to always
, always_match
or similar? The capabilities spec describes alwaysMatch
and firstMatch
here, which we may add later.
I really like passing the value as an Option<_> to allow insert or remove with one method. 👍
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.
Other possibilities here are
- a method to merge capabilities
- a build pattern of some kind, that either returns Self or &mut Self
@@ -84,8 +85,8 @@ pub trait Driver { | |||
/// The url used to connect to this driver | |||
fn url(&self) -> &str; | |||
/// Start a session for this driver | |||
fn session(self) -> Result<DriverSession, Error> where Self : Sized + 'static { | |||
DriverSession::create_session(Box::new(self)) | |||
fn session(self, params: &NewSessionCmd) -> Result<DriverSession, Error> where Self : Sized + 'static { |
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.
This is a (worthwhile) breaking change, so this release will need to be published as v0.2.0.
Most tests work, but there is definitely something going on with the property method.
Apparently this is not implemented |
Travis reports some build failures: https://travis-ci.org/fluffysquirrels/webdriver_client_rust/builds/335577419 @illicitonion proposed invoking the tests with |
Which bit requires rust nightly? Doesn't this work on stable? |
It runs on stable. But my previous versions depended on concat_idents. |
249ffe6
to
fd87258
Compare
Ah! Thanks for clarifying. Perhaps we could have: mod firefox {
browser_tests!(setup_firefox)
}
mod chrome {
browser_tests!(setup_chrome)
}
fn setup_firefox() {}
fn setup_chrome() {} |
That would imply moving the test code inside of the macro definition i.e. macro_rules! driver_tests {
( $modname:ident, $driver_fn:expr ) => {
mod $modname {
#[test]
fn navigation() {
let (server, sess) = setup();
let page1 = server.url("/page1.html");
sess.go(&page1).expect("Error going to page1");
assert_eq!(&sess.get_current_url().expect("Error getting url [1]"), &page1, "Wrong URL [1]");
(...all the other tests ...)
}
}
}
driver_tests!(gecko, setup_gecko)
driver_tests!(chrome, setup_gecko) It should work, might just be a bit hard to edit tests inside the macro wrapper. Maybe move driver generic tests to different file and do an include!. |
2b4549f
to
fe47626
Compare
Is fe47626 similar to what you had in mind for the macros? |
I have a test for this behaviour, but I'll wait for fluffysquirrels#14 to merge before adding it.
Macros: thanks for working out the definition we need, it's starting to look really good! I thought about this a lot today and wrote up a draft for you below, with comments to explain my reasoning. // I think a trait is a little easier to grok than reading the macro documentation
// to understand what expressions are required. Also easier to extend.
//
// The trait can also be re-used by tests for a single browser (i.e. not in the browser_tests macro)
trait TestBrowser : Debug {
fn session() -> DriverSession { [...] }
fn driver() -> Box<Driver> { [...] }
}
#[derive(Default, Debug)]
struct FirefoxTest;
impl TestBrowser for FirefoxTest { [...] }
#[derive(Default, Debug)]
struct ChromeTest;
impl TestBrowser for ChromeTest { [...] }
/// Tests defined in this macro are run once per browser. See the macro invocations below.
macro_rules! browser_tests {
($mod_name:ident, $browser_type:ty) => {
mod $mod_name {
// Single browser-specific function to grok.
fn browser() -> Box<TestBrowser> { Box::new($browser_type::default()) }
// The tests are tightly coupled to the per-browser functions / types, so I think I prefer
// them right next to each other in one file instead of in a separate included file.
//
// "might just be a bit hard to edit tests inside the macro wrapper":
// I think it's clear enough if you've seen it before.
// FWIW, this sort of pattern is quite common in rust std
// e.g. https://github.com/rust-lang/rust/blob/56733bc9f8302409a2b6110f422512923c878154/src/libcore/tests/num/mod.rs#L145-L197
#[test]
fn first() -> { [...] }
}
};
}
browser_tests!(firefox, FirefoxTest);
browser_tests!(chrome, ChromeTest); |
fe47626
to
b821dd2
Compare
Confirmed, removing firefox from packages and keeping the addon line installs firefox 58. Funny part is we could probably xvfb and run headless firefox (have not tested, but should work since ff 57) https://github.com/mozilla/geckodriver#mozfirefoxoptions. |
For later reference on can emulate the missing property method in chrome using javascript and an element ref let link = sess.find_element("#link_to_page_2", LocationStrategy::Css)
.expect("Error finding element");
let href = sess.execute(ExecuteCmd {
script: "return arguments[0].href;".to_owned(),
// the browser will convert the json reference to a js reference
args: vec![link.reference().unwrap()],
}).expect("Error executing script");
exec_json.as_str().unwrap(); |
efa2391
to
40132e3
Compare
I'd love to see headless Firefox. The private project I have using this crate is actually going to be on a headless server. The xvfb stuff is messier, especially the |
EDIT: Ignore this. My alternative would have allowed a user to try and set I'm working on #18 ("Use Firefox -headless"), and I want to add command line args. I think this: session_params.extend_always_match(
json!({
"moz:firefoxOptions": {
"args": ["-headless"]
};
})); is a little neater for the consumer than: session_params.extend_always_match(
"moz:firefoxOptions", json!({
"args": ["-headless"]
})); It's fewer characters to type (especially when setting multiple capabilities), and the implementation could be simpler (just use the JSON merge algorithm, as you linked on this serde_json thread). Thoughts? |
I've built on this work in branch ff-headless and issue #18. The latest Travis build on the branch just passed, and I'm thinking of merging it soonish. Happy to hear your comments. |
Remove the new constructor in NewSessionCmd since it is not clear how its api will evolve. Instead provide an implementation for the Default trait.
Capture the capabilities object when creating a session and provide a method to get the browserName property. When using a preexisting session the capabilities object is not available and the browser_name() method returns None.
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 made changes very similar to these in #18. Sorry for the duplicate work.
4334a7f
to
a6d115c
Compare
Primarily the implication of this change is that the argument to that function MUST be an object otherwisee it would clobber the I've pushed an example pub fn extend_always_match2(&mut self, new_attrs: JsonValue) -> &mut Self {
if let JsonValue::Object(map) = new_attrs {
for (k, v) in map.into_iter() {
Self::merge_obj(self.capabilities.alwaysMatch
.entry(k)
.or_insert(JsonValue::Null), v);
}
} else {
// ERROR: input is not an object
}
self
} In this one I just ignore the error though. Otherwise it seems to work. |
Continuing on my previous comment, if extend_always_match takes a value instead of (name, value) pair then should the other methods follow the same convention? Its possible to Same issue as before with error handling. pub fn always_match(&mut self, capability: JsonValue) -> &mut Self {
if let JsonValue::Object(b) = capability {
for (k, v) in b.into_iter() {
self.capabilities.alwaysMatch.insert(k, v);
}
}
self
} |
Mmm, i cant settle on a merge strategy and naming. I'll remove the extend related commits and leave those for another PR. |
2127793
to
984556b
Compare
Yes, I eventually realised your way (key and value, not object) was much better for the same reason. I edited my comment but I'm not sure if Github notifies you about edits. Sorry for the confusion. README changes look good. I'll probably merge all this when I sit down at my computer. This looks great! It'll be very useful to have Chrome tests running in the CI build. I feel it would be useful to have examples and/or tests that don't run in headless mode. I find this useful for exploratory testing. I may add a separate issue for those. |
Hey, this turned out really well, thanks for all your work! I've merged it with my Firefox headless stuff and will push to master then publish to crates.io when I get the build on my branch to pass. |
One small point that I updated in my branch but forgot to call out in your review. I prefer this: let cmd = NewSessionCmd::default(); to this: let cmd: NewSessionCmd = Default::default(); Less typing, and I find it a little more straightforward, coming from an OO background. |
984556b
to
bfcdfaa
Compare
Add travis settings to run tests with the stable version of chrome, a script to download chromedriver is available in bin/download_chromedriver. To avoid problems with the travis container the tests run in headless mode without the chrome sandbox: travis-ci/travis-ci#7150 (comment) To make the tests generic over different browsers they were wrapped in a macro whose purpose is to generate test modules for different drivers (currently chrome and firefox). At runtime tests can determine the driver they are using with the function test_browser(). It returns an enum that identifies the type of driver in use and provides constructors for the driver or session. Runtime checks for the browser being executed either use this enum or the browser name. Some loose ends are left unaddressed - we can skip a test at runtime but there is no way to indicate this in the test output, one just returns - part of the `execute` test is being skipped for chrome, the error strings and types are very different from firefox - Tests using the property method skipped in chrome because this is not implemented https://bugs.chromium.org/p/chromedriver/issues/detail?id=1936
Awesome, I've pushed the NewSessionCmd change anyway. should I close this one? |
Merged. |
Split the travis tests from #9.
Right now this is the same as #9 with a two commits on top to actually test with chrome, just so we can verify #9 works as intended.
TODO: