Skip to content

Commit 85be7b8

Browse files
committed
feature(ngProbe): Allow specifying a root Node when using a selector
1 parent 348e501 commit 85be7b8

File tree

3 files changed

+47
-15
lines changed

3 files changed

+47
-15
lines changed

lib/introspection.dart

+26-9
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,35 @@ List<ElementProbe> _findAllProbesInTree(dom.Node node) {
6464
*
6565
* The node parameter could be:
6666
* * a [dom.Node],
67-
* * a CSS selector for this node.
67+
* * a CSS selector to look for a matching node inside the [root] or the dom.document.
68+
*
69+
* Specifying a [root] element allows querying a node that is not attached to the DOM. It is an
70+
* error to pass a [root] element that is already attached to the DOM.
6871
*
6972
* **NOTE:** This global method is here to make it easier to debug Angular
7073
* application from the browser's REPL, unit or end-to-end tests. The
7174
* function is not intended to be called from Angular application.
7275
*/
73-
ElementProbe ngProbe(nodeOrSelector) {
74-
if (nodeOrSelector == null) throw "ngProbe called without node";
76+
ElementProbe ngProbe(nodeOrSelector, [dom.Node root]) {
77+
if (nodeOrSelector == null) throw "ngProbe called without node/selector";
7578
var node = nodeOrSelector;
7679
if (nodeOrSelector is String) {
77-
var nodes = ngQuery(dom.document, nodeOrSelector);
78-
node = (nodes.isNotEmpty) ? nodes.first : null;
80+
if (root == null) {
81+
root = dom.document;
82+
} else {
83+
var attached = false;
84+
for (var parent = root.parentNode; parent != null; parent = parent.parentNode) {
85+
if (parent == dom.document) {
86+
attached = true;
87+
break;
88+
}
89+
}
90+
if (attached) throw "The root element must not be attached to the DOM";
91+
root = new dom.DivElement()..append(root);
92+
}
93+
var nodes = ngQuery(root, nodeOrSelector);
94+
if (nodes.isEmpty) throw "The '$nodeOrSelector' selector does not match any node";
95+
node = nodes.first;
7996
}
8097
var probe = _findProbeWalkingUp(node);
8198
if (probe != null) {
@@ -93,7 +110,8 @@ ElementProbe ngProbe(nodeOrSelector) {
93110
* application from the browser's REPL, unit or end-to-end tests. The function
94111
* is not intended to be called from Angular application.
95112
*/
96-
DirectiveInjector ngInjector(nodeOrSelector) => ngProbe(nodeOrSelector).injector;
113+
DirectiveInjector ngInjector(nodeOrSelector, [dom.Node root]) =>
114+
ngProbe(nodeOrSelector, root).injector;
97115

98116

99117
/**
@@ -103,11 +121,10 @@ DirectiveInjector ngInjector(nodeOrSelector) => ngProbe(nodeOrSelector).injector
103121
* application from the browser's REPL, unit or end-to-end tests. The function
104122
* is not intended to be called from Angular application.
105123
*/
106-
Scope ngScope(nodeOrSelector) => ngProbe(nodeOrSelector).scope;
124+
Scope ngScope(nodeOrSelector, [dom.Node root]) => ngProbe(nodeOrSelector, root).scope;
107125

108126

109-
List<dom.Element> ngQuery(dom.Node element, String selector,
110-
[String containsText]) {
127+
List<dom.Element> ngQuery(dom.Node element, String selector, [String containsText]) {
111128
var list = [];
112129
var children = [element];
113130
if ((element is dom.Element) && element.shadowRoot != null) {

lib/mock/test_bed.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class TestBed {
3131
* - [List<Node>] then treat it as a collection of nods
3232
*
3333
* After the compilation the [rootElements] contains an array of compiled root nodes,
34-
* and [rootElement] contains the first element from the [rootElemets].
34+
* and [rootElement] contains the first element from the [rootElements].
3535
*
3636
* An option [scope] parameter can be supplied to link it with non root scope.
3737
*/

test/introspection_spec.dart

+20-5
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,32 @@ void main() {
3535
it('should select probe using CSS selector', (TestBed _) {
3636
_.compile('<div ng-show="true">WORKS</div>');
3737
document.body.append(_.rootElement);
38-
var div = new Element.html('<div><p><span></span></p></div>');
39-
var span = div.querySelector('span');
40-
var shadowRoot = span.createShadowRoot();
41-
shadowRoot.innerHtml = '<ul><li>stash</li><li>secret</li><ul>';
42-
4338
ElementProbe probe = ngProbe('[ng-show]');
4439
expect(probe).toBeDefined();
4540
expect(probe.injector.get(NgShow) is NgShow).toEqual(true);
4641
_.rootElement.remove();
4742
});
4843

44+
it('should throw if the root Element is attached to the DOM', (TestBed _) {
45+
_.compile('<div ng-show="true">WORKS</div>');
46+
document.body.append(_.rootElement);
47+
expect(() => ngProbe('[ng-show]', _.rootElement))
48+
.toThrow("The root element must not be attached to the DOM");
49+
});
50+
51+
it('should select probe using CSS selector inside the given root element',(TestBed _) {
52+
_.compile('<div ng-show="true">WORKS</div>');
53+
ElementProbe probe = ngProbe('[ng-show]', _.rootElement);
54+
expect(probe).toBeDefined();
55+
expect(probe.injector.get(NgShow) is NgShow).toEqual(true);
56+
});
57+
58+
it('should throw when the selector does not match any element', (TestBed _) {
59+
_.compile('<div></div>');
60+
expect(() => ngProbe('input', _.rootElement))
61+
.toThrowWith(message: "The 'input' selector does not match any node");
62+
});
63+
4964
it('should select elements in the root shadow root', () {
5065
var div = new Element.html('<div></div>');
5166
var shadowRoot = div.createShadowRoot();

0 commit comments

Comments
 (0)