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 * Matcher that checks to see if the actual, a Jasmine spy, was called. 167 */ 168 jasmine.Matchers.prototype.wasCalled = function() { 169 if (arguments.length > 0) { 170 throw new Error('wasCalled does not take arguments, use wasCalledWith'); 171 } 172 173 if (!jasmine.isSpy(this.actual)) { 174 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 175 } 176 177 this.message = function() { 178 return "Expected spy " + this.actual.identity + " to have been called."; 179 }; 180 181 return this.actual.wasCalled; 182 }; 183 184 /** 185 * Matcher that checks to see if the actual, a Jasmine spy, was not called. 186 */ 187 jasmine.Matchers.prototype.wasNotCalled = function() { 188 if (arguments.length > 0) { 189 throw new Error('wasNotCalled does not take arguments'); 190 } 191 192 if (!jasmine.isSpy(this.actual)) { 193 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 194 } 195 196 this.message = function() { 197 return "Expected spy " + this.actual.identity + " to not have been called."; 198 }; 199 200 return !this.actual.wasCalled; 201 }; 202 203 /** 204 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. 205 * 206 * @example 207 * 208 */ 209 jasmine.Matchers.prototype.wasCalledWith = function() { 210 var expectedArgs = jasmine.util.argsToArray(arguments); 211 if (!jasmine.isSpy(this.actual)) { 212 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 213 } 214 this.message = function() { 215 if (this.actual.callCount == 0) { 216 return "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; 217 } else { 218 return "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall); 219 } 220 }; 221 222 return this.env.contains_(this.actual.argsForCall, expectedArgs); 223 }; 224 225 jasmine.Matchers.prototype.wasNotCalledWith = function() { 226 var expectedArgs = jasmine.util.argsToArray(arguments); 227 if (!jasmine.isSpy(this.actual)) { 228 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 229 } 230 231 this.message = function() { 232 return "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was"; 233 }; 234 235 return !this.env.contains_(this.actual.argsForCall, expectedArgs); 236 }; 237 238 /** 239 * Matcher that checks that the expected item is an element in the actual Array. 240 * 241 * @param {Object} expected 242 */ 243 jasmine.Matchers.prototype.toContain = function(expected) { 244 return this.env.contains_(this.actual, expected); 245 }; 246 247 /** 248 * Matcher that checks that the expected item is NOT an element in the actual Array. 249 * 250 * @param {Object} expected 251 */ 252 jasmine.Matchers.prototype.toNotContain = function(expected) { 253 return !this.env.contains_(this.actual, expected); 254 }; 255 256 jasmine.Matchers.prototype.toBeLessThan = function(expected) { 257 return this.actual < expected; 258 }; 259 260 jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { 261 return this.actual > expected; 262 }; 263 264 /** 265 * Matcher that checks that the expected exception was thrown by the actual. 266 * 267 * @param {String} expected 268 */ 269 jasmine.Matchers.prototype.toThrow = function(expected) { 270 var result = false; 271 var exception; 272 if (typeof this.actual != 'function') { 273 throw new Error('Actual is not a function'); 274 } 275 try { 276 this.actual(); 277 } catch (e) { 278 exception = e; 279 } 280 if (exception) { 281 result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); 282 } 283 284 this.message = function() { 285 if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { 286 return ["Expected function to throw", expected.message || expected, ", but it threw", exception.message || exception].join(' '); 287 } else { 288 return "Expected function to throw an exception."; 289 } 290 }; 291 292 return result; 293 }; 294 295 jasmine.Matchers.Any = function(expectedClass) { 296 this.expectedClass = expectedClass; 297 }; 298 299 jasmine.Matchers.Any.prototype.matches = function(other) { 300 if (this.expectedClass == String) { 301 return typeof other == 'string' || other instanceof String; 302 } 303 304 if (this.expectedClass == Number) { 305 return typeof other == 'number' || other instanceof Number; 306 } 307 308 if (this.expectedClass == Function) { 309 return typeof other == 'function' || other instanceof Function; 310 } 311 312 if (this.expectedClass == Object) { 313 return typeof other == 'object'; 314 } 315 316 return other instanceof this.expectedClass; 317 }; 318 319 jasmine.Matchers.Any.prototype.toString = function() { 320 return '<jasmine.any(' + this.expectedClass + ')>'; 321 }; 322 323