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 undefined.
117  */
118 jasmine.Matchers.prototype.toBeDefined = function() {
119   return (this.actual !== undefined);
120 };
121 
122 /**
123  * Matcher that compares the actual to undefined.
124  */
125 jasmine.Matchers.prototype.toBeUndefined = function() {
126   return (this.actual === 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     return "Expected spy to have been called with " + jasmine.pp(arguments) + " but was called with " + jasmine.pp(this.actual.argsForCall);
202   };
203 
204   return this.env.contains_(this.actual.argsForCall, jasmine.util.argsToArray(arguments));
205 };
206 
207 /**
208  * Matcher that checks that the expected item is an element in the actual Array.
209  *
210  * @param {Object} item
211  */
212 jasmine.Matchers.prototype.toContain = function(expected) {
213   return this.env.contains_(this.actual, expected);
214 };
215 
216 /**
217  * Matcher that checks that the expected item is NOT an element in the actual Array.
218  *
219  * @param {Object} item
220  */
221 jasmine.Matchers.prototype.toNotContain = function(expected) {
222   return !this.env.contains_(this.actual, expected);
223 };
224 
225 jasmine.Matchers.prototype.toBeLessThan = function(expected) {
226   return this.actual < expected;
227 };
228 
229 jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
230   return this.actual > expected;
231 };
232 
233 /**
234  * Matcher that checks that the expected exception was thrown by the actual.
235  *
236  * @param {String} expectedException
237  */
238 jasmine.Matchers.prototype.toThrow = function(expected) {
239   function getException_(actual, expected) {
240     var exception;
241     if (typeof actual != 'function') {
242       throw new Error('Actual is not a function');
243     }
244     try {
245       actual();
246     } catch (e) {
247       exception = e;
248     }
249     return exception;
250   }
251 
252   var result = false;
253   var exception = getException_(this.actual, expected);
254   if (exception) {
255     result = (expected === undefined || this.env.equals_(exception.message || exception, expected.message || expected));
256   }
257 
258   this.message = function(expected) {
259     var exception = getException_(this.actual, expected);
260     if (exception && (expected === undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
261       return ["Expected function to throw", expected.message || expected, ", but it threw", exception.message || exception  ].join(' ');
262     } else {
263       return "Expected function to throw an exception.";
264     }
265   };
266 
267   return result;
268 };
269 
270 jasmine.Matchers.Any = function(expectedClass) {
271   this.expectedClass = expectedClass;
272 };
273 
274 jasmine.Matchers.Any.prototype.matches = function(other) {
275   if (this.expectedClass == String) {
276     return typeof other == 'string' || other instanceof String;
277   }
278 
279   if (this.expectedClass == Number) {
280     return typeof other == 'number' || other instanceof Number;
281   }
282 
283   if (this.expectedClass == Function) {
284     return typeof other == 'function' || other instanceof Function;
285   }
286 
287   if (this.expectedClass == Object) {
288     return typeof other == 'object';
289   }
290 
291   return other instanceof this.expectedClass;
292 };
293 
294 jasmine.Matchers.Any.prototype.toString = function() {
295   return '<jasmine.any(' + this.expectedClass + ')>';
296 };
297 
298