X Tutup
Skip to content

Commit 93a1ec2

Browse files
juliemrjelbourn
authored andcommitted
feat(test): add angular2_testing dart library
angular2_testing is a user-facing dart test library built on top of the package:test dart unittest framework and runner. For usage, see modules_dart/angular2_testing/README.md. Closes #3289
1 parent d90a226 commit 93a1ec2

File tree

6 files changed

+307
-3
lines changed

6 files changed

+307
-3
lines changed

gulpfile.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ gulp.task('test.js', function(done) {
434434

435435
gulp.task('test.dart', function(done) {
436436
runSequence('versions.dart', 'test.transpiler.unittest', 'test.unit.dart/ci',
437-
sequenceComplete(done));
437+
'test.dart.angular2_testing/ci', sequenceComplete(done));
438438
});
439439

440440
gulp.task('versions.dart', function() { dartSdk.logVersion(DART_SDK); });
@@ -787,6 +787,27 @@ gulp.task('test.server.dart', runServerDartTests(gulp, gulpPlugins, {dest: 'dist
787787
gulp.task('test.transpiler.unittest',
788788
function(done) { runJasmineTests(['tools/transpiler/unittest/**/*.js'], done); });
789789

790+
// At the moment, dart test requires dartium to be an executable on the path.
791+
// Make a temporary directory and symlink dartium from there (just for this command)
792+
// so that it can run.
793+
var dartiumTmpdir = path.join(os.tmpdir(), 'dartium' + new Date().getTime().toString());
794+
gulp.task('test.dart.angular2_testing/ci', ['!pubget.angular2_testing.dart'], function(done) {
795+
runSequence('test.dart.angular2_testing_symlink', 'test.dart.angular2_testing',
796+
sequenceComplete(done));
797+
});
798+
799+
gulp.task(
800+
'test.dart.angular2_testing_symlink',
801+
shell.task(['mkdir ' + dartiumTmpdir, 'ln -s $DARTIUM_BIN ' + dartiumTmpdir + '/dartium']));
802+
803+
gulp.task('test.dart.angular2_testing',
804+
shell.task(['PATH=$PATH:' + dartiumTmpdir + ' pub run test -p dartium'],
805+
{'cwd': 'modules_dart/angular2_testing'}));
806+
807+
gulp.task(
808+
'!pubget.angular2_testing.dart',
809+
pubget.dir(gulp, gulpPlugins, {dir: 'modules_dart/angular2_testing', command: DART_SDK.PUB}));
810+
790811
// -----------------
791812
// Pre-test checks
792813

modules/angular2/src/mock/mock_location_strategy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ export class MockLocationStrategy extends LocationStrategy {
3737
var url = path + (query.length > 0 ? ('?' + query) : '');
3838
this.internalPath = url;
3939

40-
var external = this.prepareExternalUrl(url);
41-
this.urlChanges.push(external);
40+
var externalUrl = this.prepareExternalUrl(url);
41+
this.urlChanges.push(externalUrl);
4242
}
4343

4444
onPopState(fn: (value: any) => void): void { ObservableWrapper.subscribe(this._subject, fn); }
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Contains helpers to run unit tests for angular2 components and injectables,
2+
backed by the `package:test` [library](https://pub.dartlang.org/packages/test).
3+
4+
Usage
5+
-----
6+
7+
8+
Update the dev dependencies in your `pubspec.yaml` to include the angular testing
9+
and test packages:
10+
11+
```yaml
12+
dev_dependencies:
13+
test: '^0.12.6'
14+
angular2_testing: any
15+
16+
```
17+
18+
Then in your test files, use angular2_testing helpers in place of `setUp` and `test`:
19+
20+
```dart
21+
import 'package:test/test.dart';
22+
import 'package:angular2_testing/angular2_testing.dart';
23+
24+
void main() {
25+
// This must be called at the beginning of your tests.
26+
initAngularTests();
27+
28+
// Initialize the injection tokens you will use in your tests.
29+
setUpProviders(() => [provide(MyToken, useValue: 'my string'), TestService]);
30+
31+
// You can then get tokens from the injector via ngSetUp and ngTest.
32+
ngSetUp((TestService testService) {
33+
testService.initialize();
34+
});
35+
36+
ngTest('can grab injected values', (@Inject(MyToken) token, TestService testService) {
37+
expect(token, equals('my string'));
38+
expect(testService.status, equals('ready'));
39+
});
40+
}
41+
```
42+
43+
Examples
44+
--------
45+
46+
A sample test is available in `test/angular2_testing_test.dart`.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
library angular2_testing.angular2_testing;
2+
3+
import 'package:test/test.dart';
4+
import 'package:test/src/backend/invoker.dart';
5+
import 'package:test/src/backend/live_test.dart';
6+
7+
import 'package:angular2/angular2.dart';
8+
import 'package:angular2/src/core/di/injector.dart' show Injector;
9+
import 'package:angular2/src/core/di/metadata.dart' show InjectMetadata;
10+
import 'package:angular2/src/core/di/exceptions.dart' show NoAnnotationError;
11+
import 'package:angular2/platform/browser_static.dart' show BrowserDomAdapter;
12+
import 'package:angular2/src/core/reflection/reflection.dart';
13+
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
14+
import 'package:angular2/src/testing/test_injector.dart';
15+
export 'package:angular2/src/testing/test_component_builder.dart';
16+
export 'package:angular2/src/testing/test_injector.dart' show inject;
17+
18+
/// One time initialization that must be done for Angular2 component
19+
/// tests. Call before any test methods.
20+
///
21+
/// Example:
22+
///
23+
/// ```
24+
/// main() {
25+
/// initAngularTests();
26+
/// group(...);
27+
/// }
28+
/// ```
29+
void initAngularTests() {
30+
BrowserDomAdapter.makeCurrent();
31+
reflector.reflectionCapabilities = new ReflectionCapabilities();
32+
}
33+
34+
/// Allows overriding default bindings defined in test_injector.dart.
35+
///
36+
/// The given function must return a list of DI providers.
37+
///
38+
/// Example:
39+
///
40+
/// ```
41+
/// setUpProviders(() => [
42+
/// provide(Compiler, useClass: MockCompiler),
43+
/// provide(SomeToken, useValue: myValue),
44+
/// ]);
45+
/// ```
46+
void setUpProviders(Iterable<Provider> providerFactory()) {
47+
setUp(() {
48+
if (_currentInjector != null) {
49+
throw 'setUpProviders was called after the injector had '
50+
'been used in a setUp or test block. This invalidates the '
51+
'test injector';
52+
}
53+
_currentTestProviders.addAll(providerFactory());
54+
});
55+
}
56+
57+
58+
dynamic _runInjectableFunction(Function fn) {
59+
var params = reflector.parameters(fn);
60+
List<dynamic> tokens = <dynamic>[];
61+
for (var param in params) {
62+
var token = null;
63+
for (var paramMetadata in param) {
64+
if (paramMetadata is Type) {
65+
token = paramMetadata;
66+
} else if (paramMetadata is InjectMetadata) {
67+
token = paramMetadata.token;
68+
}
69+
}
70+
if (token == null) {
71+
throw new NoAnnotationError(fn, params);
72+
}
73+
tokens.add(token);
74+
}
75+
76+
if (_currentInjector == null) {
77+
_currentInjector = createTestInjector(_currentTestProviders);
78+
}
79+
var injectFn = new FunctionWithParamTokens(tokens, fn, false);
80+
return injectFn.execute(_currentInjector);
81+
}
82+
83+
/// Use the test injector to get bindings and run a function.
84+
///
85+
/// Example:
86+
///
87+
/// ```
88+
/// ngSetUp((SomeToken token) {
89+
/// token.init();
90+
/// });
91+
/// ```
92+
void ngSetUp(Function fn) {
93+
setUp(() async {
94+
await _runInjectableFunction(fn);
95+
});
96+
}
97+
98+
/// Add a test which can use the test injector.
99+
///
100+
/// Example:
101+
///
102+
/// ```
103+
/// ngTest('description', (SomeToken token) {
104+
/// expect(token, equals('expected'));
105+
/// });
106+
/// ```
107+
void ngTest(String description, Function fn,
108+
{String testOn, Timeout timeout, skip, Map<String, dynamic> onPlatform}) {
109+
test(description, () async {
110+
await _runInjectableFunction(fn);
111+
}, testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform);
112+
}
113+
114+
final _providersExpando = new Expando<List<Provider>>('Providers for the current test');
115+
final _injectorExpando = new Expando<Injector>('Angular Injector for the current test');
116+
117+
List get _currentTestProviders {
118+
if (_providersExpando[_currentTest] == null) {
119+
return _providersExpando[_currentTest] = [];
120+
}
121+
return _providersExpando[_currentTest];
122+
}
123+
Injector get _currentInjector => _injectorExpando[_currentTest];
124+
void set _currentInjector(Injector newInjector) {
125+
_injectorExpando[_currentTest] = newInjector;
126+
}
127+
128+
// TODO: warning, the Invoker.current.liveTest is not a settled API and is
129+
// subject to change in future versions of package:test.
130+
LiveTest get _currentTest => Invoker.current.liveTest;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: angular2_testing
2+
environment:
3+
sdk: '>=1.10.0 <2.0.0'
4+
dependencies:
5+
angular2:
6+
path: ../../dist/dart/angular2
7+
dev_dependencies:
8+
test: '^0.12.6'
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Because Angular is using dart:html, we need these tests to run on an actual
2+
// browser. This means that it should be run with `-p dartium` or `-p chrome`.
3+
@TestOn('browser')
4+
import 'package:angular2/angular2.dart'
5+
show Component, View, NgFor, provide, Inject, Injectable, Optional;
6+
7+
import 'package:test/test.dart';
8+
import 'package:angular2_testing/angular2_testing.dart';
9+
10+
// This is the component we will be testing.
11+
@Component(selector: 'test-cmp')
12+
@View(directives: const [NgFor])
13+
class TestComponent {
14+
List<num> items;
15+
TestComponent() {
16+
this.items = [1, 2];
17+
}
18+
}
19+
20+
@Injectable()
21+
class TestService {
22+
String status = 'not ready';
23+
24+
init() {
25+
this.status = 'ready';
26+
}
27+
}
28+
29+
class MyToken {}
30+
31+
const TEMPLATE =
32+
'<div><copy-me template=\'ng-for #item of items\'>{{item.toString()}};</copy-me></div>';
33+
34+
void main() {
35+
initAngularTests();
36+
37+
setUpProviders(() => [provide(MyToken, useValue: 'my string'), TestService]);
38+
39+
test('normal function', () {
40+
var string = 'foo,bar,baz';
41+
expect(string.split(','), equals(['foo', 'bar', 'baz']));
42+
});
43+
44+
ngTest('can grab injected values', (@Inject(MyToken) token, TestService testService) {
45+
expect(token, equals('my string'));
46+
expect(testService.status, equals('not ready'));
47+
});
48+
49+
group('nested ngSetUp', () {
50+
ngSetUp((TestService testService) {
51+
testService.init();
52+
});
53+
54+
ngTest('ngSetUp modifies injected services', (TestService testService) {
55+
expect(testService.status, equals('ready'));
56+
});
57+
});
58+
59+
ngTest('create a component using the TestComponentBuilder', (TestComponentBuilder tcb) async {
60+
var rootTC = await tcb
61+
.overrideTemplate(TestComponent, TEMPLATE)
62+
.createAsync(TestComponent);
63+
64+
rootTC.detectChanges();
65+
expect(rootTC.debugElement.nativeElement.text, equals('1;2;'));
66+
});
67+
68+
ngTest('should reflect added elements', (TestComponentBuilder tcb) async {
69+
var rootTC = await tcb
70+
.overrideTemplate(TestComponent, TEMPLATE)
71+
.createAsync(TestComponent);
72+
73+
rootTC.detectChanges();
74+
(rootTC.debugElement.componentInstance.items as List<num>).add(3);
75+
rootTC.detectChanges();
76+
77+
expect(rootTC.debugElement.nativeElement.text, equals('1;2;3;'));
78+
});
79+
80+
group('expected failures', () {
81+
ngTest('no type in param list', (notTyped) {
82+
expect(1, equals(2));
83+
});
84+
85+
ngSetUp((TestService testService) {
86+
testService.init();
87+
});
88+
89+
// This would fail, since setUpProviders is used after a call to ngSetUp has already
90+
// initialized the injector.
91+
group('nested', () {
92+
setUpProviders(() => [TestService]);
93+
94+
test('foo', () {
95+
expect(1 + 1, equals(2));
96+
});
97+
});
98+
}, skip: 'expected failures');
99+
}

0 commit comments

Comments
 (0)
X Tutup