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