From 7118d20afddba87c9024efdc277fe9ed549c0da6 Mon Sep 17 00:00:00 2001 From: Marc Fisher II Date: Wed, 4 Feb 2015 10:14:08 -0800 Subject: [PATCH 1/2] Add option to disable shadowDom support. --- dart/lib/html.dart | 31 ++-- dart/lib/src/core.dart | 8 +- dart/lib/src/interfaces.dart | 1 + dart/lib/webdriver.dart | 9 +- dart/pubspec.yaml | 5 +- dart/test/no_shadow_dom_html_test.dart | 141 ++++++++++++++++++ dart/test/no_shadow_dom_html_test.html | 16 ++ dart/test/pageloader_test.dart | 25 +++- dart/test/webdriver_no_shadow_dom_test.dart | 82 ++++++++++ .../webdriver_no_shadow_dom_test_page.html | 74 +++++++++ dart/test/webdriver_test.dart | 2 +- dart/test/webdriver_test_page.html | 86 +++++++++++ 12 files changed, 454 insertions(+), 26 deletions(-) create mode 100644 dart/test/no_shadow_dom_html_test.dart create mode 100644 dart/test/no_shadow_dom_html_test.html create mode 100644 dart/test/webdriver_no_shadow_dom_test.dart create mode 100644 dart/test/webdriver_no_shadow_dom_test_page.html create mode 100644 dart/test/webdriver_test_page.html diff --git a/dart/lib/html.dart b/dart/lib/html.dart index cd222bdb..0082a70a 100644 --- a/dart/lib/html.dart +++ b/dart/lib/html.dart @@ -44,17 +44,19 @@ class HtmlPageLoader extends BasePageLoader { @override _HtmlMouse get mouse => _mouse; - factory HtmlPageLoader(Node globalContext, [SyncActionFn syncActionFn]) { + factory HtmlPageLoader(Node globalContext, + {SyncActionFn syncActionFn, bool useShadowDom: true}) { var clock; if (syncActionFn != null) { clock = new _SyncActionClock(syncActionFn); } - return new HtmlPageLoader._(globalContext, clock); + return new HtmlPageLoader._(globalContext, clock, useShadowDom); } - HtmlPageLoader._(Node globalContext, Clock clock) : super(clock) { + HtmlPageLoader._(Node globalContext, Clock clock, bool useShadowDom) + : super(clock: clock, useShadowDom: useShadowDom) { this._globalContext = new HtmlPageLoaderElement(globalContext, this); this._mouse = new _HtmlMouse(this); } @@ -107,9 +109,10 @@ class _HtmlMouse implements PageLoaderMouse { int get pageY => window.pageYOffset + clientY; int get _borderWidth => (window.outerWidth - window.innerWidth) ~/ 2; int get screenX => window.screenLeft + _borderWidth + clientX; - int get screenY => window.screenTop + window.outerHeight - - window.innerHeight - - _borderWidth + + int get screenY => window.screenTop + + window.outerHeight - + window.innerHeight - + _borderWidth + clientY; void dispatchEvent(String type, _ElementPageLoaderElement eventTarget, @@ -149,7 +152,12 @@ abstract class HtmlPageLoaderElement implements PageLoaderElement { } else if (node is Document) { return new _DocumentPageLoaderElement(node, loader); } else if (node is ShadowRoot) { - return new _ShadowRootPageLoaderElement(node, loader); + if (loader.useShadowDom) { + return new _ShadowRootPageLoaderElement(node, loader); + } else { + throw new PageLoaderException( + 'Cannot create element for ShadowRoot when useShadowDom is false'); + } } return null; } @@ -211,8 +219,9 @@ class _ElementPageLoaderElement extends HtmlPageLoaderElement { this.style = new _ElementStyle(_node); @override - PageLoaderElement get shadowRoot => - new HtmlPageLoaderElement(node.shadowRoot, loader); + PageLoaderElement get shadowRoot => loader.useShadowDom + ? new HtmlPageLoaderElement(node.shadowRoot, loader) + : this; @override String get name => node.tagName.toLowerCase(); // TODO(DrMarcII): implement this to recurse up the tree to see if displayed @@ -268,7 +277,9 @@ class _ElementPageLoaderElement extends HtmlPageLoaderElement { class _ShadowRootPageLoaderElement extends HtmlPageLoaderElement { final ShadowRoot node; - _ShadowRootPageLoaderElement(this.node, PageLoader loader) : super._(loader); + _ShadowRootPageLoaderElement(this.node, PageLoader loader) : super._(loader) { + assert(loader.useShadowDom); + } @override String get name => '__shadow_root__'; diff --git a/dart/lib/src/core.dart b/dart/lib/src/core.dart index bd6871ba..1c1ce9a4 100644 --- a/dart/lib/src/core.dart +++ b/dart/lib/src/core.dart @@ -32,8 +32,9 @@ const _DEFAULT_INTERVAL = const Duration(milliseconds: 100); /// fields in simple Dart objects. abstract class BasePageLoader implements PageLoader { final Clock clock; + final bool useShadowDom; - BasePageLoader([Clock clock]) + BasePageLoader({Clock clock, this.useShadowDom: true}) : this.clock = clock == null ? new FakeClock() : clock; /// Creates a new instance of [type] and binds annotated fields to @@ -50,7 +51,7 @@ abstract class BasePageLoader implements PageLoader { @override waitForValue(condition(), {Duration timeout: _DEFAULT_WAIT, - Duration interval: _DEFAULT_INTERVAL}) => + Duration interval: _DEFAULT_INTERVAL}) => waitFor(condition, isNotNull, timeout: timeout, interval: interval); @override @@ -351,7 +352,8 @@ class _FinderSingleFieldInfo extends _FinderFieldInfo { final bool _isOptional; _FinderSingleFieldInfo(Symbol fieldName, this._finder, this._filters, - this._instanceType, this._isOptional) : super(fieldName); + this._instanceType, this._isOptional) + : super(fieldName); @override calculateFieldValue(PageLoaderElement context, BasePageLoader loader) { diff --git a/dart/lib/src/interfaces.dart b/dart/lib/src/interfaces.dart index 6304c52b..8e9200ba 100644 --- a/dart/lib/src/interfaces.dart +++ b/dart/lib/src/interfaces.dart @@ -23,6 +23,7 @@ abstract class Lazy { } abstract class PageLoader { + bool get useShadowDom; PageLoaderElement get globalContext; Object getInstance(Type type, [dynamic context]); diff --git a/dart/lib/webdriver.dart b/dart/lib/webdriver.dart index e1528a3e..6f718ed6 100644 --- a/dart/lib/webdriver.dart +++ b/dart/lib/webdriver.dart @@ -28,13 +28,11 @@ import 'package:sync_webdriver/sync_webdriver.dart' as wd; class WebDriverPageLoader extends BasePageLoader { WebDriverPageLoaderElement _globalContext; - final bool useShadowRoot; @override final _WebDriverMouse mouse; - WebDriverPageLoader(wd.SearchContext globalContext, - {this.useShadowRoot: true}) - : super(const _IOClock()), + WebDriverPageLoader(wd.SearchContext globalContext, {useShadowDom: true}) + : super(clock: const _IOClock(), useShadowDom: useShadowDom), this.mouse = new _WebDriverMouse(globalContext.driver) { this._globalContext = new WebDriverPageLoaderElement(globalContext, this); } @@ -153,7 +151,7 @@ class _WebElementPageLoaderElement extends WebDriverPageLoaderElement { @override WebDriverPageLoaderElement get shadowRoot { - if (loader.useShadowRoot) { + if (loader.useShadowDom) { return new _ShadowRootPageLoaderElement(context, loader); } else { return this; @@ -231,6 +229,7 @@ class _ShadowRootPageLoaderElement extends WebDriverPageLoaderElement { _ShadowRootPageLoaderElement(this.context, WebDriverPageLoader loader) : super._(loader) { + assert(loader.useShadowDom); if (!_execute(' != null')) { throw new PageLoaderException('$context does not have a ShadowRoot'); } diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml index fdad37f6..57604a44 100644 --- a/dart/pubspec.yaml +++ b/dart/pubspec.yaml @@ -1,13 +1,14 @@ name: pageloader -version: 1.2.6 +version: 1.3.0 author: Marc Fisher II description: Supports the creation of page objects that can be shared between in-browser tests and WebDriver tests. environment: sdk: '>=1.9.0-dev.4.0 <2.0.0' dependencies: + matcher: '^0.11.4' sync_webdriver: path: ../../dart-sync-webdriver dev_dependencies: browser: '^0.10.0+2' - path: '^1.3.1' + path: '^1.3.2' unittest: '^0.11.5' diff --git a/dart/test/no_shadow_dom_html_test.dart b/dart/test/no_shadow_dom_html_test.dart new file mode 100644 index 00000000..c5c31eb0 --- /dev/null +++ b/dart/test/no_shadow_dom_html_test.dart @@ -0,0 +1,141 @@ +/* + * Copyright 2014 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. + */ +library pageloader.test.html; + +import 'page_objects.dart'; +import 'pageloader_test.dart' as plt; + +import 'package:pageloader/html.dart'; +import 'package:unittest/html_enhanced_config.dart'; +import 'package:unittest/unittest.dart'; + +import 'dart:html' as html; + +void main() { + useHtmlEnhancedConfiguration(); + + setUp(() { + var body = html.document.getElementsByTagName('body').first; + const bodyHtml = ''' + + + + + + + + + + +
r1c1r1c2
r2c1r2c2
+ +
area for mouse events
+ + + + + + test + + + +
+ outer div 1 + +
+
+ outer div 2 +
+ inner div 1 +
+
+ inner div 2 +
+
+ + + + + + '''; + + var div = body.querySelectorAll('div[id=testdocument]'); + if (div.length == 1) { + div = div[0]; + } else { + div = new html.DivElement(); + div.id = 'testdocument'; + body.append(div); + } + div.setInnerHtml(bodyHtml, validator: new NoOpNodeValidator()); + + var displayedDiv = html.document.getElementById('mouse'); + displayedDiv.onMouseDown.listen((evt) { + displayedDiv.text = displayedDiv.text + + " MouseDown: ${evt.client.x}, ${evt.client.y}; " + "${evt.screen.x}, ${evt.screen.y}"; + }); + displayedDiv.onMouseUp.listen((evt) { + displayedDiv.text = displayedDiv.text + + " MouseUp: ${evt.client.x}, ${evt.client.y}; " + "${evt.screen.x}, ${evt.screen.y}"; + }); + displayedDiv.onMouseMove.listen((evt) { + displayedDiv.text = displayedDiv.text + + " MouseMove: ${evt.client.x}, ${evt.client.y}; " + "${evt.screen.x}, ${evt.screen.y}"; + }); + + plt.loader = new HtmlPageLoader(div, useShadowDom: false); + }); + + group('html specific tests', () { + test('value on text', () { + var page = plt.loader.getInstance(PageForAttributesTests); + var handlerCalled = false; + var node = (page.text as HtmlPageLoaderElement).node as html.InputElement; + node.onInput.listen((event) { + handlerCalled = true; + }); + expect(page.text.attributes['value'], ''); + page.text.type('some text'); + expect(page.text.attributes['value'], 'some text'); + expect(handlerCalled, isTrue); + }); + + test('keypress events', () { + var data = 'my data'; + var list = []; + html.document.body.onKeyPress.listen((evt) => list.add(evt.charCode)); + plt.loader.globalContext.type(data); + expect(new String.fromCharCodes(list), equals(data)); + }); + }); + + plt.runTests(); +} + +class NoOpNodeValidator implements html.NodeValidator { + bool allowsAttribute( + html.Element element, String attributeName, String value) => true; + bool allowsElement(html.Element element) => true; +} diff --git a/dart/test/no_shadow_dom_html_test.html b/dart/test/no_shadow_dom_html_test.html new file mode 100644 index 00000000..f599ddb5 --- /dev/null +++ b/dart/test/no_shadow_dom_html_test.html @@ -0,0 +1,16 @@ + + + + + + + no_shadow_dom_html_test + + + + + + + + diff --git a/dart/test/pageloader_test.dart b/dart/test/pageloader_test.dart index 0189c99a..3b3d3d5d 100644 --- a/dart/test/pageloader_test.dart +++ b/dart/test/pageloader_test.dart @@ -230,7 +230,9 @@ void runTests() { expect(page.button1.button.visibleText, contains('some')); expect(page.button2.button.visibleText, contains('button 2')); expect(page.button2.button.visibleText, contains('some')); - expect(page.shouldBeEmpty, isEmpty); + if (loader.useShadowDom) { + expect(page.shouldBeEmpty, hasLength(0)); + } }); test('WithVisibleText in shadow dom', () { @@ -239,8 +241,6 @@ void runTests() { expect(page.button1.visibleText, contains('button 1')); expect(page.button1.visibleText, contains('some')); - expect(page.button1.innerText, contains('button 1')); - expect(page.button1.innerText, isNot(contains('some'))); }); test('chain', () { @@ -254,6 +254,11 @@ void runTests() { }); test('WithInnerText in shadow dom', () { + if (!loader.useShadowDom) { + // if shadow dom is disabled, then visibleText and innerText are + // identical + return; + } PageForShadowDomWithInnerTextTest page = loader.getInstance(PageForShadowDomWithInnerTextTest); @@ -273,9 +278,19 @@ void runTests() { expect(page.buttons[1].shadowRoot.visibleText, contains('some')); expect(page.buttons[2].shadowRoot.visibleText, contains('button 2')); expect(page.buttons[2].shadowRoot.visibleText, contains('some')); - expect(page.buttons[1].shadowRoot.innerText, isNot(contains('button 1'))); + if (loader.useShadowDom) { + expect( + page.buttons[1].shadowRoot.innerText, isNot(contains('button 1'))); + } else { + expect(page.buttons[1].shadowRoot.innerText, contains('button 1')); + } expect(page.buttons[1].shadowRoot.innerText, contains('some')); - expect(page.buttons[2].shadowRoot.innerText, isNot(contains('button 2'))); + if (loader.useShadowDom) { + expect( + page.buttons[2].shadowRoot.innerText, isNot(contains('button 2'))); + } else { + expect(page.buttons[2].shadowRoot.innerText, contains('button 2')); + } expect(page.buttons[2].shadowRoot.innerText, contains('some')); }); diff --git a/dart/test/webdriver_no_shadow_dom_test.dart b/dart/test/webdriver_no_shadow_dom_test.dart new file mode 100644 index 00000000..5f83134a --- /dev/null +++ b/dart/test/webdriver_no_shadow_dom_test.dart @@ -0,0 +1,82 @@ +/* + * Copyright 2014 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. + */ +library pageloader.test.webdriver_no_shadow_dom; + +import 'pageloader_test.dart' as plt; + +import 'package:pageloader/webdriver.dart'; +import 'package:path/path.dart' as path; +import 'package:unittest/unittest.dart'; +import 'package:unittest/vm_config.dart'; +import 'package:sync_webdriver/sync_webdriver.dart' hide Platform; + +/// These tests are not expected to be run as part of normal automated testing, +/// as they are slow and they have external dependencies. +void main() { + useVMConfiguration(); + + WebDriver driver; + + setUp(() { + driver = freshDriver; + driver.url = testPagePath; + plt.loader = new WebDriverPageLoader(driver, useShadowDom: false); + }); + + plt.runTests(); + + // This test needs to be last to properly close the browser. + test('one-time teardown', () { + closeDriver(); + }); +} + +String get testPagePath => path + .toUri(path.absolute('webdriver_no_shadow_dom_test_page.html')) + .toString(); + +WebDriver _driver; + +WebDriver get freshDriver { + if (_driver != null) { + try { + Window firstWindow = null; + + for (Window window in _driver.windows) { + if (firstWindow == null) { + firstWindow = window; + } else { + _driver.switchTo.window(window); + _driver.close(); + } + } + _driver.switchTo.window(firstWindow); + _driver.url = 'about:'; + } catch (e) { + closeDriver(); + } + } + if (_driver == null) { + _driver = new WebDriver(desired: Capabilities.chrome); + } + return _driver; +} + +void closeDriver() { + try { + _driver.quit(); + } catch (e) {} + _driver = null; +} diff --git a/dart/test/webdriver_no_shadow_dom_test_page.html b/dart/test/webdriver_no_shadow_dom_test_page.html new file mode 100644 index 00000000..3a393a1e --- /dev/null +++ b/dart/test/webdriver_no_shadow_dom_test_page.html @@ -0,0 +1,74 @@ + + + + + test_page + + + + + + + + + + + + + + +
r1c1r1c2
r2c1r2c2
+ +
area for mouse events
+ + + + + + test + + + +
+ outer div 1 + +
+
+ outer div 2 +
+ inner div 1 +
+
+ inner div 2 +
+
+ + + + + + + + diff --git a/dart/test/webdriver_test.dart b/dart/test/webdriver_test.dart index 820465f1..99b8ed48 100644 --- a/dart/test/webdriver_test.dart +++ b/dart/test/webdriver_test.dart @@ -44,7 +44,7 @@ void main() { } String get testPagePath => - path.toUri(path.absolute('test_page.html')).toString(); + path.toUri(path.absolute('webdriver_test_page.html')).toString(); WebDriver _driver; diff --git a/dart/test/webdriver_test_page.html b/dart/test/webdriver_test_page.html new file mode 100644 index 00000000..713f6499 --- /dev/null +++ b/dart/test/webdriver_test_page.html @@ -0,0 +1,86 @@ + + + + + test_page + + + + + + + + + + + + + + +
r1c1r1c2
r2c1r2c2
+ +
area for mouse events
+ + + + + + test + + + +
+ outer div 1 + +
+
+ outer div 2 +
+ inner div 1 +
+
+ inner div 2 +
+
+ + button 1 + + + button 2 + + + + + From e18b9126998818e10ff167cd81558a7f710ae059 Mon Sep 17 00:00:00 2001 From: Marc Fisher Date: Wed, 4 Feb 2015 10:18:47 -0800 Subject: [PATCH 2/2] Delete test_page.html --- dart/test/test_page.html | 86 ---------------------------------------- 1 file changed, 86 deletions(-) delete mode 100644 dart/test/test_page.html diff --git a/dart/test/test_page.html b/dart/test/test_page.html deleted file mode 100644 index 713f6499..00000000 --- a/dart/test/test_page.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - test_page - - - - - - - - - - - - - - -
r1c1r1c2
r2c1r2c2
- -
area for mouse events
- - - - - - test - - - -
- outer div 1 - -
-
- outer div 2 -
- inner div 1 -
-
- inner div 2 -
-
- - button 1 - - - button 2 - - - - -