Skip to content

Commit

Permalink
chore(jasmine_syntax): use the guinness testing library instead of ja…
Browse files Browse the repository at this point in the history
…smine_syntax

Replace the jasmine syntax implemented by jasmine_syntax.dart and _specs.dart with Guinness:
- remove jasmine_syntax.dart
- update _specs.dart to use guinness instead of jasmine_syntax.dart
- change karma config files to initialize guinness
- replace all references to jasmine with guinness

Closes dart-archive#549

Closes dart-archive#994
  • Loading branch information
vsavkin authored and mhevery committed May 7, 2014
1 parent db04ce3 commit 5c6a18e
Show file tree
Hide file tree
Showing 24 changed files with 132 additions and 481 deletions.
2 changes: 0 additions & 2 deletions benchmark/dom/_perf.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import '../../test/_specs.dart' as perf;
import '../../test/jasmine_syntax.dart' as jasmine;
import '../_perf.dart' hide xtime, time;
import 'package:angular/mock/module.dart';

Expand All @@ -17,6 +16,5 @@ time(name, body, {verify:_noop, cleanUp:_noop}) {
}

main() {
jasmine.main();
perf.main();
}
6 changes: 5 additions & 1 deletion karma-perf.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = function(config) {
files: [
'benchmark/dom/*.dart',
'benchmark/*_perf.dart',
'test/config/filter_tests.dart',
'test/config/init_guinness.dart',
{pattern: '**/*.dart', watched: true, included: false, served: true},
'packages/browser/dart.js',
'packages/browser/interop.js'
Expand All @@ -29,6 +29,10 @@ module.exports = function(config) {
'../../../karma-parser-getter-setter'
],

karmaDartImports: {
guinness: 'package:guinness/guinness_html.dart'
},

preprocessors: {
'test/core/parser/generated_getter_setter.dart': ['parser-getter-setter']
},
Expand Down
7 changes: 5 additions & 2 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ module.exports = function(config) {
// all tests must be 'included', but all other libraries must be 'served' and
// optionally 'watched' only.
files: [
'test/jasmine_syntax.dart',
'test/*.dart',
'test/**/*_spec.dart',
'test/config/filter_tests.dart',
'test/config/init_guinness.dart',
{pattern: '**/*.dart', watched: true, included: false, served: true},
'packages/browser/dart.js',
'packages/browser/interop.js',
Expand Down Expand Up @@ -40,6 +39,10 @@ module.exports = function(config) {
'../../../karma-parser-getter-setter'
],

karmaDartImports: {
guinness: 'package:guinness/guinness_html.dart'
},

customLaunchers: {
ChromeNoSandbox: { base: 'Chrome', flags: ['--no-sandbox'] }
},
Expand Down
4 changes: 4 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ packages:
description: di
source: hosted
version: "0.0.40"
guinness:
description: guinness
source: hosted
version: "0.1.1"
html5lib:
description: html5lib
source: hosted
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ dev_dependencies:
benchmark_harness: '>=1.0.0'
unittest: '>=0.10.1 <0.12.0'
mock: '>=0.10.0 <0.12.0'
guinness: '>=0.1.1 <0.2.0'
239 changes: 49 additions & 190 deletions test/_specs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import 'dart:html' hide Animation;

import 'package:angular/angular.dart';
import 'package:angular/mock/module.dart';
import 'package:unittest/unittest.dart' as unit;

import 'jasmine_syntax.dart' as jasmine_syntax;
import 'package:guinness/guinness_html.dart' as gns;

export 'dart:html' hide Animation;
export 'package:unittest/unittest.dart';

export 'package:unittest/unittest.dart' hide expect;
export 'package:guinness/guinness_html.dart';

export 'package:mock/mock.dart';
export 'package:di/di.dart';
export 'package:di/dynamic_injector.dart';
Expand Down Expand Up @@ -37,194 +39,42 @@ es(String html) {

e(String html) => es(html).first;

Expect expect(actual, [unit.Matcher matcher = null]) {
if (matcher != null) unit.expect(actual, matcher);
return new Expect(actual);
}

class Expect {
var actual;
NotExpect not;

Expect(this.actual) {
not = new NotExpect(this);
}

toEqual(expected) => unit.expect(actual, unit.equals(expected));
toContain(expected) => unit.expect(actual, unit.contains(expected));
toBe(expected) => unit.expect(actual,
unit.predicate((actual) => identical(expected, actual), '$expected'));
toThrow([exception]) => unit.expect(actual, exception == null ?
unit.throws:
unit.throwsA(new ExceptionContains(exception)));
toBeFalsy() => unit.expect(actual, _isFalsy,
reason: '"$actual" is not Falsy');
toBeTruthy() => unit.expect(actual, (v) => !_isFalsy(v),
reason: '"$actual" is not Truthy');
toBeDefined() => unit.expect(actual, unit.isNotNull);
toBeNull() => unit.expect(actual, unit.isNull);
toBeNotNull() => unit.expect(actual, unit.isNotNull);

toHaveHtml(expected) => unit.expect(_toHtml(actual), unit.equals(expected));
toHaveText(expected) =>
unit.expect(_elementText(actual), unit.equals(expected));

toHaveBeenCalled() =>
unit.expect(actual.called, true, reason: 'method not called');
toHaveBeenCalledOnce() => unit.expect(actual.count, 1,
reason: 'method invoked ${actual.count} expected once');
toHaveBeenCalledWith([a,b,c,d,e,f]) =>
unit.expect(actual.firstArgsMatch(a,b,c,d,e,f), true,
reason: 'method invoked with correct arguments');
toHaveBeenCalledOnceWith([a,b,c,d,e,f]) =>
unit.expect(actual.count == 1 && actual.firstArgsMatch(a,b,c,d,e,f),
true,
reason: 'method invoked once with correct arguments. '
'(Called ${actual.count} times)');

toHaveClass(cls) => unit.expect(actual.classes.contains(cls), true,
reason: ' Expected ${actual} to have css class ${cls}');

toHaveAttribute(name, [value = null]) {
unit.expect(actual.attributes.containsKey(name), true,
reason: 'Epxected $actual to have attribute $name');
if (value != null) {
unit.expect(actual.attributes[name], value,
reason: 'Epxected $actual attribute "$name" to be "$value"');
}
Expect expect(actual, [matcher]) {
final expect = new Expect(actual);
if (matcher != null) {
expect.to(matcher);
}
return expect;
}

toEqualSelect(options) {
var actualOptions = [];
class Expect extends gns.Expect {
Expect(actual) : super(actual);

for (var option in actual.querySelectorAll('option')) {
actualOptions.add(option.selected ? [option.value] : option.value);
}
return unit.expect(actualOptions, options);
}
NotExpect get not => new NotExpect(actual);

toBeValid() => unit.expect(actual.valid && !actual.invalid, true,
toBeValid() => _expect(actual.valid && !actual.invalid, true,
reason: 'Form is not valid');
toBePristine() =>
unit.expect(actual.pristine && !actual.dirty, true,
reason: 'Form is dirty');

_isFalsy(v) => v == null ? true: v is bool ? v == false : false;

_toHtml(node, [bool outer = false]) {
if (node is Comment) {
return '<!--${node.text}-->';
} else if (node is DocumentFragment) {
var acc = '';
node.childNodes.forEach((n) { acc += _toHtml(n, true); });
return acc;
} else if (node is List) {
var acc = '';
node.forEach((n) { acc += _toHtml(n); });
return acc;
} else if (node is Element) {
// Remove all the "ng-binding" internal classes
node = node.clone(true) as Element;
node.classes.remove('ng-binding');
node.querySelectorAll(".ng-binding").forEach((Element e) {
e.classes.remove('ng-binding');
});
var htmlString = outer ? node.outerHtml : node.innerHtml;
// Strip out empty class attributes. This seems like a Dart bug...
return htmlString.replaceAll(' class=""', '').trim();
} else if (node is Text) {
return node.text;
} else {
throw "JQuery._toHtml not implemented for node type [${node.nodeType}]";
}
}

_elementText(n, [bool notShadow = false]) {
if (n is Iterable) {
return n.map((nn) => _elementText(nn)).join("");
}

if (n is Comment) return '';

if (!notShadow && n is Element && n.shadowRoot != null) {
var cShadows = n.shadowRoot.nodes.map((n) => n.clone(true)).toList();
for (var i = 0, ii = cShadows.length; i < ii; i++) {
var n = cShadows[i];
if (n is Element) {
var updateElement = (e) {
var text = new Text('SHADOW-CONTENT');
if (e.parent == null) {
cShadows[i] = text;
} else {
e.parent.insertBefore(text, e);
}
e.nodes = [];
};
if (n is ContentElement) { updateElement(n); }
n.querySelectorAll('content').forEach(updateElement);
}
};
var shadowText = _elementText(cShadows, true);
var domText = _elementText(n, true);

return shadowText.replaceFirst("SHADOW-CONTENT", domText);
}

if (n.nodes == null || n.nodes.length == 0) return n.text;

return n.nodes.map((cn) => _elementText(cn)).join("");
}
}
toBePristine() => _expect(actual.pristine && !actual.dirty, true,
reason: 'Form is dirty');

class NotExpect {
Expect _expect;
get actual => _expect.actual;

NotExpect(this._expect);

toHaveBeenCalled() =>
unit.expect(actual.called, false, reason: 'method called');
toThrow() => actual();

toHaveClass(cls) => unit.expect(actual.classes.contains(cls), false,
reason: ' Expected ${actual} to not have css class ${cls}');
toHaveAttribute(name) => unit.expect(actual.attributes.containsKey(name),
false, reason: ' Expected $actual to not have attribute "$name"');
toBe(expected) => unit.expect(actual,
unit.predicate((actual) => !identical(expected, actual), 'not $expected'));
toEqual(expected) => unit.expect(actual,
unit.predicate((actual) => expected != actual, 'not $expected'));
toContain(expected) => unit.expect(actual,
unit.predicate((actual) => !actual.contains(expected), 'not $expected'));
toBePristine() => unit.expect(actual.pristine && !actual.dirty, false,
reason: 'Form is pristine');
toBeValid() => unit.expect(actual.valid && !actual.invalid, false,
reason: 'Form is valid');
get _expect => gns.guinness.matchers.expect;
}

class ExceptionContains extends unit.Matcher {

final _expected;

const ExceptionContains(this._expected);
class NotExpect extends gns.NotExpect {
NotExpect(actual) : super(actual);

bool matches(item, Map matchState) {
if (item is String) {
return item.indexOf(_expected) >= 0;
}
return matches('$item', matchState);
}
toBeValid() => _expect(actual.valid && !actual.invalid, false,
reason: 'Form is valid');

unit.Description describe(unit.Description description) =>
description.add('exception contains ').addDescriptionOf(_expected);
toBePristine() => _expect(actual.pristine && !actual.dirty, false,
reason: 'Form is pristine');

unit.Description describeMismatch(item, unit.Description mismatchDescription,
Map matchState, bool verbose) {
return super.describeMismatch('$item', mismatchDescription, matchState,
verbose);
}
get _expect => gns.guinness.matchers.expect;
}


_injectify(fn) {
// The function does two things:
// First: if the it() passed a function, we wrap it in
Expand All @@ -236,20 +86,29 @@ _injectify(fn) {
return fn.outer(inject(fn.inner));
}

// Jasmine syntax
beforeEachModule(fn) => jasmine_syntax.beforeEach(module(fn), priority:1);
beforeEach(fn) => jasmine_syntax.beforeEach(_injectify(fn));
afterEach(fn) => jasmine_syntax.afterEach(_injectify(fn));
it(name, fn) => jasmine_syntax.it(name, _injectify(fn));
iit(name, fn) => jasmine_syntax.iit(name, _injectify(fn));
xit(name, fn) => jasmine_syntax.xit(name, fn);
xdescribe(name, fn) => jasmine_syntax.xdescribe(name, fn);
ddescribe(name, fn) => jasmine_syntax.ddescribe(name, fn);
describe(name, fn) => jasmine_syntax.describe(name, fn);

var jasmine = jasmine_syntax.jasmine;
// Replace guinness syntax elements to inject dependencies.
beforeEachModule(fn) => gns.beforeEach(module(fn), priority:1);
beforeEach(fn) => gns.beforeEach(_injectify(fn));
afterEach(fn) => gns.afterEach(_injectify(fn));
it(name, fn) => gns.it(name, _injectify(fn));
iit(name, fn) => gns.iit(name, _injectify(fn));

_removeNgBinding(node) {
if (node is Element) {
node = node.clone(true) as Element;
node.classes.remove('ng-binding');
node.querySelectorAll(".ng-binding").forEach((Element e) {
e.classes.remove('ng-binding');
});
return node;
}
return node;
}

main() {
jasmine_syntax.beforeEach(setUpInjector, priority:3);
jasmine_syntax.afterEach(tearDownInjector);
gns.beforeEach(setUpInjector, priority:3);
gns.afterEach(tearDownInjector);

gns.guinnessEnableHtmlMatchers();
gns.guinness.matchers.config.preprocessHtml = _removeNgBinding;
}
15 changes: 0 additions & 15 deletions test/config/filter_tests.dart

This file was deleted.

8 changes: 8 additions & 0 deletions test/config/init_guinness.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'package:guinness/guinness.dart';
import 'package:unittest/unittest.dart' as unit;

main() {
unit.filterStacks = true;
unit.formatStacks = false;
guinness.initSpecs();
}
4 changes: 2 additions & 2 deletions test/core/scope_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,7 @@ void main() {

it(r'should return a function that allows listeners to be unregistered', inject(
(RootScope rootScope) {
var listener = jasmine.createSpy('watch listener');
var listener = guinness.createSpy('watch listener');
var watch;

watch = rootScope.watch('foo', listener);
Expand Down Expand Up @@ -1248,7 +1248,7 @@ void main() {
..watch('numberValue', logger)
..digest();

expect(log.removeAt(0).isNaN).toEqual(true); //jasmine's toBe and toEqual don't work well with NaNs
expect(log.removeAt(0).isNaN).toEqual(true); //guinness's toBe and toEqual don't work well with NaNs
expect(log).toEqual([null, '', false, 23]);
log = [];
rootScope.digest();
Expand Down
Loading

0 comments on commit 5c6a18e

Please sign in to comment.