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 this.report(); 19 }; 20 21 /** @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. */ 22 jasmine.Matchers.prototype.report = function(result, failing_message, details) { 23 // todo: report a deprecation warning [xw] 24 25 if (this.isNot) { 26 throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); 27 } 28 29 this.reportWasCalled_ = true; 30 var expectationResult = new jasmine.ExpectationResult({ 31 passed: result, 32 message: failing_message, 33 details: details 34 }); 35 this.spec.addMatcherResult(expectationResult); 36 return result; 37 }; 38 39 jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { 40 for (var methodName in prototype) { 41 if (methodName == 'report') continue; 42 var orig = prototype[methodName]; 43 matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); 44 } 45 }; 46 47 jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { 48 return function() { 49 var matcherArgs = jasmine.util.argsToArray(arguments); 50 var result = matcherFunction.apply(this, arguments); 51 52 if (this.isNot) { 53 result = !result; 54 } 55 56 if (this.reportWasCalled_) return result; 57 58 var message; 59 if (!result) { 60 if (this.message) { 61 message = this.message.apply(this, arguments); 62 if (jasmine.isArray_(message)) { 63 message = message[this.isNot ? 1 : 0]; 64 } 65 } else { 66 var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); 67 message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; 68 if (matcherArgs.length > 0) { 69 for (var i = 0; i < matcherArgs.length; i++) { 70 if (i > 0) message += ","; 71 message += " " + jasmine.pp(matcherArgs[i]); 72 } 73 } 74 message += "."; 75 } 76 } 77 var expectationResult = new jasmine.ExpectationResult({ 78 matcherName: matcherName, 79 passed: result, 80 expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], 81 actual: this.actual, 82 message: message 83 }); 84 this.spec.addMatcherResult(expectationResult); 85 return result; 86 }; 87 }; 88 89 90 91 92 /** 93 * toBe: compares the actual to the expected using === 94 * @param expected 95 */ 96 jasmine.Matchers.prototype.toBe = function(expected) { 97 return this.actual === expected; 98 }; 99 100 /** 101 * toNotBe: compares the actual to the expected using !== 102 * @param expected 103 */ 104 jasmine.Matchers.prototype.toNotBe = function(expected) { 105 return this.actual !== expected; 106 }; 107 108 /** 109 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. 110 * 111 * @param expected 112 */ 113 jasmine.Matchers.prototype.toEqual = function(expected) { 114 return this.env.equals_(this.actual, expected); 115 }; 116 117 /** 118 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual 119 * @param expected 120 */ 121 jasmine.Matchers.prototype.toNotEqual = function(expected) { 122 return !this.env.equals_(this.actual, expected); 123 }; 124 125 /** 126 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes 127 * a pattern or a String. 128 * 129 * @param expected 130 */ 131 jasmine.Matchers.prototype.toMatch = function(expected) { 132 return new RegExp(expected).test(this.actual); 133 }; 134 135 /** 136 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch 137 * @param expected 138 */ 139 jasmine.Matchers.prototype.toNotMatch = function(expected) { 140 return !(new RegExp(expected).test(this.actual)); 141 }; 142 143 /** 144 * Matcher that compares the actual to jasmine.undefined. 145 */ 146 jasmine.Matchers.prototype.toBeDefined = function() { 147 return (this.actual !== jasmine.undefined); 148 }; 149 150 /** 151 * Matcher that compares the actual to jasmine.undefined. 152 */ 153 jasmine.Matchers.prototype.toBeUndefined = function() { 154 return (this.actual === jasmine.undefined); 155 }; 156 157 /** 158 * Matcher that compares the actual to null. 159 */ 160 jasmine.Matchers.prototype.toBeNull = function() { 161 return (this.actual === null); 162 }; 163 164 /** 165 * Matcher that boolean not-nots the actual. 166 */ 167 jasmine.Matchers.prototype.toBeTruthy = function() { 168 return !!this.actual; 169 }; 170 171 172 /** 173 * Matcher that boolean nots the actual. 174 */ 175 jasmine.Matchers.prototype.toBeFalsy = function() { 176 return !this.actual; 177 }; 178 179 /** 180 * Matcher that checks to see if the actual, a Jasmine spy, was called. 181 */ 182 jasmine.Matchers.prototype.wasCalled = function() { 183 if (arguments.length > 0) { 184 throw new Error('wasCalled does not take arguments, use wasCalledWith'); 185 } 186 187 if (!jasmine.isSpy(this.actual)) { 188 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 189 } 190 191 this.message = function() { 192 return "Expected spy " + this.actual.identity + " to have been called."; 193 }; 194 195 return this.actual.wasCalled; 196 }; 197 198 /** 199 * Matcher that checks to see if the actual, a Jasmine spy, was not called. 200 */ 201 jasmine.Matchers.prototype.wasNotCalled = function() { 202 if (arguments.length > 0) { 203 throw new Error('wasNotCalled does not take arguments'); 204 } 205 206 if (!jasmine.isSpy(this.actual)) { 207 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 208 } 209 210 this.message = function() { 211 return "Expected spy " + this.actual.identity + " to not have been called."; 212 }; 213 214 return !this.actual.wasCalled; 215 }; 216 217 /** 218 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. 219 * 220 * @example 221 * 222 */ 223 jasmine.Matchers.prototype.wasCalledWith = function() { 224 var expectedArgs = jasmine.util.argsToArray(arguments); 225 if (!jasmine.isSpy(this.actual)) { 226 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 227 } 228 this.message = function() { 229 if (this.actual.callCount == 0) { 230 return "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; 231 } else { 232 return "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall); 233 } 234 }; 235 236 return this.env.contains_(this.actual.argsForCall, expectedArgs); 237 }; 238 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