Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Commit d220ecf

Browse files
committed
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'))
1 parent 2658865 commit d220ecf

File tree

7 files changed

+110
-0
lines changed

7 files changed

+110
-0
lines changed

lib/locators.js

+23
Original file line numberDiff line numberDiff line change
@@ -428,4 +428,27 @@ ProtractorBy.prototype.options = function(optionsDescriptor) {
428428
};
429429
};
430430

431+
/**
432+
* Find an element by css selector within the Shadow DOM.
433+
*
434+
* @alias by.deepCss(selector)
435+
* @view
436+
* <div>
437+
* <span id="outerspan">
438+
* <"shadow tree">
439+
* <span id="span1"></span>
440+
* <"shadow tree">
441+
* <span id="span2"></span>
442+
* </>
443+
* </>
444+
* </div>
445+
* @example
446+
* spans = element.all(by.deepCss('span'));
447+
* expect(spans.count()).toEqual(3);
448+
*/
449+
ProtractorBy.prototype.deepCss = function(selector) {
450+
// return webdriver.By.css('* >>> ' + selector);
451+
return webdriver.By.css('* /deep/ ' + selector);
452+
};
453+
431454
exports.ProtractorBy = ProtractorBy;

spec/basic/locators_spec.js

+23
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,29 @@ describe('locators', function() {
355355
});
356356
});
357357

358+
describe('by deep css', function() {
359+
beforeEach(function() {
360+
browser.get('index.html#/shadow');
361+
});
362+
363+
it('should find items inside the shadow DOM', function() {
364+
var parentHeading = element(by.deepCss('.parentshadowheading'));
365+
var olderChildHeading = element(by.deepCss('.oldershadowheading'));
366+
var youngerChildHeading = element(by.deepCss('.youngershadowheading'));
367+
368+
expect(parentHeading.isPresent()).toBe(true);
369+
expect(olderChildHeading.isPresent()).toBe(true);
370+
expect(youngerChildHeading.isPresent()).toBe(true);
371+
372+
expect(parentHeading.getText()).toEqual('Parent');
373+
expect(olderChildHeading.getText()).toEqual('Older Child');
374+
expect(youngerChildHeading.getText()).toEqual('Younger Child');
375+
376+
expect(element(by.deepCss('.originalcontent')).getText())
377+
.toEqual('original content');
378+
});
379+
});
380+
358381
it('should determine if an element is present', function() {
359382
expect(browser.isElementPresent(by.binding('greet'))).toBe(true);
360383
expect(browser.isElementPresent(by.binding('nopenopenope'))).toBe(false);

testapp/alt_root_index.html

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<script src="repeater/repeater.js"></script>
4040
<script src="animation/animation.js"></script>
4141
<script src="interaction/interaction.js"></script>
42+
<script src="shadow/shadow.js"></script>
4243
<script src="app.js"></script>
4344
</body>
4445
</html>

testapp/app.js

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ angular.module('myApp', ['ngAnimate', 'ngRoute', 'myApp.appVersion']).
1212
$routeProvider.when('/polling', {templateUrl: 'polling/polling.html', controller: PollingCtrl});
1313
$routeProvider.when('/animation', {templateUrl: 'animation/animation.html', controller: AnimationCtrl});
1414
$routeProvider.when('/interaction', {templateUrl: 'interaction/interaction.html', controller: InteractionCtrl});
15+
$routeProvider.when('/shadow', {templateUrl: 'shadow/shadow.html', controller: ShadowCtrl});
1516
$routeProvider.when('/slowloader', {
1617
templateUrl: 'polling/polling.html',
1718
controller: PollingCtrl,

testapp/index.html

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<li><a href="#/polling">polling</a></li>
1717
<li><a href="#/animation">animation</a></li>
1818
<li><a href="#/interaction">interaction</a></li>
19+
<li><a href="#/shadow">shadow dom</a></li>
1920
</ul>
2021

2122
<div ng-view></div>
@@ -35,6 +36,7 @@
3536
<script src="repeater/repeater.js"></script>
3637
<script src="animation/animation.js"></script>
3738
<script src="interaction/interaction.js"></script>
39+
<script src="shadow/shadow.js"></script>
3840
<script src="app.js"></script>
3941

4042
</body>

testapp/shadow/shadow.html

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<H1>Elements in shadow DOM</H1>
2+
<p>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.
3+
<div>
4+
<div id="innerDiv" style="border-style:solid;border-color:yellow">
5+
<span class='originalcontent'>original content</span>
6+
</div>
7+
</div>
8+
<template id="parentTemplate">
9+
<div id="parentDiv">
10+
<div style="border-style:solid;border-color:green">
11+
<H3 class="shadowheading parentshadowheading">Parent</H3>
12+
<H4>Parent Contents</H4>
13+
<content></content>
14+
</div>
15+
</div>
16+
</template>
17+
<template id="olderChildTemplate">
18+
<div id="olderChildDiv">
19+
<div style="border-style:solid;border-color:red">
20+
<H3 class="shadowheading oldershadowheading">Older Child</H3>
21+
<H4>Older Child Contents</H4>
22+
<content></content>
23+
</div>
24+
</div>
25+
</template>
26+
<template id="youngerChildTemplate">
27+
<div id="youngerChildDiv">
28+
<div style="border-style:solid;border-color:blue">
29+
<H3 class="shadowheading youngershadowheading">Younger Child</H3>
30+
<div style="border-style:dotted;border-color:blue">
31+
<H4>
32+
Younger Child Contents
33+
</H4>
34+
<content></content>
35+
</div>
36+
<div style="border-style:dashed;border-color:blue">
37+
<H4>Younger Child Shadow</H4>
38+
<shadow></shadow>
39+
</div>
40+
</div>
41+
</div>
42+
</template>

testapp/shadow/shadow.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
function ShadowCtrl($scope) {
2+
// This is terrible Angular.js style, do not put DOM manipulation inside
3+
// controllers like this.
4+
var parentShadowRoot = document.querySelector('#innerDiv')
5+
.createShadowRoot();
6+
parentShadowRoot.appendChild(document.querySelector('#parentTemplate')
7+
.content.cloneNode(true));
8+
var olderShadowRoot = parentShadowRoot.querySelector("#parentDiv")
9+
.createShadowRoot();
10+
olderShadowRoot.appendChild(document.querySelector('#olderChildTemplate')
11+
.content.cloneNode(true));
12+
var youngerShadowRoot = parentShadowRoot.querySelector("#parentDiv")
13+
.createShadowRoot();
14+
youngerShadowRoot.appendChild(document.querySelector('#youngerChildTemplate')
15+
.content.cloneNode(true));
16+
}
17+
18+
RepeaterCtrl.$inject = ['$scope'];

0 commit comments

Comments
 (0)