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