From d220ecf5ebc7ba023eab728d4a684e978ff77c83 Mon Sep 17 00:00:00 2001 From: Julie Ralph Date: Sun, 15 Feb 2015 16:21:34 -0800 Subject: [PATCH] feat(locators): add by.deepCss selector for finding elements in the shadow dom Usage: element(by.deepCss('.foo')) equivalent to 'element(by.css('* /deep/ .foo')) --- lib/locators.js | 23 ++++++++++++++++++++ spec/basic/locators_spec.js | 23 ++++++++++++++++++++ testapp/alt_root_index.html | 1 + testapp/app.js | 1 + testapp/index.html | 2 ++ testapp/shadow/shadow.html | 42 +++++++++++++++++++++++++++++++++++++ testapp/shadow/shadow.js | 18 ++++++++++++++++ 7 files changed, 110 insertions(+) create mode 100644 testapp/shadow/shadow.html create mode 100644 testapp/shadow/shadow.js diff --git a/lib/locators.js b/lib/locators.js index 36e391bde..db29a8790 100644 --- a/lib/locators.js +++ b/lib/locators.js @@ -428,4 +428,27 @@ ProtractorBy.prototype.options = function(optionsDescriptor) { }; }; +/** + * Find an element by css selector within the Shadow DOM. + * + * @alias by.deepCss(selector) + * @view + *
+ * + * <"shadow tree"> + * + * <"shadow tree"> + * + * + * + *
+ * @example + * spans = element.all(by.deepCss('span')); + * expect(spans.count()).toEqual(3); + */ +ProtractorBy.prototype.deepCss = function(selector) { + // return webdriver.By.css('* >>> ' + selector); + return webdriver.By.css('* /deep/ ' + selector); +}; + exports.ProtractorBy = ProtractorBy; diff --git a/spec/basic/locators_spec.js b/spec/basic/locators_spec.js index 8899bfe24..930d87856 100644 --- a/spec/basic/locators_spec.js +++ b/spec/basic/locators_spec.js @@ -355,6 +355,29 @@ describe('locators', function() { }); }); + describe('by deep css', function() { + beforeEach(function() { + browser.get('index.html#/shadow'); + }); + + it('should find items inside the shadow DOM', function() { + var parentHeading = element(by.deepCss('.parentshadowheading')); + var olderChildHeading = element(by.deepCss('.oldershadowheading')); + var youngerChildHeading = element(by.deepCss('.youngershadowheading')); + + expect(parentHeading.isPresent()).toBe(true); + expect(olderChildHeading.isPresent()).toBe(true); + expect(youngerChildHeading.isPresent()).toBe(true); + + expect(parentHeading.getText()).toEqual('Parent'); + expect(olderChildHeading.getText()).toEqual('Older Child'); + expect(youngerChildHeading.getText()).toEqual('Younger Child'); + + expect(element(by.deepCss('.originalcontent')).getText()) + .toEqual('original content'); + }); + }); + it('should determine if an element is present', function() { expect(browser.isElementPresent(by.binding('greet'))).toBe(true); expect(browser.isElementPresent(by.binding('nopenopenope'))).toBe(false); diff --git a/testapp/alt_root_index.html b/testapp/alt_root_index.html index 38c4ece8c..9f1918e2a 100644 --- a/testapp/alt_root_index.html +++ b/testapp/alt_root_index.html @@ -39,6 +39,7 @@ + diff --git a/testapp/app.js b/testapp/app.js index 91084379c..00b452dd1 100644 --- a/testapp/app.js +++ b/testapp/app.js @@ -12,6 +12,7 @@ angular.module('myApp', ['ngAnimate', 'ngRoute', 'myApp.appVersion']). $routeProvider.when('/polling', {templateUrl: 'polling/polling.html', controller: PollingCtrl}); $routeProvider.when('/animation', {templateUrl: 'animation/animation.html', controller: AnimationCtrl}); $routeProvider.when('/interaction', {templateUrl: 'interaction/interaction.html', controller: InteractionCtrl}); + $routeProvider.when('/shadow', {templateUrl: 'shadow/shadow.html', controller: ShadowCtrl}); $routeProvider.when('/slowloader', { templateUrl: 'polling/polling.html', controller: PollingCtrl, diff --git a/testapp/index.html b/testapp/index.html index 2359d6fa9..39581223b 100644 --- a/testapp/index.html +++ b/testapp/index.html @@ -16,6 +16,7 @@
  • polling
  • animation
  • interaction
  • +
  • shadow dom
  • @@ -35,6 +36,7 @@ + diff --git a/testapp/shadow/shadow.html b/testapp/shadow/shadow.html new file mode 100644 index 000000000..f549afc60 --- /dev/null +++ b/testapp/shadow/shadow.html @@ -0,0 +1,42 @@ +

    Elements in shadow DOM

    +

    Inspired by ChromeDriver's page for shadow dom webdriver tests. The page has a shadow root that in turn contains two shadow roots. So we can check behaviour with both nested roots and younger/older sibling roots. +

    +
    + original content +
    +
    + + + diff --git a/testapp/shadow/shadow.js b/testapp/shadow/shadow.js new file mode 100644 index 000000000..db307814a --- /dev/null +++ b/testapp/shadow/shadow.js @@ -0,0 +1,18 @@ +function ShadowCtrl($scope) { + // This is terrible Angular.js style, do not put DOM manipulation inside + // controllers like this. + var parentShadowRoot = document.querySelector('#innerDiv') + .createShadowRoot(); + parentShadowRoot.appendChild(document.querySelector('#parentTemplate') + .content.cloneNode(true)); + var olderShadowRoot = parentShadowRoot.querySelector("#parentDiv") + .createShadowRoot(); + olderShadowRoot.appendChild(document.querySelector('#olderChildTemplate') + .content.cloneNode(true)); + var youngerShadowRoot = parentShadowRoot.querySelector("#parentDiv") + .createShadowRoot(); + youngerShadowRoot.appendChild(document.querySelector('#youngerChildTemplate') + .content.cloneNode(true)); +} + +RepeaterCtrl.$inject = ['$scope'];