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