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