From 843bd355d25ebf2369aec79f98cb6704d38497e9 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 9 Apr 2010 16:20:15 -0700 Subject: various bug fixes --- angular-debug.js | 86 +- example/temp.html | 9 + jsTestDriver.conf | 4 +- lib/jasmine-jstd-adapter/JasmineAdapter.js | 181 +-- lib/jasmine/jasmine-0.10.1.js | 2276 --------------------------- lib/jasmine/jasmine-0.10.3.js | 2331 ++++++++++++++++++++++++++++ lib/jstestdriver/JsTestDriver.jar | Bin 3133666 -> 3133701 bytes src/Angular.js | 2 +- src/Compiler.js | 14 +- src/Scope.js | 5 +- src/angular-bootstrap.js | 2 +- src/apis.js | 4 +- src/services.js | 26 +- src/widgets.js | 35 +- test/ApiTest.js | 4 + test/ValidatorsTest.js | 2 +- test/markupSpec.js | 5 + test/servicesSpec.js | 14 + test/widgetsSpec.js | 9 +- 19 files changed, 2574 insertions(+), 2435 deletions(-) create mode 100644 example/temp.html delete mode 100644 lib/jasmine/jasmine-0.10.1.js create mode 100644 lib/jasmine/jasmine-0.10.3.js diff --git a/angular-debug.js b/angular-debug.js index c3f419f7..dd74428a 100644 --- a/angular-debug.js +++ b/angular-debug.js @@ -343,7 +343,7 @@ function merge(src, dst) { } function compile(element, parentScope, overrides) { - var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget); + var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget), $element = jqLite(element), parent = extend({}, parentScope); parent.$element = $element; @@ -641,8 +641,8 @@ Compiler.prototype = { }; function eachTextNode(element, fn){ - var i, chldNodes = element[0].childNodes || [], size = chldNodes.length, chld; - for (i = 0; i < size; i++) { + var i, chldNodes = element[0].childNodes || [], chld; + for (i = 0; i < chldNodes.length; i++) { if(isTextNode(chld = chldNodes[i])) { fn(jqLite(chld), i); } @@ -650,8 +650,8 @@ function eachTextNode(element, fn){ } function eachNode(element, fn){ - var i, chldNodes = element[0].childNodes || [], size = chldNodes.length, chld; - for (i = 0; i < size; i++) { + var i, chldNodes = element[0].childNodes || [], chld; + for (i = 0; i < chldNodes.length; i++) { if(!isTextNode(chld = chldNodes[i])) { fn(jqLite(chld), i); } @@ -659,12 +659,12 @@ function eachNode(element, fn){ } function eachAttribute(element, fn){ - var i, attrs = element[0].attributes || [], size = attrs.length, chld, attr, attrValue = {}; - for (i = 0; i < size; i++) { + var i, attrs = element[0].attributes || [], chld, attr, attrValue = {}; + for (i = 0; i < attrs.length; i++) { attr = attrs[i]; attrValue[attr.name] = attr.value; } - foreach(attrValue, fn); + foreachSorted(attrValue, fn); } function getter(instance, path, unboundFn) { @@ -848,7 +848,10 @@ function createScope(parent, services, existing) { } foreach(services, function(_, name){ - instance[name] = inject(name); + var service = inject(name); + if (service) { + instance[name] = service; + } }); return instance; @@ -2026,7 +2029,9 @@ var angularGlobal = { var angularCollection = { 'size': size }; -var angularObject = {}; +var angularObject = { + 'extend': extend +}; var angularArray = { 'indexOf': indexOf, 'include': includes, @@ -3135,9 +3140,11 @@ function valueAccessor(scope, element) { required = required || required === ''; if (!validator) throw "Validator named '" + validatorName + "' not found."; function validate(value) { - var error = required && !trim(value) ? - "Required" : - validator({state:scope, scope:{get:scope.$get, set:scope.$set}}, value); + var error, + validateScope = extend(new (extend(function(){}, {prototype:scope}))(), {$element:element}); + error = required && !trim(value) ? + "Required" : + validator({state:validateScope, scope:{get:validateScope.$get, set:validateScope.$set}}, value); if (error !== lastError) { elementError(element, NG_VALIDATION_ERROR, error); lastError = error; @@ -3298,7 +3305,8 @@ angularWidget('NG:INCLUDE', function(element){ angularWidget('NG:SWITCH', function ngSwitch(element){ var compiler = this, watchExpr = element.attr("on"), - whenFn = ngSwitch[element.attr("using") || 'equals']; + whenExpr = (element.attr("using") || 'equals').split(":"); + whenFn = ngSwitch[whenExpr.shift()]; changeExpr = element.attr('change') || '', cases = []; if (!whenFn) throw "Using expression '" + usingExpr + "' unknown."; @@ -3307,7 +3315,11 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ if (when) { cases.push({ when: function(scope, value){ - return whenFn.call(scope, value, when); + var args = [value, when]; + foreach(whenExpr, function(arg){ + args.push(arg); + }); + return whenFn.apply(scope, args); }, change: changeExpr, element: caseElement, @@ -3320,13 +3332,10 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ var scope = this, childScope; this.$watch(watchExpr, function(value){ element.html(''); - childScope = null; - var params = {}; + childScope = createScope(scope); foreach(cases, function(switchCase){ - if (switchCase.when(params, value)) { + if (switchCase.when(childScope, value)) { element.append(switchCase.element); - childScope = createScope(scope); - extend(childScope, params); childScope.$tryEval(switchCase.change, element); switchCase.template(switchCase.element, childScope); childScope.$init(); @@ -3341,13 +3350,15 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ equals: function(on, when) { return on == when; }, - route: function(on, when) { - var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$', params = [], self = this; + route: function(on, when, dstName) { + var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$', + params = [], + dst = {}; foreach(when.split(/\W/), function(param){ if (param) { var paramRegExp = new RegExp(":" + param + "([\\W])"); if (regex.match(paramRegExp)) { - regex = regex.replace(paramRegExp, "(.*)$1"); + regex = regex.replace(paramRegExp, "([^\/]*)$1"); params.push(param); } } @@ -3355,8 +3366,9 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ var match = on.match(new RegExp(regex)); if (match) { foreach(params, function(name, index){ - self[name] = match[index + 1]; + dst[name] = match[index + 1]; }); + if (dstName) this.$set(dstName, dst); } return match; } @@ -3370,6 +3382,7 @@ var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.]*)(:([0-9]+)) var DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21}; angularService("$location", function(browser){ var scope = this, location = {parse:parse, toString:toString}; + var lastHash; function parse(url){ if (isDefined(url)) { var match = URL_MATCH.exec(url); @@ -3381,16 +3394,25 @@ angularService("$location", function(browser){ location.path = match[6]; location.search = parseKeyValue(match[8]); location.hash = match[9]; - if (location.hash) location.hash = location.hash.substr(1); + if (location.hash) + location.hash = location.hash.substr(1); + lastHash = location.hash; location.hashPath = match[11] || ''; location.hashSearch = parseKeyValue(match[13]); } } } function toString() { - var hashKeyValue = toKeyValue(location.hashSearch), - hash = (location.hashPath ? location.hashPath : '') + (hashKeyValue ? '?' + hashKeyValue : ''); - return location.href.split('#')[0] + '#' + (hash ? hash : ''); + if (lastHash === location.hash) { + var hashKeyValue = toKeyValue(location.hashSearch), + hash = (location.hashPath ? location.hashPath : '') + (hashKeyValue ? '?' + hashKeyValue : ''), + url = location.href.split('#')[0] + '#' + (hash ? hash : ''); + if (url !== location.href) parse(url); + return url; + } else { + parse(location.href.split('#')[0] + '#' + location.hash); + return toString(); + } } browser.watchUrl(function(url){ parse(url); @@ -3398,11 +3420,7 @@ angularService("$location", function(browser){ }); parse(browser.getUrl()); this.$onEval(PRIORITY_LAST, function(){ - var href = toString(); - if (href != location.href) { - browser.setUrl(href); - location.href = href; - } + browser.setUrl(toString()); }); return location; }, {inject: ['$browser']}); @@ -3432,6 +3450,7 @@ angularService("$hover", function(browser) { tooltip.arrow.addClass('ng-arrow-right'); tooltip.arrow.css({left: (width + 1)+'px'}); tooltip.callout.css({ + position: 'fixed', left: (elementRect.left - arrowWidth - width - 4) + "px", top: (elementRect.top - 3) + "px", width: width + "px" @@ -3439,6 +3458,7 @@ angularService("$hover", function(browser) { } else { tooltip.arrow.addClass('ng-arrow-left'); tooltip.callout.css({ + position: 'fixed', left: (elementRect.right + arrowWidth) + "px", top: (elementRect.top - 3) + "px", width: width + "px" diff --git a/example/temp.html b/example/temp.html new file mode 100644 index 00000000..3580249d --- /dev/null +++ b/example/temp.html @@ -0,0 +1,9 @@ + + +
+ + + + {{'first'}}jasmine.undefined instead of undefined, since undefined 0;
-};
-
-/**
- * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
- *
- * @example
- * // don't care about which function is passed in, as long as it's a function
- * expect(mySpy).wasCalledWith(jasmine.any(Function));
- *
- * @param {Class} clazz
- * @returns matchable object of the type clazz
- */
-jasmine.any = function(clazz) {
- return new jasmine.Matchers.Any(clazz);
-};
-
-/**
- * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
- *
- * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
- * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
- *
- * A Spy has the following mehtod: wasCalled, callCount, mostRecentCall, and argsForCall (see docs)
- * Spies are torn down at the end of every spec.
- *
- * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
- *
- * @example
- * // a stub
- * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
- *
- * // spy example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- *
- * // actual foo.not will not be called, execution stops
- * spyOn(foo, 'not');
-
- // foo.not spied upon, execution will continue to implementation
- * spyOn(foo, 'not').andCallThrough();
- *
- * // fake example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- *
- * // foo.not(val) will return val
- * spyOn(foo, 'not').andCallFake(function(value) {return value;});
- *
- * // mock example
- * foo.not(7 == 7);
- * expect(foo.not).wasCalled();
- * expect(foo.not).wasCalledWith(true);
- *
- * @constructor
- * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
- * @param {String} name
- */
-jasmine.Spy = function(name) {
- /**
- * The name of the spy, if provided.
- */
- this.identity = name || 'unknown';
- /**
- * Is this Object a spy?
- */
- this.isSpy = true;
- /**
- * The actual function this spy stubs.
- */
- this.plan = function() {
- };
- /**
- * Tracking of the most recent call to the spy.
- * @example
- * var mySpy = jasmine.createSpy('foo');
- * mySpy(1, 2);
- * mySpy.mostRecentCall.args = [1, 2];
- */
- this.mostRecentCall = {};
-
- /**
- * Holds arguments for each call to the spy, indexed by call count
- * @example
- * var mySpy = jasmine.createSpy('foo');
- * mySpy(1, 2);
- * mySpy(7, 8);
- * mySpy.mostRecentCall.args = [7, 8];
- * mySpy.argsForCall[0] = [1, 2];
- * mySpy.argsForCall[1] = [7, 8];
- */
- this.argsForCall = [];
- this.calls = [];
-};
-
-/**
- * Tells a spy to call through to the actual implemenatation.
- *
- * @example
- * var foo = {
- * bar: function() { // do some stuff }
- * }
- *
- * // defining a spy on an existing property: foo.bar
- * spyOn(foo, 'bar').andCallThrough();
- */
-jasmine.Spy.prototype.andCallThrough = function() {
- this.plan = this.originalValue;
- return this;
-};
-
-/**
- * For setting the return value of a spy.
- *
- * @example
- * // defining a spy from scratch: foo() returns 'baz'
- * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
- *
- * // defining a spy on an existing property: foo.bar() returns 'baz'
- * spyOn(foo, 'bar').andReturn('baz');
- *
- * @param {Object} value
- */
-jasmine.Spy.prototype.andReturn = function(value) {
- this.plan = function() {
- return value;
- };
- return this;
-};
-
-/**
- * For throwing an exception when a spy is called.
- *
- * @example
- * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
- * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
- *
- * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
- * spyOn(foo, 'bar').andThrow('baz');
- *
- * @param {String} exceptionMsg
- */
-jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
- this.plan = function() {
- throw exceptionMsg;
- };
- return this;
-};
-
-/**
- * Calls an alternate implementation when a spy is called.
- *
- * @example
- * var baz = function() {
- * // do some stuff, return something
- * }
- * // defining a spy from scratch: foo() calls the function baz
- * var foo = jasmine.createSpy('spy on foo').andCall(baz);
- *
- * // defining a spy on an existing property: foo.bar() calls an anonymnous function
- * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
- *
- * @param {Function} fakeFunc
- */
-jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
- this.plan = fakeFunc;
- return this;
-};
-
-/**
- * Resets all of a spy's the tracking variables so that it can be used again.
- *
- * @example
- * spyOn(foo, 'bar');
- *
- * foo.bar();
- *
- * expect(foo.bar.callCount).toEqual(1);
- *
- * foo.bar.reset();
- *
- * expect(foo.bar.callCount).toEqual(0);
- */
-jasmine.Spy.prototype.reset = function() {
- this.wasCalled = false;
- this.callCount = 0;
- this.argsForCall = [];
- this.calls = [];
- this.mostRecentCall = {};
-};
-
-jasmine.createSpy = function(name) {
-
- var spyObj = function() {
- spyObj.wasCalled = true;
- spyObj.callCount++;
- var args = jasmine.util.argsToArray(arguments);
- spyObj.mostRecentCall.object = this;
- spyObj.mostRecentCall.args = args;
- spyObj.argsForCall.push(args);
- spyObj.calls.push({object: this, args: args});
- return spyObj.plan.apply(this, arguments);
- };
-
- var spy = new jasmine.Spy(name);
-
- for (var prop in spy) {
- spyObj[prop] = spy[prop];
- }
-
- spyObj.reset();
-
- return spyObj;
-};
-
-/**
- * Determines whether an object is a spy.
- *
- * @param {jasmine.Spy|Object} putativeSpy
- * @returns {Boolean}
- */
-jasmine.isSpy = function(putativeSpy) {
- return putativeSpy && putativeSpy.isSpy;
-};
-
-/**
- * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
- * large in one call.
- *
- * @param {String} baseName name of spy class
- * @param {Array} methodNames array of names of methods to make spies
- */
-jasmine.createSpyObj = function(baseName, methodNames) {
- if (!jasmine.isArray_(methodNames) || methodNames.length == 0) {
- throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
- }
- var obj = {};
- for (var i = 0; i < methodNames.length; i++) {
- obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
- }
- return obj;
-};
-
-jasmine.log = function(message) {
- jasmine.getEnv().currentSpec.log(message);
-};
-
-/**
- * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
- *
- * @example
- * // spy example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
- *
- * @see jasmine.createSpy
- * @param obj
- * @param methodName
- * @returns a Jasmine spy that can be chained with all spy methods
- */
-var spyOn = function(obj, methodName) {
- return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
-};
-
-/**
- * Creates a Jasmine spec that will be added to the current suite.
- *
- * // TODO: pending tests
- *
- * @example
- * it('should be true', function() {
- * expect(true).toEqual(true);
- * });
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var it = function(desc, func) {
- return jasmine.getEnv().it(desc, func);
-};
-
-/**
- * Creates a disabled Jasmine spec.
- *
- * A convenience method that allows existing specs to be disabled temporarily during development.
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var xit = function(desc, func) {
- return jasmine.getEnv().xit(desc, func);
-};
-
-/**
- * Starts a chain for a Jasmine expectation.
- *
- * It is passed an Object that is the actual value and should chain to one of the many
- * jasmine.Matchers functions.
- *
- * @param {Object} actual Actual value to test against and expected value
- */
-var expect = function(actual) {
- return jasmine.getEnv().currentSpec.expect(actual);
-};
-
-/**
- * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
- *
- * @param {Function} func Function that defines part of a jasmine spec.
- */
-var runs = function(func) {
- jasmine.getEnv().currentSpec.runs(func);
-};
-
-/**
- * Waits for a timeout before moving to the next runs()-defined block.
- * @param {Number} timeout
- */
-var waits = function(timeout) {
- jasmine.getEnv().currentSpec.waits(timeout);
-};
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next runs()-defined block.
- *
- * @param {Number} timeout
- * @param {Function} latchFunction
- * @param {String} message
- */
-var waitsFor = function(timeout, latchFunction, message) {
- jasmine.getEnv().currentSpec.waitsFor(timeout, latchFunction, message);
-};
-
-/**
- * A function that is called before each spec in a suite.
- *
- * Used for spec setup, including validating assumptions.
- *
- * @param {Function} beforeEachFunction
- */
-var beforeEach = function(beforeEachFunction) {
- jasmine.getEnv().beforeEach(beforeEachFunction);
-};
-
-/**
- * A function that is called after each spec in a suite.
- *
- * Used for restoring any state that is hijacked during spec execution.
- *
- * @param {Function} afterEachFunction
- */
-var afterEach = function(afterEachFunction) {
- jasmine.getEnv().afterEach(afterEachFunction);
-};
-
-/**
- * Defines a suite of specifications.
- *
- * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
- * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
- * of setup in some tests.
- *
- * @example
- * // TODO: a simple suite
- *
- * // TODO: a simple suite with a nested describe block
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var describe = function(description, specDefinitions) {
- return jasmine.getEnv().describe(description, specDefinitions);
-};
-
-/**
- * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var xdescribe = function(description, specDefinitions) {
- return jasmine.getEnv().xdescribe(description, specDefinitions);
-};
-
-
-// Provide the XMLHttpRequest class for IE 5.x-6.x:
-jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
- try {
- return new ActiveXObject("Msxml2.XMLHTTP.6.0");
- } catch(e) {
- }
- try {
- return new ActiveXObject("Msxml2.XMLHTTP.3.0");
- } catch(e) {
- }
- try {
- return new ActiveXObject("Msxml2.XMLHTTP");
- } catch(e) {
- }
- try {
- return new ActiveXObject("Microsoft.XMLHTTP");
- } catch(e) {
- }
- throw new Error("This browser does not support XMLHttpRequest.");
-} : XMLHttpRequest;
-
-/**
- * Adds suite files to an HTML document so that they are executed, thus adding them to the current
- * Jasmine environment.
- *
- * @param {String} url path to the file to include
- * @param {Boolean} opt_global
- */
-jasmine.include = function(url, opt_global) {
- if (opt_global) {
- document.write('