| Index: pkg/observe/test/path_observer_test.dart
|
| diff --git a/pkg/observe/test/path_observer_test.dart b/pkg/observe/test/path_observer_test.dart
|
| index dcb8853ba963622cbeffef0c4d567caac3e6b956..0993c69754db0a21d82695618f0181038f38649d 100644
|
| --- a/pkg/observe/test/path_observer_test.dart
|
| +++ b/pkg/observe/test/path_observer_test.dart
|
| @@ -2,6 +2,7 @@
|
| // for details. All rights reserved. Use of this source code is governed by a
|
| // BSD-style license that can be found in the LICENSE file.
|
|
|
| +import 'dart:async';
|
| import 'package:observe/observe.dart';
|
| import 'package:unittest/unittest.dart';
|
| import 'observe_test_utils.dart';
|
| @@ -9,240 +10,293 @@ import 'observe_test_utils.dart';
|
| // This file contains code ported from:
|
| // https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js
|
|
|
| -main() {
|
| +main() => dirtyCheckZone().run(() {
|
| group('PathObserver', observePathTests);
|
| -}
|
|
|
| -observePath(obj, path) => new PathObserver(obj, path);
|
| + group('PropertyPath', () {
|
| + test('toString length', () {
|
| + expectPath(p, str, len) {
|
| + var path = new PropertyPath(p);
|
| + expect(path.toString(), str);
|
| + expect(path.length, len, reason: 'expected path length $len for $path');
|
| + }
|
| +
|
| + expectPath('/foo', '<invalid path>', 0);
|
| + expectPath('abc', 'abc', 1);
|
| + expectPath('a.b.c', 'a.b.c', 3);
|
| + expectPath('a.b.c ', 'a.b.c', 3);
|
| + expectPath(' a.b.c', 'a.b.c', 3);
|
| + expectPath(' a.b.c ', 'a.b.c', 3);
|
| + expectPath('1.abc', '1.abc', 2);
|
| + expectPath([#qux], 'qux', 1);
|
| + expectPath([1, #foo, #bar], '1.foo.bar', 3);
|
| + });
|
| +
|
| + test('caching and ==', () {
|
| + var start = new PropertyPath('abc.0');
|
| + for (int i = 1; i <= 100; i++) {
|
| + expect(identical(new PropertyPath('abc.0'), start), true,
|
| + reason: 'should return identical path');
|
| +
|
| + var p = new PropertyPath('abc.$i');
|
| + expect(identical(p, start), false,
|
| + reason: 'different paths should not be merged');
|
| + }
|
| + var end = new PropertyPath('abc.0');
|
| + expect(identical(end, start), false,
|
| + reason: 'first entry expired');
|
| + expect(end, start, reason: 'different instances are equal');
|
| + });
|
| +
|
| + test('hashCode equal', () {
|
| + var a = new PropertyPath([#foo, 2, #bar]);
|
| + var b = new PropertyPath('foo.2.bar');
|
| + expect(identical(a, b), false, reason: 'only strings cached');
|
| + expect(a, b, reason: 'same paths are equal');
|
| + expect(a.hashCode, b.hashCode, reason: 'equal hashCodes');
|
| + });
|
| +
|
| + test('hashCode not equal', () {
|
| + expect(2.hashCode, isNot(3.hashCode),
|
| + reason: 'test depends on 2 and 3 having different hashcodes');
|
| +
|
| + var a = new PropertyPath([2]);
|
| + var b = new PropertyPath([3]);
|
| + expect(a, isNot(b), reason: 'different paths');
|
| + expect(a.hashCode, isNot(b.hashCode), reason: 'different hashCodes');
|
| + });
|
| + });
|
| +});
|
| +
|
|
|
| observePathTests() {
|
| - observeTest('Degenerate Values', () {
|
| - expect(observePath(null, '').value, null);
|
| - expect(observePath(123, '').value, 123);
|
| - expect(observePath(123, 'foo.bar.baz').value, null);
|
| + test('Degenerate Values', () {
|
| + expect(new PathObserver(null, '').value, null);
|
| + expect(new PathObserver(123, '').value, 123);
|
| + expect(new PathObserver(123, 'foo.bar.baz').value, null);
|
|
|
| // shouldn't throw:
|
| - observePath(123, '').changes.listen((_) {}).cancel();
|
| - observePath(null, '').value = null;
|
| - observePath(123, '').value = 42;
|
| - observePath(123, 'foo.bar.baz').value = 42;
|
| + new PathObserver(123, '')..open((_) {})..close();
|
| + new PropertyPath('').setValueFrom(null, null);
|
| + new PropertyPath('').setValueFrom(123, 42);
|
| + new PropertyPath('foo.bar.baz').setValueFrom(123, 42);
|
|
|
| var foo = {};
|
| - expect(observePath(foo, '').value, foo);
|
| + expect(new PathObserver(foo, '').value, foo);
|
|
|
| foo = new Object();
|
| - expect(observePath(foo, '').value, foo);
|
| + expect(new PathObserver(foo, '').value, foo);
|
|
|
| - expect(observePath(foo, 'a/3!').value, null);
|
| + expect(new PathObserver(foo, 'a/3!').value, null);
|
| });
|
|
|
| - observeTest('get value at path ObservableBox', () {
|
| + test('get value at path ObservableBox', () {
|
| var obj = new ObservableBox(new ObservableBox(new ObservableBox(1)));
|
|
|
| - expect(observePath(obj, '').value, obj);
|
| - expect(observePath(obj, 'value').value, obj.value);
|
| - expect(observePath(obj, 'value.value').value, obj.value.value);
|
| - expect(observePath(obj, 'value.value.value').value, 1);
|
| + expect(new PathObserver(obj, '').value, obj);
|
| + expect(new PathObserver(obj, 'value').value, obj.value);
|
| + expect(new PathObserver(obj, 'value.value').value, obj.value.value);
|
| + expect(new PathObserver(obj, 'value.value.value').value, 1);
|
|
|
| obj.value.value.value = 2;
|
| - expect(observePath(obj, 'value.value.value').value, 2);
|
| + expect(new PathObserver(obj, 'value.value.value').value, 2);
|
|
|
| obj.value.value = new ObservableBox(3);
|
| - expect(observePath(obj, 'value.value.value').value, 3);
|
| + expect(new PathObserver(obj, 'value.value.value').value, 3);
|
|
|
| obj.value = new ObservableBox(4);
|
| - expect(observePath(obj, 'value.value.value').value, null);
|
| - expect(observePath(obj, 'value.value').value, 4);
|
| + expect(new PathObserver(obj, 'value.value.value').value, null);
|
| + expect(new PathObserver(obj, 'value.value').value, 4);
|
| });
|
|
|
|
|
| - observeTest('get value at path ObservableMap', () {
|
| + test('get value at path ObservableMap', () {
|
| var obj = toObservable({'a': {'b': {'c': 1}}});
|
|
|
| - expect(observePath(obj, '').value, obj);
|
| - expect(observePath(obj, 'a').value, obj['a']);
|
| - expect(observePath(obj, 'a.b').value, obj['a']['b']);
|
| - expect(observePath(obj, 'a.b.c').value, 1);
|
| + expect(new PathObserver(obj, '').value, obj);
|
| + expect(new PathObserver(obj, 'a').value, obj['a']);
|
| + expect(new PathObserver(obj, 'a.b').value, obj['a']['b']);
|
| + expect(new PathObserver(obj, 'a.b.c').value, 1);
|
|
|
| obj['a']['b']['c'] = 2;
|
| - expect(observePath(obj, 'a.b.c').value, 2);
|
| + expect(new PathObserver(obj, 'a.b.c').value, 2);
|
|
|
| obj['a']['b'] = toObservable({'c': 3});
|
| - expect(observePath(obj, 'a.b.c').value, 3);
|
| + expect(new PathObserver(obj, 'a.b.c').value, 3);
|
|
|
| obj['a'] = toObservable({'b': 4});
|
| - expect(observePath(obj, 'a.b.c').value, null);
|
| - expect(observePath(obj, 'a.b').value, 4);
|
| + expect(new PathObserver(obj, 'a.b.c').value, null);
|
| + expect(new PathObserver(obj, 'a.b').value, 4);
|
| });
|
|
|
| - observeTest('set value at path', () {
|
| + test('set value at path', () {
|
| var obj = toObservable({});
|
| - observePath(obj, 'foo').value = 3;
|
| + new PropertyPath('foo').setValueFrom(obj, 3);
|
| expect(obj['foo'], 3);
|
|
|
| var bar = toObservable({ 'baz': 3 });
|
| - observePath(obj, 'bar').value = bar;
|
| + new PropertyPath('bar').setValueFrom(obj, bar);
|
| expect(obj['bar'], bar);
|
|
|
| - observePath(obj, 'bar.baz.bat').value = 'not here';
|
| - expect(observePath(obj, 'bar.baz.bat').value, null);
|
| + new PropertyPath('bar.baz.bat').setValueFrom(obj, 'not here');
|
| + expect(new PathObserver(obj, 'bar.baz.bat').value, null);
|
| });
|
|
|
| - observeTest('set value back to same', () {
|
| + test('set value back to same', () {
|
| var obj = toObservable({});
|
| - var path = observePath(obj, 'foo');
|
| + var path = new PathObserver(obj, 'foo');
|
| var values = [];
|
| - path.changes.listen((_) { values.add(path.value); });
|
| + path.open((x) {
|
| + expect(x, path.value, reason: 'callback should get current value');
|
| + values.add(x);
|
| + });
|
|
|
| path.value = 3;
|
| expect(obj['foo'], 3);
|
| expect(path.value, 3);
|
|
|
| - observePath(obj, 'foo').value = 2;
|
| - performMicrotaskCheckpoint();
|
| - expect(path.value, 2);
|
| - expect(observePath(obj, 'foo').value, 2);
|
| + new PropertyPath('foo').setValueFrom(obj, 2);
|
| + return new Future(() {
|
| + expect(path.value, 2);
|
| + expect(new PathObserver(obj, 'foo').value, 2);
|
|
|
| - observePath(obj, 'foo').value = 3;
|
| - performMicrotaskCheckpoint();
|
| - expect(path.value, 3);
|
| + new PropertyPath('foo').setValueFrom(obj, 3);
|
| + }).then(newMicrotask).then((_) {
|
| + expect(path.value, 3);
|
|
|
| - performMicrotaskCheckpoint();
|
| - expect(values, [2, 3]);
|
| + }).then(newMicrotask).then((_) {
|
| + expect(values, [2, 3]);
|
| + });
|
| });
|
|
|
| - observeTest('Observe and Unobserve - Paths', () {
|
| + test('Observe and Unobserve - Paths', () {
|
| var arr = toObservable({});
|
|
|
| arr['foo'] = 'bar';
|
| var fooValues = [];
|
| - var fooPath = observePath(arr, 'foo');
|
| - var fooSub = fooPath.changes.listen((_) {
|
| - fooValues.add(fooPath.value);
|
| - });
|
| + var fooPath = new PathObserver(arr, 'foo');
|
| + fooPath.open(fooValues.add);
|
| arr['foo'] = 'baz';
|
| arr['bat'] = 'bag';
|
| var batValues = [];
|
| - var batPath = observePath(arr, 'bat');
|
| - var batSub = batPath.changes.listen((_) {
|
| - batValues.add(batPath.value);
|
| + var batPath = new PathObserver(arr, 'bat');
|
| + batPath.open(batValues.add);
|
| +
|
| + return new Future(() {
|
| + expect(fooValues, ['baz']);
|
| + expect(batValues, []);
|
| +
|
| + arr['foo'] = 'bar';
|
| + fooPath.close();
|
| + arr['bat'] = 'boo';
|
| + batPath.close();
|
| + arr['bat'] = 'boot';
|
| +
|
| + }).then(newMicrotask).then((_) {
|
| + expect(fooValues, ['baz']);
|
| + expect(batValues, []);
|
| });
|
| -
|
| - performMicrotaskCheckpoint();
|
| - expect(fooValues, ['baz']);
|
| - expect(batValues, []);
|
| -
|
| - arr['foo'] = 'bar';
|
| - fooSub.cancel();
|
| - arr['bat'] = 'boo';
|
| - batSub.cancel();
|
| - arr['bat'] = 'boot';
|
| -
|
| - performMicrotaskCheckpoint();
|
| - expect(fooValues, ['baz']);
|
| - expect(batValues, []);
|
| });
|
|
|
| - observeTest('Path Value With Indices', () {
|
| + test('Path Value With Indices', () {
|
| var model = toObservable([]);
|
| - var path = observePath(model, '0');
|
| - path.changes.listen(expectAsync1((_) {
|
| + var path = new PathObserver(model, '0');
|
| + path.open(expectAsync1((x) {
|
| expect(path.value, 123);
|
| + expect(x, 123);
|
| }));
|
| model.add(123);
|
| });
|
|
|
| group('ObservableList', () {
|
| - observeTest('isNotEmpty', () {
|
| + test('isNotEmpty', () {
|
| var model = new ObservableList();
|
| - var path = observePath(model, 'isNotEmpty');
|
| + var path = new PathObserver(model, 'isNotEmpty');
|
| expect(path.value, false);
|
|
|
| - var future = path.changes.first.then((_) {
|
| + path.open(expectAsync1((_) {
|
| expect(path.value, true);
|
| - });
|
| + }));
|
| model.add(123);
|
| -
|
| - return future;
|
| });
|
|
|
| - observeTest('isEmpty', () {
|
| + test('isEmpty', () {
|
| var model = new ObservableList();
|
| - var path = observePath(model, 'isEmpty');
|
| + var path = new PathObserver(model, 'isEmpty');
|
| expect(path.value, true);
|
|
|
| - var future = path.changes.first.then((_) {
|
| + path.open(expectAsync1((_) {
|
| expect(path.value, false);
|
| - });
|
| + }));
|
| model.add(123);
|
| -
|
| - return future;
|
| });
|
| });
|
|
|
| for (var createModel in [() => new TestModel(), () => new WatcherModel()]) {
|
| - observeTest('Path Observation - ${createModel().runtimeType}', () {
|
| + test('Path Observation - ${createModel().runtimeType}', () {
|
| var model = createModel()..a =
|
| (createModel()..b = (createModel()..c = 'hello, world'));
|
|
|
| - var path = observePath(model, 'a.b.c');
|
| + var path = new PathObserver(model, 'a.b.c');
|
| var lastValue = null;
|
| - var sub = path.changes.listen((_) { lastValue = path.value; });
|
| + path.open((x) { lastValue = x; });
|
|
|
| model.a.b.c = 'hello, mom';
|
|
|
| expect(lastValue, null);
|
| - performMicrotaskCheckpoint();
|
| - expect(lastValue, 'hello, mom');
|
| + return new Future(() {
|
| + expect(lastValue, 'hello, mom');
|
|
|
| - model.a.b = createModel()..c = 'hello, dad';
|
| - performMicrotaskCheckpoint();
|
| - expect(lastValue, 'hello, dad');
|
| + model.a.b = createModel()..c = 'hello, dad';
|
| + }).then(newMicrotask).then((_) {
|
| + expect(lastValue, 'hello, dad');
|
|
|
| - model.a = createModel()..b =
|
| - (createModel()..c = 'hello, you');
|
| - performMicrotaskCheckpoint();
|
| - expect(lastValue, 'hello, you');
|
| + model.a = createModel()..b =
|
| + (createModel()..c = 'hello, you');
|
| + }).then(newMicrotask).then((_) {
|
| + expect(lastValue, 'hello, you');
|
|
|
| - model.a.b = 1;
|
| - performMicrotaskCheckpoint();
|
| - expect(lastValue, null);
|
| + model.a.b = 1;
|
| + }).then(newMicrotask).then((_) {
|
| + expect(lastValue, null);
|
|
|
| - // Stop observing
|
| - sub.cancel();
|
| + // Stop observing
|
| + path.close();
|
|
|
| - model.a.b = createModel()..c = 'hello, back again -- but not observing';
|
| - performMicrotaskCheckpoint();
|
| - expect(lastValue, null);
|
| + model.a.b = createModel()..c = 'hello, back again -- but not observing';
|
| + }).then(newMicrotask).then((_) {
|
| + expect(lastValue, null);
|
|
|
| - // Resume observing
|
| - sub = path.changes.listen((_) { lastValue = path.value; });
|
| + // Resume observing
|
| + new PathObserver(model, 'a.b.c').open((x) { lastValue = x; });
|
|
|
| - model.a.b.c = 'hello. Back for reals';
|
| - performMicrotaskCheckpoint();
|
| - expect(lastValue, 'hello. Back for reals');
|
| + model.a.b.c = 'hello. Back for reals';
|
| + }).then(newMicrotask).then((_) {
|
| + expect(lastValue, 'hello. Back for reals');
|
| + });
|
| });
|
| }
|
|
|
| - observeTest('observe map', () {
|
| + test('observe map', () {
|
| var model = toObservable({'a': 1});
|
| - var path = observePath(model, 'a');
|
| + var path = new PathObserver(model, 'a');
|
|
|
| var values = [path.value];
|
| - var sub = path.changes.listen((_) { values.add(path.value); });
|
| + path.open(values.add);
|
| expect(values, [1]);
|
|
|
| model['a'] = 2;
|
| - performMicrotaskCheckpoint();
|
| - expect(values, [1, 2]);
|
| + return new Future(() {
|
| + expect(values, [1, 2]);
|
|
|
| - sub.cancel();
|
| - model['a'] = 3;
|
| - performMicrotaskCheckpoint();
|
| - expect(values, [1, 2]);
|
| + path.close();
|
| + model['a'] = 3;
|
| + }).then(newMicrotask).then((_) {
|
| + expect(values, [1, 2]);
|
| + });
|
| });
|
|
|
| - observeTest('errors thrown from getter/setter', () {
|
| + test('errors thrown from getter/setter', () {
|
| var model = new ObjectWithErrors();
|
| var observer = new PathObserver(model, 'foo');
|
|
|
| @@ -253,7 +307,7 @@ observePathTests() {
|
| expect(model.setFooCalled, [123]);
|
| });
|
|
|
| - observeTest('object with noSuchMethod', () {
|
| + test('object with noSuchMethod', () {
|
| var model = new NoSuchMethodModel();
|
| var observer = new PathObserver(model, 'foo');
|
|
|
| @@ -271,7 +325,7 @@ observePathTests() {
|
| expect(observer.value, null, reason: 'path not found');
|
| });
|
|
|
| - observeTest('object with indexer', () {
|
| + test('object with indexer', () {
|
| var model = new IndexerModel();
|
| var observer = new PathObserver(model, 'foo');
|
|
|
|
|