189 lines
5.9 KiB
Dart
189 lines
5.9 KiB
Dart
library testability.browser_testability;
|
|
|
|
import 'package:angular2/core.dart';
|
|
import 'package:angular2/platform/common_dom.dart';
|
|
|
|
import 'dart:html';
|
|
import 'dart:js' as js;
|
|
|
|
// Work around http://dartbug.com/17752, copied from
|
|
// https://github.com/angular/angular.dart/blob/master/lib/introspection.dart
|
|
// Proxies a Dart function that accepts up to 10 parameters.
|
|
js.JsFunction _jsFunction(Function fn) {
|
|
const Object X = __varargSentinel;
|
|
return new js.JsFunction.withThis((thisArg,
|
|
[o1 = X,
|
|
o2 = X,
|
|
o3 = X,
|
|
o4 = X,
|
|
o5 = X,
|
|
o6 = X,
|
|
o7 = X,
|
|
o8 = X,
|
|
o9 = X,
|
|
o10 = X]) {
|
|
return __invokeFn(fn, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10);
|
|
});
|
|
}
|
|
|
|
const Object __varargSentinel = const Object();
|
|
|
|
__invokeFn(fn, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10) {
|
|
var args = [o1, o2, o3, o4, o5, o6, o7, o8, o9, o10];
|
|
while (args.length > 0 && identical(args.last, __varargSentinel)) {
|
|
args.removeLast();
|
|
}
|
|
return _jsify(Function.apply(fn, args));
|
|
}
|
|
|
|
// Helper function to JSify a Dart object. While this is *required* to JSify
|
|
// the result of a scope.eval(), other uses are not required and are used to
|
|
// work around http://dartbug.com/17752 in a convenient way (that bug affects
|
|
// dart2js in checked mode.)
|
|
_jsify(var obj) {
|
|
if (obj == null || obj is js.JsObject) {
|
|
return obj;
|
|
}
|
|
if (obj is _JsObjectProxyable) {
|
|
return obj._toJsObject();
|
|
}
|
|
if (obj is Function) {
|
|
return _jsFunction(obj);
|
|
}
|
|
if ((obj is Map) || (obj is Iterable)) {
|
|
var mappedObj = (obj is Map)
|
|
? new Map.fromIterables(obj.keys, obj.values.map(_jsify))
|
|
: obj.map(_jsify);
|
|
if (obj is List) {
|
|
return new js.JsArray.from(mappedObj);
|
|
} else {
|
|
return new js.JsObject.jsify(mappedObj);
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
abstract class _JsObjectProxyable {
|
|
js.JsObject _toJsObject();
|
|
}
|
|
|
|
class PublicTestability implements _JsObjectProxyable {
|
|
Testability _testability;
|
|
PublicTestability(Testability testability) {
|
|
this._testability = testability;
|
|
}
|
|
|
|
bool isStable() {
|
|
return this._testability.isStable();
|
|
}
|
|
|
|
whenStable(Function callback) {
|
|
return this._testability.whenStable(callback);
|
|
}
|
|
|
|
findBindings(Element elem, String binding, bool exactMatch) {
|
|
return this._testability.findBindings(elem, binding, exactMatch);
|
|
}
|
|
|
|
js.JsObject _toJsObject() {
|
|
return _jsify({
|
|
'findBindings': (bindingString, [exactMatch, allowNonElementNodes]) =>
|
|
findBindings(bindingString, exactMatch, allowNonElementNodes),
|
|
'isStable': () => isStable(),
|
|
'whenStable': (callback) => whenStable((didWork) => callback.apply([didWork]))
|
|
})..['_dart_'] = this;
|
|
}
|
|
}
|
|
|
|
class BrowserGetTestability implements GetTestability {
|
|
const BrowserGetTestability();
|
|
|
|
static init() {
|
|
setTestabilityGetter(const BrowserGetTestability());
|
|
}
|
|
|
|
void addToWindow(TestabilityRegistry registry) {
|
|
var jsRegistry = js.context['ngTestabilityRegistries'];
|
|
if (jsRegistry == null) {
|
|
js.context['ngTestabilityRegistries'] = jsRegistry = new js.JsArray();
|
|
js.context['getAngularTestability'] =
|
|
_jsify((Element elem, [bool findInAncestors = true]) {
|
|
var registry = js.context['ngTestabilityRegistries'];
|
|
for (int i = 0; i < registry.length; i++) {
|
|
var result = registry[i]
|
|
.callMethod('getAngularTestability', [elem, findInAncestors]);
|
|
if (result != null) return result;
|
|
}
|
|
throw 'Could not find testability for element.';
|
|
});
|
|
var getAllAngularTestabilities = () {
|
|
var registry = js.context['ngTestabilityRegistries'];
|
|
var result = [];
|
|
for (int i = 0; i < registry.length; i++) {
|
|
var testabilities =
|
|
registry[i].callMethod('getAllAngularTestabilities');
|
|
if (testabilities != null) result.addAll(testabilities);
|
|
}
|
|
return _jsify(result);
|
|
};
|
|
js.context['getAllAngularTestabilities'] =
|
|
_jsify(getAllAngularTestabilities);
|
|
|
|
var whenAllStable = _jsify((callback) {
|
|
var testabilities = getAllAngularTestabilities();
|
|
var count = testabilities.length;
|
|
var didWork = false;
|
|
var decrement = _jsify((bool didWork_) {
|
|
didWork = didWork || didWork_;
|
|
count--;
|
|
if (count == 0) {
|
|
callback.apply([didWork]);
|
|
}
|
|
});
|
|
testabilities.forEach((testability) {
|
|
testability.callMethod('whenStable', [decrement]);
|
|
});
|
|
});
|
|
if (js.context['frameworkStabilizers'] == null) {
|
|
js.context['frameworkStabilizers'] = new js.JsArray();
|
|
}
|
|
js.context['frameworkStabilizers'].add(whenAllStable);
|
|
}
|
|
jsRegistry.add(this._createRegistry(registry));
|
|
}
|
|
|
|
findTestabilityInTree(TestabilityRegistry registry, dynamic elem, bool findInAncestors) {
|
|
if (elem == null) {
|
|
return null;
|
|
}
|
|
var t = registry.getTestability(elem);
|
|
if (t != null) {
|
|
return t;
|
|
} else if (!findInAncestors) {
|
|
return null;
|
|
}
|
|
if (getDOM().isShadowRoot(elem)) {
|
|
return this.findTestabilityInTree(registry, getDOM().getHost(elem), true);
|
|
}
|
|
return this.findTestabilityInTree(registry, getDOM().parentElement(elem), true);
|
|
}
|
|
|
|
js.JsObject _createRegistry(TestabilityRegistry registry) {
|
|
var object = new js.JsObject(js.context['Object']);
|
|
object['getAngularTestability'] =
|
|
_jsify((Element elem, bool findInAncestors) {
|
|
var testability = registry.findTestabilityInTree(elem, findInAncestors);
|
|
return testability == null
|
|
? null
|
|
: _jsify(new PublicTestability(testability));
|
|
});
|
|
object['getAllAngularTestabilities'] = _jsify(() {
|
|
var publicTestabilities = registry
|
|
.getAllTestabilities()
|
|
.map((testability) => new PublicTestability(testability));
|
|
return _jsify(publicTestabilities);
|
|
});
|
|
return object;
|
|
}
|
|
}
|