aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorIgor Minar2011-01-25 20:44:44 -0800
committerIgor Minar2011-01-26 15:46:05 -0800
commitf5d08963b0c836b10133a94d03a81254242661eb (patch)
tree14c525d0aa22193e5b1a1f8aa791b96608dc83b2 /test
parent7a48ee6aa949e8d4338033de8a0c6a079ceb17a3 (diff)
downloadangular.js-f5d08963b0c836b10133a94d03a81254242661eb.tar.bz2
split mocks and create $log and $exceptionHandler mocks
- split mocks between angular-mocks.js and mocks.js - src/angular-mocks.js now contains only mocks that we want to ship - test/mocks.js contains mocks that we use internally for testing angular - created angular.mock namespace - created public $exceptionHandler mock rethrows errors - created public $log mock stores all logs messages in an array that can be accessed to make assertions - internally we now have factory to create $exceptionHandler that we can assert on - internally we also keep track of all messages logged and fail tests if messages were not expected and cleaned up (checked via global beforeEach and afterEach) - updated RakeFile and docs reader.js to point to the new angular-mocks.js location - made real $exceptionHandler and $log factories accessible from tests and simplified their specs - fixed typos in several spec descriptions - added log assertions throughout the test suite
Diffstat (limited to 'test')
-rw-r--r--test/BinderSpec.js16
-rw-r--r--test/ScopeSpec.js24
-rw-r--r--test/angular-mocks.js320
-rw-r--r--test/servicesSpec.js41
-rw-r--r--test/testabilityPatch.js50
-rw-r--r--test/widgetsSpec.js17
6 files changed, 116 insertions, 352 deletions
diff --git a/test/BinderSpec.js b/test/BinderSpec.js
index f12c1a74..73650bd6 100644
--- a/test/BinderSpec.js
+++ b/test/BinderSpec.js
@@ -273,6 +273,7 @@ describe('Binder', function(){
it('IfTextBindingThrowsErrorDecorateTheSpan', function(){
var a = this.compile('<div>{{error.throw()}}</div>');
var doc = a.node;
+ var errorLogs = a.scope.$service('$log').error.logs;
a.scope.$set('error.throw', function(){throw "ErrorMsg1";});
a.scope.$eval();
@@ -280,6 +281,7 @@ describe('Binder', function(){
assertTrue(span.hasClass('ng-exception'));
assertTrue(!!span.text().match(/ErrorMsg1/));
assertTrue(!!span.attr('ng-exception').match(/ErrorMsg1/));
+ assertEquals(['ErrorMsg1'], errorLogs.shift());
a.scope.$set('error.throw', function(){throw "MyError";});
a.scope.$eval();
@@ -287,30 +289,34 @@ describe('Binder', function(){
assertTrue(span.hasClass('ng-exception'));
assertTrue(span.text(), span.text().match('MyError') !== null);
assertEquals('MyError', span.attr('ng-exception'));
+ assertEquals(['MyError'], errorLogs.shift());
a.scope.$set('error.throw', function(){return "ok";});
a.scope.$eval();
assertFalse(span.hasClass('ng-exception'));
assertEquals('ok', span.text());
assertEquals(null, span.attr('ng-exception'));
+ assertEquals(0, errorLogs.length);
});
it('IfAttrBindingThrowsErrorDecorateTheAttribute', function(){
var a = this.compile('<div attr="before {{error.throw()}} after"></div>');
var doc = a.node;
+ var errorLogs = a.scope.$service('$log').error.logs;
a.scope.$set('error.throw', function(){throw "ErrorMsg";});
a.scope.$eval();
assertTrue('ng-exception', doc.hasClass('ng-exception'));
assertEquals('"ErrorMsg"', doc.attr('ng-exception'));
assertEquals('before "ErrorMsg" after', doc.attr('attr'));
+ assertEquals(['ErrorMsg'], errorLogs.shift());
a.scope.$set('error.throw', function(){ return 'X';});
a.scope.$eval();
assertFalse('!ng-exception', doc.hasClass('ng-exception'));
assertEquals('before X after', doc.attr('attr'));
assertEquals(null, doc.attr('ng-exception'));
-
+ assertEquals(0, errorLogs.length);
});
it('NestedRepeater', function(){
@@ -447,6 +453,7 @@ describe('Binder', function(){
var error = input.attr('ng-exception');
assertTrue(!!error.match(/MyError/));
assertTrue("should have an error class", input.hasClass('ng-exception'));
+ assertTrue(!!c.scope.$service('$log').error.logs.shift()[0].message.match(/MyError/));
// TODO: I think that exception should never get cleared so this portion of test makes no sense
//c.scope.action = noop;
@@ -491,7 +498,7 @@ describe('Binder', function(){
it('FillInOptionValueWhenMissing', function(){
var c = this.compile(
- '<select><option selected="true">{{a}}</option><option value="">{{b}}</option><option>C</option></select>');
+ '<select name="foo"><option selected="true">{{a}}</option><option value="">{{b}}</option><option>C</option></select>');
c.scope.$set('a', 'A');
c.scope.$set('b', 'B');
c.scope.$eval();
@@ -569,18 +576,21 @@ describe('Binder', function(){
assertChild(5, false);
});
- it('ItShouldDisplayErrorWhenActionIsSyntacticlyIncorect', function(){
+ it('ItShouldDisplayErrorWhenActionIsSyntacticlyIncorrect', function(){
var c = this.compile('<div>' +
'<input type="button" ng:click="greeting=\'ABC\'"/>' +
'<input type="button" ng:click=":garbage:"/></div>');
var first = jqLite(c.node[0].childNodes[0]);
var second = jqLite(c.node[0].childNodes[1]);
+ var errorLogs = c.scope.$service('$log').error.logs;
browserTrigger(first, 'click');
assertEquals("ABC", c.scope.greeting);
+ expect(errorLogs).toEqual([]);
browserTrigger(second, 'click');
assertTrue(second.hasClass("ng-exception"));
+ expect(errorLogs.shift()[0]).toMatchError(/Parse Error: Token ':' not a primary expression/);
});
it('ItShouldSelectTheCorrectRadioBox', function(){
diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js
index 8984f605..ab1ba124 100644
--- a/test/ScopeSpec.js
+++ b/test/ScopeSpec.js
@@ -146,30 +146,34 @@ describe('scope/model', function(){
});
describe('$tryEval', function(){
- it('should report error on element', function(){
- var scope = createScope();
+ it('should report error using the provided error handler and $log.error', function(){
+ var scope = createScope(),
+ errorLogs = scope.$service('$log').error.logs;
+
scope.$tryEval(function(){throw "myError";}, function(error){
scope.error = error;
});
expect(scope.error).toEqual('myError');
+ expect(errorLogs.shift()[0]).toBe("myError");
});
it('should report error on visible element', function(){
- var element = jqLite('<div></div>');
- var scope = createScope();
+ var element = jqLite('<div></div>'),
+ scope = createScope(),
+ errorLogs = scope.$service('$log').error.logs;
+
scope.$tryEval(function(){throw "myError";}, element);
expect(element.attr('ng-exception')).toEqual('myError');
expect(element.hasClass('ng-exception')).toBeTruthy();
+ expect(errorLogs.shift()[0]).toBe("myError");
});
it('should report error on $excetionHandler', function(){
- var errors = [],
- errorLogs = [],
- scope = createScope(null, {}, {$exceptionHandler: function(e) {errors.push(e)},
- $log: {error: function(e) {errorLogs.push(e)}}});
+ var scope = createScope(null, {$exceptionHandler: $exceptionHandlerMockFactory},
+ {$log: $logMock});
scope.$tryEval(function(){throw "myError";});
- expect(errors).toEqual(["myError"]);
- expect(errorLogs).toEqual(["myError"]);
+ expect(scope.$service('$exceptionHandler').errors.shift()).toEqual("myError");
+ expect(scope.$service('$log').error.logs.shift()).toEqual(["myError"]);
});
});
diff --git a/test/angular-mocks.js b/test/angular-mocks.js
deleted file mode 100644
index e601d9ab..00000000
--- a/test/angular-mocks.js
+++ /dev/null
@@ -1,320 +0,0 @@
-/**
- * The MIT License
- *
- * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-/*
-
- NUGGGGGH MUST TONGUE WANGS
- \
- .....
- C C /
- /< /
- ___ __________/_#__=o
- /(- /(\_\________ \
- \ ) \ )_ \o \
- /|\ /|\ |' |
- | _|
- /o __\
- / ' |
- / / |
- /_/\______|
- ( _( <
- \ \ \
- \ \ |
- \____\____\
- ____\_\__\_\
- /` /` o\
- |___ |_______|.. . b'ger
-
-
- IN THE FINAL BUILD THIS FILE DOESN'T HAVE DIRECT ACCESS TO GLOBAL FUNCTIONS
- DEFINED IN Angular.js YOU *MUST* REFER TO THEM VIA angular OBJECT
- (e.g. angular.forEach(...)) AND MAKE SURE THAT THE GIVEN FUNCTION IS EXPORTED
- TO THE angular NAMESPACE in AngularPublic.js
-
- */
-
-
-function MockBrowser() {
- var self = this,
- expectations = {},
- requests = [];
-
- this.isMock = true;
- self.url = "http://server";
- self.lastUrl = self.url; // used by url polling fn
- self.pollFns = [];
-
-
- // register url polling fn
-
- self.onHashChange = function(listener) {
- self.pollFns.push(
- function() {
- if (self.lastUrl != self.url) {
- listener();
- }
- }
- );
-
- return listener;
- };
-
-
- self.xhr = function(method, url, data, callback) {
- if (angular.isFunction(data)) {
- callback = data;
- data = null;
- }
- if (data && angular.isObject(data)) data = angular.toJson(data);
- if (data && angular.isString(data)) url += "|" + data;
- var expect = expectations[method] || {};
- var response = expect[url];
- if (!response) {
- throw {
- message: "Unexpected request for method '" + method + "' and url '" + url + "'.",
- name: "Unexpected Request"
- };
- }
- requests.push(function(){
- callback(response.code, response.response);
- });
- };
- self.xhr.expectations = expectations;
- self.xhr.requests = requests;
- self.xhr.expect = function(method, url, data) {
- if (data && angular.isObject(data)) data = angular.toJson(data);
- if (data && angular.isString(data)) url += "|" + data;
- var expect = expectations[method] || (expectations[method] = {});
- return {
- respond: function(code, response) {
- if (!angular.isNumber(code)) {
- response = code;
- code = 200;
- }
- expect[url] = {code:code, response:response};
- }
- };
- };
- self.xhr.expectGET = angular.bind(self, self.xhr.expect, 'GET');
- self.xhr.expectPOST = angular.bind(self, self.xhr.expect, 'POST');
- self.xhr.expectDELETE = angular.bind(self, self.xhr.expect, 'DELETE');
- self.xhr.expectPUT = angular.bind(self, self.xhr.expect, 'PUT');
- self.xhr.expectJSON = angular.bind(self, self.xhr.expect, 'JSON');
- self.xhr.flush = function() {
- while(requests.length) {
- requests.pop()();
- }
- };
-
- self.cookieHash = {};
- self.lastCookieHash = {};
- self.deferredFns = [];
-
- self.defer = function(fn) {
- self.deferredFns.push(fn);
- };
-
- self.defer.flush = function() {
- while (self.deferredFns.length) self.deferredFns.shift()();
- };
-}
-MockBrowser.prototype = {
-
- poll: function poll(){
- angular.forEach(this.pollFns, function(pollFn){
- pollFn();
- });
- },
-
- addPollFn: function(pollFn) {
- this.pollFns.push(pollFn);
- return pollFn;
- },
-
- hover: function(onHover) {
- },
-
- getUrl: function(){
- return this.url;
- },
-
- setUrl: function(url){
- this.url = url;
- },
-
- cookies: function(name, value) {
- if (name) {
- if (value == undefined) {
- delete this.cookieHash[name];
- } else {
- if (angular.isString(value) && //strings only
- value.length <= 4096) { //strict cookie storage limits
- this.cookieHash[name] = value;
- }
- }
- } else {
- if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
- this.lastCookieHash = angular.copy(this.cookieHash);
- this.cookieHash = angular.copy(this.cookieHash);
- }
- return this.cookieHash;
- }
- }
-};
-
-angular.service('$browser', function(){
- return new MockBrowser();
-});
-
-
-/**
- * Mock of the Date type which has its timezone specified via constroctor arg.
- *
- * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
- * offset, so that we can test code that depends on local timezone settings without dependency on
- * the time zone settings of the machine where the code is running.
- *
- * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
- * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
- *
- * @example
- * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
- * newYearInBratislava.getTimezoneOffset() => -60;
- * newYearInBratislava.getFullYear() => 2010;
- * newYearInBratislava.getMonth() => 0;
- * newYearInBratislava.getDate() => 1;
- * newYearInBratislava.getHours() => 0;
- * newYearInBratislava.getMinutes() => 0;
- *
- *
- * !!!! WARNING !!!!!
- * This is not a complete Date object so only methods that were implemented can be called safely.
- * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
- *
- * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
- * incomplete we might be missing some non-standard methods. This can result in errors like:
- * "Date.prototype.foo called on incompatible Object".
- */
-function TzDate(offset, timestamp) {
- if (angular.isString(timestamp)) {
- var tsStr = timestamp;
-
- this.origDate = angular.String.toDate(timestamp);
-
- timestamp = this.origDate.getTime();
- if (isNaN(timestamp))
- throw {
- name: "Illegal Argument",
- message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
- };
- } else {
- this.origDate = new Date(timestamp);
- }
-
- var localOffset = new Date(timestamp).getTimezoneOffset();
- this.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
- this.date = new Date(timestamp + this.offsetDiff);
-
- this.getTime = function() {
- return this.date.getTime() - this.offsetDiff;
- };
-
- this.toLocaleDateString = function() {
- return this.date.toLocaleDateString();
- };
-
- this.getFullYear = function() {
- return this.date.getFullYear();
- };
-
- this.getMonth = function() {
- return this.date.getMonth();
- };
-
- this.getDate = function() {
- return this.date.getDate();
- };
-
- this.getHours = function() {
- return this.date.getHours();
- };
-
- this.getMinutes = function() {
- return this.date.getMinutes();
- };
-
- this.getSeconds = function() {
- return this.date.getSeconds();
- };
-
- this.getTimezoneOffset = function() {
- return offset * 60;
- };
-
- this.getUTCFullYear = function() {
- return this.origDate.getUTCFullYear();
- };
-
- this.getUTCMonth = function() {
- return this.origDate.getUTCMonth();
- };
-
- this.getUTCDate = function() {
- return this.origDate.getUTCDate();
- };
-
- this.getUTCHours = function() {
- return this.origDate.getUTCHours();
- };
-
- this.getUTCMinutes = function() {
- return this.origDate.getUTCMinutes();
- };
-
- this.getUTCSeconds = function() {
- return this.origDate.getUTCSeconds();
- };
-
-
- //hide all methods not implemented in this mock that the Date prototype exposes
- var unimplementedMethods = ['getDay', 'getMilliseconds', 'getTime', 'getUTCDay',
- 'getUTCMilliseconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
- 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
- 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
- 'setYear', 'toDateString', 'toJSON', 'toGMTString', 'toLocaleFormat', 'toLocaleString',
- 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
-
- angular.forEach(unimplementedMethods, function(methodName) {
- this[methodName] = function() {
- throw {
- name: "MethodNotImplemented",
- message: "Method '" + methodName + "' is not implemented in the TzDate mock"
- };
- };
- });
-}
-
-//make "tzDateInstance instanceof Date" return true
-TzDate.prototype = Date.prototype;
diff --git a/test/servicesSpec.js b/test/servicesSpec.js
index e9b16621..c3c10552 100644
--- a/test/servicesSpec.js
+++ b/test/servicesSpec.js
@@ -31,8 +31,14 @@ describe("service", function(){
function warn(){ logger+= 'warn;'; }
function info(){ logger+= 'info;'; }
function error(){ logger+= 'error;'; }
- var scope = createScope({}, angularService, {$window: {console:{log:log, warn:warn, info:info, error:error}}, $document:[{cookie:''}]}),
+ var scope = createScope({}, {$log: $logFactory},
+ {$exceptionHandler: rethrow,
+ $window: {console: {log: log,
+ warn: warn,
+ info: info,
+ error: error}}}),
$log = scope.$service('$log');
+
$log.log();
$log.warn();
$log.info();
@@ -40,10 +46,12 @@ describe("service", function(){
expect(logger).toEqual('log;warn;info;error;');
});
- it('should use console.log if other not present', function(){
+ it('should use console.log() if other not present', function(){
var logger = "";
function log(){ logger+= 'log;'; }
- var scope = createScope({}, angularService, {$window: {console:{log:log}}, $document:[{cookie:''}]});
+ var scope = createScope({}, {$log: $logFactory},
+ {$window: {console:{log:log}},
+ $exceptionHandler: rethrow});
var $log = scope.$service('$log');
$log.log();
$log.warn();
@@ -53,7 +61,9 @@ describe("service", function(){
});
it('should use noop if no console', function(){
- var scope = createScope({}, angularService, {$window: {}, $document:[{cookie:''}]}),
+ var scope = createScope({}, {$log: $logFactory},
+ {$window: {},
+ $exceptionHandler: rethrow}),
$log = scope.$service('$log');
$log.log();
$log.warn();
@@ -61,8 +71,8 @@ describe("service", function(){
$log.error();
});
- describe('Error', function(){
- var e, $log, $console, errorArgs;
+ describe('error', function(){
+ var e, $log, errorArgs;
beforeEach(function(){
e = new Error('');
e.message = undefined;
@@ -70,19 +80,19 @@ describe("service", function(){
e.line = undefined;
e.stack = undefined;
- $console = angular.service('$log')({console:{error:function(){
+ $log = $logFactory({console:{error:function(){
errorArgs = arguments;
}}});
});
it('should pass error if does not have trace', function(){
- $console.error('abc', e);
+ $log.error('abc', e);
expect(errorArgs).toEqual(['abc', e]);
});
it('should print stack', function(){
e.stack = 'stack';
- $console.error('abc', e);
+ $log.error('abc', e);
expect(errorArgs).toEqual(['abc', 'stack']);
});
@@ -90,7 +100,7 @@ describe("service", function(){
e.message = 'message';
e.sourceURL = 'sourceURL';
e.line = '123';
- $console.error('abc', e);
+ $log.error('abc', e);
expect(errorArgs).toEqual(['abc', 'message\nsourceURL:123']);
});
@@ -100,10 +110,13 @@ describe("service", function(){
describe("$exceptionHandler", function(){
it('should log errors', function(){
- var error = '';
- $log.error = function(m) { error += m; };
- scope.$service('$exceptionHandler')('myError');
- expect(error).toEqual('myError');
+ var scope = createScope({}, {$exceptionHandler: $exceptionHandlerFactory},
+ {$log: $logMock}),
+ $log = scope.$service('$log'),
+ $exceptionHandler = scope.$service('$exceptionHandler');
+
+ $exceptionHandler('myError');
+ expect($log.error.logs.shift()).toEqual(['myError']);
});
});
diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js
index 7029b213..78cb767f 100644
--- a/test/testabilityPatch.js
+++ b/test/testabilityPatch.js
@@ -59,11 +59,59 @@ beforeEach(function(){
return this.actual.hasClass ?
this.actual.hasClass(clazz) :
jqLite(this.actual).hasClass(clazz);
+ },
+
+ toEqualError: function(message) {
+ this.message = function() {
+ var expected;
+ if (this.actual.message && this.actual.name == 'Error') {
+ expected = toJson(this.actual.message);
+ } else {
+ expected = toJson(this.actual);
+ }
+ return "Expected " + expected + " to be an Error with message " + toJson(message);
+ }
+ return this.actual.name == 'Error' && this.actual.message == message;
+ },
+
+ toMatchError: function(messageRegexp) {
+ this.message = function() {
+ var expected;
+ if (this.actual.message && this.actual.name == 'Error') {
+ expected = toJson(this.actual.message);
+ } else {
+ expected = toJson(this.actual);
+ }
+ return "Expected " + expected + " to match an Error with message " + toJson(messageRegexp);
+ }
+ return this.actual.name == 'Error' && messageRegexp.test(this.actual.message);
}
});
+
+ $logMock.log.logs = [];
+ $logMock.warn.logs = [];
+ $logMock.info.logs = [];
+ $logMock.error.logs = [];
});
-afterEach(clearJqCache);
+afterEach(function() {
+ // check $log mock
+ forEach(['error', 'warn', 'info', 'log'], function(logLevel) {
+ if ($logMock[logLevel].logs.length) {
+ forEach($logMock[logLevel].logs, function(log) {
+ forEach(log, function deleteStack(logItem) {
+ if (logItem instanceof Error) delete logItem.stack;
+ });
+ });
+
+ throw new Error("Exprected $log." + logLevel + ".logs array to be empty. " +
+ "Either a message was logged unexpectedly, or an expected log message was not checked " +
+ "and removed. Array contents: " + toJson($logMock[logLevel].logs));
+ }
+ });
+
+ clearJqCache();
+});
function clearJqCache(){
var count = 0;
diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js
index 2812614f..ee339e89 100644
--- a/test/widgetsSpec.js
+++ b/test/widgetsSpec.js
@@ -546,12 +546,16 @@ describe("widget", function(){
it('should report error on assignment error', function(){
compile('<input type="text" name="throw \'\'" value="x"/>');
expect(element.hasClass('ng-exception')).toBeTruthy();
+ expect(scope.$service('$log').error.logs.shift()[0]).
+ toMatchError(/Parse Error: Token '''' is extra token not part of expression/);
});
it('should report error on ng:change exception', function(){
compile('<button ng:change="a-2=x">click</button>');
browserTrigger(element);
expect(element.hasClass('ng-exception')).toBeTruthy();
+ expect(scope.$service('$log').error.logs.shift()[0]).
+ toMatchError(/Parse Error: Token '=' implies assignment but \[a-2\] can not be assigned to/);
});
});
@@ -750,10 +754,15 @@ describe("widget", function(){
it('should error on wrong parsing of ng:repeat', function(){
var scope = compile('<ul><li ng:repeat="i dont parse"></li></ul>');
- var log = "";
- log += element.attr('ng-exception') + ';';
- log += element.hasClass('ng-exception') + ';';
- expect(log).toMatch(/Expected ng:repeat in form of 'item in collection' but got 'i dont parse'./);
+
+ expect(scope.$service('$log').error.logs.shift()[0]).
+ toEqualError("Expected ng:repeat in form of 'item in collection' but got 'i dont parse'.");
+
+ expect(scope.$element.attr('ng-exception')).
+ toMatch(/Expected ng:repeat in form of 'item in collection' but got 'i dont parse'/);
+ expect(scope.$element).toHaveClass('ng-exception');
+
+ dealoc(scope);
});
it('should expose iterator offset as $index when iterating over arrays', function() {