1 /** 2 * @constructor 3 * @param {jasmine.Env} env 4 * @param actual 5 * @param {jasmine.Spec} spec 6 */ 7 jasmine.Matchers = function(env, actual, spec, opt_isNot) { 8 this.env = env; 9 this.actual = actual; 10 this.spec = spec; 11 this.isNot = opt_isNot || false; 12 this.reportWasCalled_ = false; 13 }; 14 15 // todo: @deprecated as of Jasmine 0.11, remove soon [xw] 16 jasmine.Matchers.pp = function(str) { 17 throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); 18 }; 19 20 // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] 21 jasmine.Matchers.prototype.report = function(result, failing_message, details) { 22 throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); 23 }; 24 25 jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { 26 for (var methodName in prototype) { 27 if (methodName == 'report') continue; 28 var orig = prototype[methodName]; 29 matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); 30 } 31 }; 32 33 jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { 34 return function() { 35 var matcherArgs = jasmine.util.argsToArray(arguments); 36 var result = matcherFunction.apply(this, arguments); 37 38 if (this.isNot) { 39 result = !result; 40 } 41 42 if (this.reportWasCalled_) return result; 43 44 var message; 45 if (!result) { 46 if (this.message) { 47 message = this.message.apply(this, arguments); 48 if (jasmine.isArray_(message)) { 49 message = message[this.isNot ? 1 : 0]; 50 } 51 } else { 52 var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); 53 message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; 54 if (matcherArgs.length > 0) { 55 for (var i = 0; i < matcherArgs.length; i++) { 56 if (i > 0) message += ","; 57 message += " " + jasmine.pp(matcherArgs[i]); 58 } 59 } 60 message += "."; 61 } 62 } 63 var expectationResult = new jasmine.ExpectationResult({ 64 matcherName: matcherName, 65 passed: result, 66 expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], 67 actual: this.actual, 68 message: message 69 }); 70 this.spec.addMatcherResult(expectationResult); 71 return jasmine.undefined; 72 }; 73 }; 74 75 76 77 78 /** 79 * toBe: compares the actual to the expected using === 80 * @param expected 81 */ 82 jasmine.Matchers.prototype.toBe = function(expected) { 83 return this.actual === expected; 84 }; 85 86 /** 87 * toNotBe: compares the actual to the expected using !== 88 * @param expected 89 * @deprecated as of 1.0. Use not.toBe() instead. 90 */ 91 jasmine.Matchers.prototype.toNotBe = function(expected) { 92 return this.actual !== expected; 93 }; 94 95 /** 96 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. 97 * 98 * @param expected 99 */ 100 jasmine.Matchers.prototype.toEqual = function(expected) { 101 return this.env.equals_(this.actual, expected); 102 }; 103 104 /** 105 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual 106 * @param expected 107 * @deprecated as of 1.0. Use not.toNotEqual() instead. 108 */ 109 jasmine.Matchers.prototype.toNotEqual = function(expected) { 110 return !this.env.equals_(this.actual, expected); 111 }; 112 113 /** 114 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes 115 * a pattern or a String. 116 * 117 * @param expected 118 */ 119 jasmine.Matchers.prototype.toMatch = function(expected) { 120 return new RegExp(expected).test(this.actual); 121 }; 122 123 /** 124 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch 125 * @param expected 126 * @deprecated as of 1.0. Use not.toMatch() instead. 127 */ 128 jasmine.Matchers.prototype.toNotMatch = function(expected) { 129 return !(new RegExp(expected).test(this.actual)); 130 }; 131 132 /** 133 * Matcher that compares the actual to jasmine.undefined. 134 */ 135 jasmine.Matchers.prototype.toBeDefined = function() { 136 return (this.actual !== jasmine.undefined); 137 }; 138 139 /** 140 * Matcher that compares the actual to jasmine.undefined. 141 */ 142 jasmine.Matchers.prototype.toBeUndefined = function() { 143 return (this.actual === jasmine.undefined); 144 }; 145 146 /** 147 * Matcher that compares the actual to null. 148 */ 149 jasmine.Matchers.prototype.toBeNull = function() { 150 return (this.actual === null); 151 }; 152 153 /** 154 * Matcher that boolean not-nots the actual. 155 */ 156 jasmine.Matchers.prototype.toBeTruthy = function() { 157 return !!this.actual; 158 }; 159 160 161 /** 162 * Matcher that boolean nots the actual. 163 */ 164 jasmine.Matchers.prototype.toBeFalsy = function() { 165 return !this.actual; 166 }; 167 168 169 /** 170 * Matcher that checks to see if the actual, a Jasmine spy, was called. 171 */ 172 jasmine.Matchers.prototype.toHaveBeenCalled = function() { 173 if (arguments.length > 0) { 174 throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); 175 } 176 177 if (!jasmine.isSpy(this.actual)) { 178 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 179 } 180 181 this.message = function() { 182 return [ 183 "Expected spy " + this.actual.identity + " to have been called.", 184 "Expected spy " + this.actual.identity + " not to have been called." 185 ]; 186 }; 187 188 return this.actual.wasCalled; 189 }; 190 191 /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ 192 jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; 193 194 /** 195 * Matcher that checks to see if the actual, a Jasmine spy, was not called. 196 * 197 * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead 198 */ 199 jasmine.Matchers.prototype.wasNotCalled = function() { 200 if (arguments.length > 0) { 201 throw new Error('wasNotCalled does not take arguments'); 202 } 203 204 if (!jasmine.isSpy(this.actual)) { 205 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 206 } 207 208 this.message = function() { 209 return [ 210 "Expected spy " + this.actual.identity + " to not have been called.", 211 "Expected spy " + this.actual.identity + " to have been called." 212 ]; 213 }; 214 215 return !this.actual.wasCalled; 216 }; 217 218 /** 219 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. 220 * 221 * @example 222 * 223 */ 224 jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { 225 var expectedArgs = jasmine.util.argsToArray(arguments); 226 if (!jasmine.isSpy(this.actual)) { 227 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 228 } 229 this.message = function() { 230 if (this.actual.callCount == 0) { 231 // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] 232 return [ 233 "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", 234 "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was." 235 ]; 236 } else { 237 return [ 238 "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), 239 "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) 240 ]; 241 } 242 }; 243 244 return this.env.contains_(this.actual.argsForCall, expectedArgs); 245 }; 246 247 /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ 248 jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; 249 250 /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ 251 jasmine.Matchers.prototype.wasNotCalledWith = function() { 252 var expectedArgs = jasmine.util.argsToArray(arguments); 253 if (!jasmine.isSpy(this.actual)) { 254 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 255 } 256 257 this.message = function() { 258 return [ 259 "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", 260 "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" 261 ] 262 }; 263 264 return !this.env.contains_(this.actual.argsForCall, expectedArgs); 265 }; 266 267 /** 268 * Matcher that checks that the expected item is an element in the actual Array. 269 * 270 * @param {Object} expected 271 */ 272 jasmine.Matchers.prototype.toContain = function(expected) { 273 return this.env.contains_(this.actual, expected); 274 }; 275 276 /** 277 * Matcher that checks that the expected item is NOT an element in the actual Array. 278 * 279 * @param {Object} expected 280 * @deprecated as of 1.0. Use not.toNotContain() instead. 281 */ 282 jasmine.Matchers.prototype.toNotContain = function(expected) { 283 return !this.env.contains_(this.actual, expected); 284 }; 285 286 jasmine.Matchers.prototype.toBeLessThan = function(expected) { 287 return this.actual < expected; 288 }; 289 290 jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { 291 return this.actual > expected; 292 }; 293 294 /** 295 * Matcher that checks that the expected exception was thrown by the actual. 296 * 297 * @param {String} expected 298 */ 299 jasmine.Matchers.prototype.toThrow = function(expected) { 300 var result = false; 301 var exception; 302 if (typeof this.actual != 'function') { 303 throw new Error('Actual is not a function'); 304 } 305 try { 306 this.actual(); 307 } catch (e) { 308 exception = e; 309 } 310 if (exception) { 311 result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); 312 } 313 314 var not = this.isNot ? "not " : ""; 315 316 this.message = function() { 317 if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { 318 return ["Expected function " + not + "to throw", expected ? expected.message || expected : " an exception", ", but it threw", exception.message || exception].join(' '); 319 } else { 320 return "Expected function to throw an exception."; 321 } 322 }; 323 324 return result; 325 }; 326 327 jasmine.Matchers.Any = function(expectedClass) { 328 this.expectedClass = expectedClass; 329 }; 330 331 jasmine.Matchers.Any.prototype.matches = function(other) { 332 if (this.expectedClass == String) { 333 return typeof other == 'string' || other instanceof String; 334 } 335 336 if (this.expectedClass == Number) { 337 return typeof other == 'number' || other instanceof Number; 338 } 339 340 if (this.expectedClass == Function) { 341 return typeof other == 'function' || other instanceof Function; 342 } 343 344 if (this.expectedClass == Object) { 345 return typeof other == 'object'; 346 } 347 348 return other instanceof this.expectedClass; 349 }; 350 351 jasmine.Matchers.Any.prototype.toString = function() { 352 return '<jasmine.any(' + this.expectedClass + ')>'; 353 }; 354 355