From 45d8121ddd1763c85a323b19239ef054b1cb5da3 Mon Sep 17 00:00:00 2001 From: staats-google Date: Tue, 27 Jun 2017 10:51:00 +0200 Subject: [PATCH] Adds logic to automatically infer specification type during WebDriver creation. (#172) * Adds automated spec inference ability during WebDriver creation. * Adds ability to define spec during WebDriver creation. Sets defaults to W3C for FireFox and JSONWire for Chrome. * Adds test for spec inference. Updates logic for reusing exisiting sessions for W3C spec. * Add error messages. * Updates spec inference test with different exception types. --- lib/src/sync/json_wire_spec/exception.dart | 2 +- .../spec_inference_response_processor.dart | 51 +++++++++++++++ lib/src/sync/w3c_spec/exception.dart | 2 +- lib/sync_core.dart | 14 ++-- test/sync/spec_inference_test.dart | 64 +++++++++++++++++++ test/sync/sync_io_config.dart | 11 ++-- 6 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 lib/src/sync/spec_inference_response_processor.dart create mode 100644 test/sync/spec_inference_test.dart diff --git a/lib/src/sync/json_wire_spec/exception.dart b/lib/src/sync/json_wire_spec/exception.dart index 0c0b0354..b1082f6e 100644 --- a/lib/src/sync/json_wire_spec/exception.dart +++ b/lib/src/sync/json_wire_spec/exception.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -library webdriver.exception; +library webdriver.json_exception; import '../exception.dart'; diff --git a/lib/src/sync/spec_inference_response_processor.dart b/lib/src/sync/spec_inference_response_processor.dart new file mode 100644 index 00000000..b5cf656e --- /dev/null +++ b/lib/src/sync/spec_inference_response_processor.dart @@ -0,0 +1,51 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert' show JSON; + +import 'package:sync_http/sync_http.dart'; +import 'package:webdriver/sync_core.dart'; + +/// Inferred spec and sessionId from a session creation request. +class InferredResponse { + final String sessionId; + + final WebDriverSpec spec; + + InferredResponse(this.sessionId, this.spec); +} + +/// Infers the spec during session creation. +dynamic inferSessionResponseSpec(SyncHttpClientResponse response, bool _) { + Map responseBody; + try { + responseBody = JSON.decode(response.body); + } catch (e) {} + + // TODO(staats): create more description error messages. + if (response.statusCode < 200 || response.statusCode > 299) { + throw 'Response code: ${response.statusCode}'; + } + + // JSON responses have multiple keys. + if (responseBody.keys.length > 1) { + return new InferredResponse( + responseBody['sessionId'], WebDriverSpec.JsonWire); + // W3C responses have only one key, value. + } else if (responseBody.keys.length == 1) { + return new InferredResponse( + responseBody['value']['sessionId'], WebDriverSpec.W3c); + } + throw 'Could not infer spec type; number of keys: ${responseBody.keys}'; +} diff --git a/lib/src/sync/w3c_spec/exception.dart b/lib/src/sync/w3c_spec/exception.dart index 8fee62c7..6bf474e7 100644 --- a/lib/src/sync/w3c_spec/exception.dart +++ b/lib/src/sync/w3c_spec/exception.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -library webdriver.exception; +library webdriver.w3c_exception; import '../exception.dart'; diff --git a/lib/sync_core.dart b/lib/sync_core.dart index 45dd7025..4024c45b 100644 --- a/lib/sync_core.dart +++ b/lib/sync_core.dart @@ -20,6 +20,7 @@ import 'package:webdriver/src/sync/capabilities.dart' show Capabilities; import 'package:webdriver/src/sync/web_driver.dart' show WebDriver; import 'package:webdriver/src/sync/command_processor.dart'; +import 'package:webdriver/src/sync/spec_inference_response_processor.dart'; import 'package:webdriver/src/sync/json_wire_spec/response_processor.dart'; import 'package:webdriver/src/sync/json_wire_spec/web_driver.dart' as jwire; import 'package:webdriver/src/sync/w3c_spec/response_processor.dart'; @@ -79,7 +80,12 @@ WebDriver createDriver( return new w3c.W3cWebDriver(processor, uri, response['sessionId'], new UnmodifiableMapView(response['value'] as Map)); case WebDriverSpec.Auto: - throw 'Not yet supported!'; + final response = + new SyncHttpCommandProcessor(processor: inferSessionResponseSpec) + .post(uri.resolve('session'), {'desiredCapabilities': desired}, + value: true) as InferredResponse; + return fromExistingSession(response.sessionId, + uri: uri, spec: response.spec); default: throw 'Not yet supported!'; // Impossible. } @@ -102,12 +108,10 @@ WebDriver fromExistingSession(String sessionId, case WebDriverSpec.W3c: final processor = new SyncHttpCommandProcessor(processor: processW3cResponse); - final response = processor.get(uri.resolve('session/$sessionId')) - as Map; return new w3c.W3cWebDriver( - processor, uri, sessionId, new UnmodifiableMapView(response)); + processor, uri, sessionId, new Map()); case WebDriverSpec.Auto: - throw 'Not yet supported!'; + throw 'Not supported!'; default: throw 'Not yet supported!'; // Impossible. } diff --git a/test/sync/spec_inference_test.dart b/test/sync/spec_inference_test.dart new file mode 100644 index 00000000..4f7c6088 --- /dev/null +++ b/test/sync/spec_inference_test.dart @@ -0,0 +1,64 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@TestOn("vm") +library webdriver.spec_inference_test; + +import 'package:test/test.dart'; +import 'package:webdriver/src/sync/json_wire_spec/exception.dart' as json; +import 'package:webdriver/src/sync/w3c_spec/exception.dart' as w3c; +import 'package:webdriver/sync_core.dart'; + +import 'sync_io_config.dart' as config; + +void main() { + group('Spec inference', () { + WebDriver driver; + + setUp(() {}); + + tearDown(() { + if (driver != null) { + driver.quit(); + } + driver = null; + }); + + test('chrome works', () { + driver = config.createChromeTestDriver(spec: WebDriverSpec.Auto); + driver.get(config.testPagePath); + final button = driver.findElement(const By.tagName('button')); + try { + button.findElement(const By.tagName('tr')); + throw 'Expected NoSuchElementException'; + } catch(e) { + expect(e, new isInstanceOf()); + expect(e.toString(), contains('Unable to locate element')); + } + }); + + test('firefox work', () { + driver = config.createFirefoxTestDriver(spec: WebDriverSpec.Auto); + driver.get(config.testPagePath); + final button = driver.findElement(const By.tagName('button')); + try { + button.findElement(const By.tagName('tr')); + throw 'Expected W3cWebDriverException'; + } catch(e) { + expect(e, new isInstanceOf()); + expect(e.toString(), contains('Unable to locate element')); + } + }); + }, timeout: new Timeout(new Duration(minutes: 2))); +} diff --git a/test/sync/sync_io_config.dart b/test/sync/sync_io_config.dart index 8e2e9e9e..85a37978 100644 --- a/test/sync/sync_io_config.dart +++ b/test/sync/sync_io_config.dart @@ -29,18 +29,20 @@ final Uri _defaultChromeUri = Uri.parse('http://127.0.0.1:4444/wd/hub/'); final Uri _defaultFirefoxUri = Uri.parse('http://127.0.0.1:4445/'); WebDriver createFirefoxTestDriver( - {Map additionalCapabilities}) { + {Map additionalCapabilities, + WebDriverSpec spec: WebDriverSpec.W3c}) { final capabilities = Capabilities.firefox; if (additionalCapabilities != null) { capabilities.addAll(additionalCapabilities); } return createDriver( - uri: _defaultFirefoxUri, desired: capabilities, spec: WebDriverSpec.W3c); + uri: _defaultFirefoxUri, desired: capabilities, spec: spec); } WebDriver createChromeTestDriver( - {Map additionalCapabilities}) { + {Map additionalCapabilities, + WebDriverSpec spec: WebDriverSpec.JsonWire}) { var capabilities = Capabilities.chrome; Map env = Platform.environment; @@ -62,5 +64,6 @@ WebDriver createChromeTestDriver( capabilities.addAll(additionalCapabilities); } - return createDriver(uri: _defaultChromeUri, desired: additionalCapabilities); + return createDriver( + uri: _defaultChromeUri, desired: additionalCapabilities, spec: spec); }