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 result; 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 */ 90 jasmine.Matchers.prototype.toNotBe = function(expected) { 91 return this.actual !== expected; 92 }; 93 94 /** 95 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. 96 * 97 * @param expected 98 */ 99 jasmine.Matchers.prototype.toEqual = function(expected) { 100 return this.env.equals_(this.actual, expected); 101 }; 102 103 /** 104 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual 105 * @param expected 106 */ 107 jasmine.Matchers.prototype.toNotEqual = function(expected) { 108 return !this.env.equals_(this.actual, expected); 109 }; 110 111 /** 112 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes 113 * a pattern or a String. 114 * 115 * @param expected 116 */ 117 jasmine.Matchers.prototype.toMatch = function(expected) { 118 return new RegExp(expected).test(this.actual); 119 }; 120 121 /** 122 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch 123 * @param expected 124 */ 125 jasmine.Matchers.prototype.toNotMatch = function(expected) { 126 return !(new RegExp(expected).test(this.actual)); 127 }; 128 129 /** 130 * Matcher that compares the actual to jasmine.undefined. 131 */ 132 jasmine.Matchers.prototype.toBeDefined = function() { 133 return (this.actual !== jasmine.undefined); 134 }; 135 136 /** 137 * Matcher that compares the actual to jasmine.undefined. 138 */ 139 jasmine.Matchers.prototype.toBeUndefined = function() { 140 return (this.actual === jasmine.undefined); 141 }; 142 143 /** 144 * Matcher that compares the actual to null. 145 */ 146 jasmine.Matchers.prototype.toBeNull = function() { 147 return (this.actual === null); 148 }; 149 150 /** 151 * Matcher that boolean not-nots the actual. 152 */ 153 jasmine.Matchers.prototype.toBeTruthy = function() { 154 return !!this.actual; 155 }; 156 157 158 /** 159 * Matcher that boolean nots the actual. 160 */ 161 jasmine.Matchers.prototype.toBeFalsy = function() { 162 return !this.actual; 163 }; 164 165 166 /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ 167 jasmine.Matchers.prototype.wasCalled = function() { 168 return this.toHaveBeenCalled(); 169 }; 170 171 /** 172 * Matcher that checks to see if the actual, a Jasmine spy, was called. 173 */ 174 jasmine.Matchers.prototype.toHaveBeenCalled = function() { 175 if (arguments.length > 0) { 176 throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); 177 } 178 179 if (!jasmine.isSpy(this.actual)) { 180 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 181 } 182 183 this.message = function() { 184 return "Expected spy " + this.actual.identity + " to have been called."; 185 }; 186 187 return this.actual.wasCalled; 188 }; 189 190 /** 191 * Matcher that checks to see if the actual, a Jasmine spy, was not called. 192 * 193 * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead 194 */ 195 jasmine.Matchers.prototype.wasNotCalled = function() { 196 if (arguments.length > 0) { 197 throw new Error('wasNotCalled does not take arguments'); 198 } 199 200 if (!jasmine.isSpy(this.actual)) { 201 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 202 } 203 204 this.message = function() { 205 return "Expected spy " + this.actual.identity + " to not have been called."; 206 }; 207 208 return !this.actual.wasCalled; 209 }; 210 211 /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ 212 jasmine.Matchers.prototype.wasCalledWith = function() { 213 return this.toHaveBeenCalledWith.apply(this, arguments); 214 }; 215 216 /** 217 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. 218 * 219 * @example 220 * 221 */ 222 jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { 223 var expectedArgs = jasmine.util.argsToArray(arguments); 224 if (!jasmine.isSpy(this.actual)) { 225 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 226 } 227 this.message = function() { 228 if (this.actual.callCount == 0) { 229 return "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; 230 } else { 231 return "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall); 232 } 233 }; 234 235 return this.env.contains_(this.actual.argsForCall, expectedArgs); 236 }; 237 238 /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ 239 jasmine.Matchers.prototype.wasNotCalledWith = function() { 240 var expectedArgs = jasmine.util.argsToArray(arguments); 241 if (!jasmine.isSpy(this.actual)) { 242 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 243 } 244 245 this.message = function() { 246 return "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was"; 247 }; 248 249 return !this.env.contains_(this.actual.argsForCall, expectedArgs); 250 }; 251 252 /** 253 * Matcher that checks that the expected item is an element in the actual Array. 254 * 255 * @param {Object} expected 256 */ 257 jasmine.Matchers.prototype.toContain = function(expected) { 258 return this.env.contains_(this.actual, expected); 259 }; 260 261 /** 262 * Matcher that checks that the expected item is NOT an element in the actual Array. 263 * 264 * @param {Object} expected 265 */ 266 jasmine.Matchers.prototype.toNotContain = function(expected) { 267 return !this.env.contains_(this.actual, expected); 268 }; 269 270 jasmine.Matchers.prototype.toBeLessThan = function(expected) { 271 return this.actual < expected; 272 }; 273 274 jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { 275 return this.actual > expected; 276 }; 277 278 /** 279 * Matcher that checks that the expected exception was thrown by the actual. 280 * 281 * @param {String} expected 282 */ 283 jasmine.Matchers.prototype.toThrow = function(expected) { 284 var result = false; 285 var exception; 286 if (typeof this.actual != 'function') { 287 throw new Error('Actual is not a function'); 288 } 289 try { 290 this.actual(); 291 } catch (e) { 292 exception = e; 293 } 294 if (exception) { 295 result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); 296 } 297 298 this.message = function() { 299 if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { 300 return ["Expected function to throw", expected.message || expected, ", but it threw", exception.message || exception].join(' '); 301 } else { 302 return "Expected function to throw an exception."; 303 } 304 }; 305 306 return result; 307 }; 308 309 jasmine.Matchers.Any = function(expectedClass) { 310 this.expectedClass = expectedClass; 311 }; 312 313 jasmine.Matchers.Any.prototype.matches = function(other) { 314 if (this.expectedClass == String) { 315 return typeof other == 'string' || other instanceof String; 316 } 317 318 if (this.expectedClass == Number) { 319 return typeof other == 'number' || other instanceof Number; 320 } 321 322 if (this.expectedClass == Function) { 323 return typeof other == 'function' || other instanceof Function; 324 } 325 326 if (this.expectedClass == Object) { 327 return typeof other == 'object'; 328 } 329 330 return other instanceof this.expectedClass; 331 }; 332 333 jasmine.Matchers.Any.prototype.toString = function() { 334 return '<jasmine.any(' + this.expectedClass + ')>'; 335 }; 336 337