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
This commit is contained in:
parent
d90a2269f9
commit
93a1ec29e1
23
gulpfile.js
23
gulpfile.js
|
@ -434,7 +434,7 @@ gulp.task('test.js', function(done) {
|
|||
|
||||
gulp.task('test.dart', function(done) {
|
||||
runSequence('versions.dart', 'test.transpiler.unittest', 'test.unit.dart/ci',
|
||||
sequenceComplete(done));
|
||||
'test.dart.angular2_testing/ci', sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task('versions.dart', function() { dartSdk.logVersion(DART_SDK); });
|
||||
|
@ -787,6 +787,27 @@ gulp.task('test.server.dart', runServerDartTests(gulp, gulpPlugins, {dest: 'dist
|
|||
gulp.task('test.transpiler.unittest',
|
||||
function(done) { runJasmineTests(['tools/transpiler/unittest/**/*.js'], done); });
|
||||
|
||||
// At the moment, dart test requires dartium to be an executable on the path.
|
||||
// Make a temporary directory and symlink dartium from there (just for this command)
|
||||
// so that it can run.
|
||||
var dartiumTmpdir = path.join(os.tmpdir(), 'dartium' + new Date().getTime().toString());
|
||||
gulp.task('test.dart.angular2_testing/ci', ['!pubget.angular2_testing.dart'], function(done) {
|
||||
runSequence('test.dart.angular2_testing_symlink', 'test.dart.angular2_testing',
|
||||
sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
'test.dart.angular2_testing_symlink',
|
||||
shell.task(['mkdir ' + dartiumTmpdir, 'ln -s $DARTIUM_BIN ' + dartiumTmpdir + '/dartium']));
|
||||
|
||||
gulp.task('test.dart.angular2_testing',
|
||||
shell.task(['PATH=$PATH:' + dartiumTmpdir + ' pub run test -p dartium'],
|
||||
{'cwd': 'modules_dart/angular2_testing'}));
|
||||
|
||||
gulp.task(
|
||||
'!pubget.angular2_testing.dart',
|
||||
pubget.dir(gulp, gulpPlugins, {dir: 'modules_dart/angular2_testing', command: DART_SDK.PUB}));
|
||||
|
||||
// -----------------
|
||||
// Pre-test checks
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@ export class MockLocationStrategy extends LocationStrategy {
|
|||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
this.internalPath = url;
|
||||
|
||||
var external = this.prepareExternalUrl(url);
|
||||
this.urlChanges.push(external);
|
||||
var externalUrl = this.prepareExternalUrl(url);
|
||||
this.urlChanges.push(externalUrl);
|
||||
}
|
||||
|
||||
onPopState(fn: (value: any) => void): void { ObservableWrapper.subscribe(this._subject, fn); }
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
Contains helpers to run unit tests for angular2 components and injectables,
|
||||
backed by the `package:test` [library](https://pub.dartlang.org/packages/test).
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
|
||||
Update the dev dependencies in your `pubspec.yaml` to include the angular testing
|
||||
and test packages:
|
||||
|
||||
```yaml
|
||||
dev_dependencies:
|
||||
test: '^0.12.6'
|
||||
angular2_testing: any
|
||||
|
||||
```
|
||||
|
||||
Then in your test files, use angular2_testing helpers in place of `setUp` and `test`:
|
||||
|
||||
```dart
|
||||
import 'package:test/test.dart';
|
||||
import 'package:angular2_testing/angular2_testing.dart';
|
||||
|
||||
void main() {
|
||||
// This must be called at the beginning of your tests.
|
||||
initAngularTests();
|
||||
|
||||
// Initialize the injection tokens you will use in your tests.
|
||||
setUpProviders(() => [provide(MyToken, useValue: 'my string'), TestService]);
|
||||
|
||||
// You can then get tokens from the injector via ngSetUp and ngTest.
|
||||
ngSetUp((TestService testService) {
|
||||
testService.initialize();
|
||||
});
|
||||
|
||||
ngTest('can grab injected values', (@Inject(MyToken) token, TestService testService) {
|
||||
expect(token, equals('my string'));
|
||||
expect(testService.status, equals('ready'));
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
A sample test is available in `test/angular2_testing_test.dart`.
|
|
@ -0,0 +1,130 @@
|
|||
library angular2_testing.angular2_testing;
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test/src/backend/invoker.dart';
|
||||
import 'package:test/src/backend/live_test.dart';
|
||||
|
||||
import 'package:angular2/angular2.dart';
|
||||
import 'package:angular2/src/core/di/injector.dart' show Injector;
|
||||
import 'package:angular2/src/core/di/metadata.dart' show InjectMetadata;
|
||||
import 'package:angular2/src/core/di/exceptions.dart' show NoAnnotationError;
|
||||
import 'package:angular2/platform/browser_static.dart' show BrowserDomAdapter;
|
||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
|
||||
import 'package:angular2/src/testing/test_injector.dart';
|
||||
export 'package:angular2/src/testing/test_component_builder.dart';
|
||||
export 'package:angular2/src/testing/test_injector.dart' show inject;
|
||||
|
||||
/// One time initialization that must be done for Angular2 component
|
||||
/// tests. Call before any test methods.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// main() {
|
||||
/// initAngularTests();
|
||||
/// group(...);
|
||||
/// }
|
||||
/// ```
|
||||
void initAngularTests() {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
}
|
||||
|
||||
/// Allows overriding default bindings defined in test_injector.dart.
|
||||
///
|
||||
/// The given function must return a list of DI providers.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// setUpProviders(() => [
|
||||
/// provide(Compiler, useClass: MockCompiler),
|
||||
/// provide(SomeToken, useValue: myValue),
|
||||
/// ]);
|
||||
/// ```
|
||||
void setUpProviders(Iterable<Provider> providerFactory()) {
|
||||
setUp(() {
|
||||
if (_currentInjector != null) {
|
||||
throw 'setUpProviders was called after the injector had '
|
||||
'been used in a setUp or test block. This invalidates the '
|
||||
'test injector';
|
||||
}
|
||||
_currentTestProviders.addAll(providerFactory());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
dynamic _runInjectableFunction(Function fn) {
|
||||
var params = reflector.parameters(fn);
|
||||
List<dynamic> tokens = <dynamic>[];
|
||||
for (var param in params) {
|
||||
var token = null;
|
||||
for (var paramMetadata in param) {
|
||||
if (paramMetadata is Type) {
|
||||
token = paramMetadata;
|
||||
} else if (paramMetadata is InjectMetadata) {
|
||||
token = paramMetadata.token;
|
||||
}
|
||||
}
|
||||
if (token == null) {
|
||||
throw new NoAnnotationError(fn, params);
|
||||
}
|
||||
tokens.add(token);
|
||||
}
|
||||
|
||||
if (_currentInjector == null) {
|
||||
_currentInjector = createTestInjector(_currentTestProviders);
|
||||
}
|
||||
var injectFn = new FunctionWithParamTokens(tokens, fn, false);
|
||||
return injectFn.execute(_currentInjector);
|
||||
}
|
||||
|
||||
/// Use the test injector to get bindings and run a function.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// ngSetUp((SomeToken token) {
|
||||
/// token.init();
|
||||
/// });
|
||||
/// ```
|
||||
void ngSetUp(Function fn) {
|
||||
setUp(() async {
|
||||
await _runInjectableFunction(fn);
|
||||
});
|
||||
}
|
||||
|
||||
/// Add a test which can use the test injector.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// ngTest('description', (SomeToken token) {
|
||||
/// expect(token, equals('expected'));
|
||||
/// });
|
||||
/// ```
|
||||
void ngTest(String description, Function fn,
|
||||
{String testOn, Timeout timeout, skip, Map<String, dynamic> onPlatform}) {
|
||||
test(description, () async {
|
||||
await _runInjectableFunction(fn);
|
||||
}, testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform);
|
||||
}
|
||||
|
||||
final _providersExpando = new Expando<List<Provider>>('Providers for the current test');
|
||||
final _injectorExpando = new Expando<Injector>('Angular Injector for the current test');
|
||||
|
||||
List get _currentTestProviders {
|
||||
if (_providersExpando[_currentTest] == null) {
|
||||
return _providersExpando[_currentTest] = [];
|
||||
}
|
||||
return _providersExpando[_currentTest];
|
||||
}
|
||||
Injector get _currentInjector => _injectorExpando[_currentTest];
|
||||
void set _currentInjector(Injector newInjector) {
|
||||
_injectorExpando[_currentTest] = newInjector;
|
||||
}
|
||||
|
||||
// TODO: warning, the Invoker.current.liveTest is not a settled API and is
|
||||
// subject to change in future versions of package:test.
|
||||
LiveTest get _currentTest => Invoker.current.liveTest;
|
|
@ -0,0 +1,8 @@
|
|||
name: angular2_testing
|
||||
environment:
|
||||
sdk: '>=1.10.0 <2.0.0'
|
||||
dependencies:
|
||||
angular2:
|
||||
path: ../../dist/dart/angular2
|
||||
dev_dependencies:
|
||||
test: '^0.12.6'
|
|
@ -0,0 +1,99 @@
|
|||
// Because Angular is using dart:html, we need these tests to run on an actual
|
||||
// browser. This means that it should be run with `-p dartium` or `-p chrome`.
|
||||
@TestOn('browser')
|
||||
import 'package:angular2/angular2.dart'
|
||||
show Component, View, NgFor, provide, Inject, Injectable, Optional;
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:angular2_testing/angular2_testing.dart';
|
||||
|
||||
// This is the component we will be testing.
|
||||
@Component(selector: 'test-cmp')
|
||||
@View(directives: const [NgFor])
|
||||
class TestComponent {
|
||||
List<num> items;
|
||||
TestComponent() {
|
||||
this.items = [1, 2];
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class TestService {
|
||||
String status = 'not ready';
|
||||
|
||||
init() {
|
||||
this.status = 'ready';
|
||||
}
|
||||
}
|
||||
|
||||
class MyToken {}
|
||||
|
||||
const TEMPLATE =
|
||||
'<div><copy-me template=\'ng-for #item of items\'>{{item.toString()}};</copy-me></div>';
|
||||
|
||||
void main() {
|
||||
initAngularTests();
|
||||
|
||||
setUpProviders(() => [provide(MyToken, useValue: 'my string'), TestService]);
|
||||
|
||||
test('normal function', () {
|
||||
var string = 'foo,bar,baz';
|
||||
expect(string.split(','), equals(['foo', 'bar', 'baz']));
|
||||
});
|
||||
|
||||
ngTest('can grab injected values', (@Inject(MyToken) token, TestService testService) {
|
||||
expect(token, equals('my string'));
|
||||
expect(testService.status, equals('not ready'));
|
||||
});
|
||||
|
||||
group('nested ngSetUp', () {
|
||||
ngSetUp((TestService testService) {
|
||||
testService.init();
|
||||
});
|
||||
|
||||
ngTest('ngSetUp modifies injected services', (TestService testService) {
|
||||
expect(testService.status, equals('ready'));
|
||||
});
|
||||
});
|
||||
|
||||
ngTest('create a component using the TestComponentBuilder', (TestComponentBuilder tcb) async {
|
||||
var rootTC = await tcb
|
||||
.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent);
|
||||
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.debugElement.nativeElement.text, equals('1;2;'));
|
||||
});
|
||||
|
||||
ngTest('should reflect added elements', (TestComponentBuilder tcb) async {
|
||||
var rootTC = await tcb
|
||||
.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent);
|
||||
|
||||
rootTC.detectChanges();
|
||||
(rootTC.debugElement.componentInstance.items as List<num>).add(3);
|
||||
rootTC.detectChanges();
|
||||
|
||||
expect(rootTC.debugElement.nativeElement.text, equals('1;2;3;'));
|
||||
});
|
||||
|
||||
group('expected failures', () {
|
||||
ngTest('no type in param list', (notTyped) {
|
||||
expect(1, equals(2));
|
||||
});
|
||||
|
||||
ngSetUp((TestService testService) {
|
||||
testService.init();
|
||||
});
|
||||
|
||||
// This would fail, since setUpProviders is used after a call to ngSetUp has already
|
||||
// initialized the injector.
|
||||
group('nested', () {
|
||||
setUpProviders(() => [TestService]);
|
||||
|
||||
test('foo', () {
|
||||
expect(1 + 1, equals(2));
|
||||
});
|
||||
});
|
||||
}, skip: 'expected failures');
|
||||
}
|
Loading…
Reference in New Issue