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