konacha-chai-matchers 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.gitmodules ADDED
@@ -0,0 +1,36 @@
1
+ [submodule "chai-spies"]
2
+ path = chai-spies
3
+ url = git://github.com/chaijs/chai-spies.git
4
+ [submodule "sinon-chai"]
5
+ path = sinon-chai
6
+ url = git://github.com/domenic/sinon-chai.git
7
+ [submodule "chai-as-promised"]
8
+ path = chai-as-promised
9
+ url = git://github.com/domenic/chai-as-promised.git
10
+ [submodule "chai-jquery"]
11
+ path = chai-jquery
12
+ url = git://github.com/chaijs/chai-jquery.git
13
+ [submodule "chai-timers"]
14
+ path = chai-timers
15
+ url = git://github.com/chaijs/chai-timers.git
16
+ [submodule "chai-stats"]
17
+ path = chai-stats
18
+ url = git://github.com/chaijs/chai-stats.git
19
+ [submodule "chai-null"]
20
+ path = chai-null
21
+ url = git://github.com/chaijs/chai-null.git
22
+ [submodule "chai-factories"]
23
+ path = chai-factories
24
+ url = git://github.com/chaijs/chai-factories.git
25
+ [submodule "chai-changes"]
26
+ path = chai-changes
27
+ url = https://github.com/matthijsgroen/chai-changes.git
28
+ [submodule "chai-backbone"]
29
+ path = chai-backbone
30
+ url = https://github.com/matthijsgroen/chai-backbone.git
31
+ [submodule "Sinon.JS"]
32
+ path = Sinon.JS
33
+ url = git://github.com/cjohansen/Sinon.JS.git
34
+ [submodule "js-factories"]
35
+ path = js-factories
36
+ url = git://github.com/matthijsgroen/js-factories.git
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rake'
4
+ gem 'juicer'
5
+
6
+ # Specify your gem's dependencies in konacha-chai-matchers.gemspec
7
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Matthijs Groen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ Konacha Chai Matchers
2
+ =====================
3
+
4
+ This library contains all [Chai.js plugins](http://chaijs.com/plugins)
5
+
6
+ Not all plugins are tested!
7
+
8
+ Installation
9
+ ------------
10
+
11
+ Add in the `Gemfile`:
12
+
13
+ gem 'konacha-chai-matchers'
14
+
15
+
16
+ Usage
17
+ -----
18
+
19
+ 1. Check the vendor/assets/javascripts
20
+ 2. Require the files needed
21
+
22
+ Example:
23
+
24
+ #= require sinon
25
+ #= require chai-changes
26
+ #= require js-factories
27
+ #= require chai-backbone
28
+ #= require chai-jquery
29
+
30
+ Contribution
31
+ ------------
32
+
33
+ Please submit an Github issue for libraries you want included or where the wrong file ends up in the `vendor` folder.
34
+
35
+ Updating the vendor libraries is done through `rake update`
36
+
37
+ please check `tasks/update.rb` for the code
38
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ Dir['tasks/*.rb'].each { |ext| load ext } if defined? Rake
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "konacha-chai-matchers/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "konacha-chai-matchers"
7
+ s.version = Konacha::Chai::Matchers::VERSION
8
+ s.authors = ["Matthijs Groen"]
9
+ s.email = ["matthijs.groen@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Chai.js plugins collection for Konacha}
12
+ s.description = %q{A set of Chai.js libraries ready to use for Konacha}
13
+
14
+ s.rubyforge_project = "konacha-chai-matchers"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.license = 'MIT'
21
+ end
@@ -0,0 +1,11 @@
1
+ require "konacha-chai-matchers/version"
2
+
3
+ module Konacha
4
+ module Chai
5
+ module Matchers
6
+ class Engine < ::Rails::Engine
7
+
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Konacha
2
+ module Chai
3
+ module Matchers
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
data/tasks/update.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'pathname'
2
+
3
+ desc 'updates all submodules'
4
+ task :update do
5
+ modules = collect_modules
6
+ update_modules modules
7
+ vendor_files modules
8
+ end
9
+
10
+ def collect_modules
11
+ modules = []
12
+ File.open('.gitmodules') do |f|
13
+ contents = f.read
14
+ contents.each_line do |line|
15
+ modules << $1 if line =~ /\[submodule "(.*)"\]/
16
+ end
17
+ end
18
+ modules
19
+ end
20
+
21
+ def main_files folders
22
+ folders.map do |f|
23
+ file_search = f.downcase
24
+ file_search << '.js' unless file_search =~ /\.js$/
25
+
26
+ path_order = ["./#{f}/pkg/#{file_search}", "./#{f}/#{file_search}", "./#{f}/lib/#{file_search}"]
27
+
28
+ r = path_order.map do |p|
29
+ p if File.exist? p
30
+ end.flatten.compact.first
31
+ end.flatten.compact
32
+ end
33
+
34
+ def vendor_files modules
35
+ files = main_files modules
36
+ puts "found #{files.length} of the #{modules.length} lib files" unless files.length == modules.length
37
+ path = "./vendor/assets/javascripts/"
38
+ Pathname.new(path).mkpath()
39
+ FileUtils.cp files, path
40
+ end
41
+
42
+ def update_modules modules
43
+ modules.each do |mod|
44
+ `cd ./#{mod} && git pull`
45
+ `cd ./#{mod} && ./build` if File.exist? "./#{mod}/build"
46
+ end
47
+ end
@@ -0,0 +1,391 @@
1
+ (function (chaiAsPromised) {
2
+ "use strict";
3
+
4
+ // Module systems magic dance.
5
+
6
+ if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
7
+ // NodeJS
8
+ module.exports = chaiAsPromised;
9
+ } else if (typeof define === "function" && define.amd) {
10
+ // AMD
11
+ define(function () {
12
+ return chaiAsPromised;
13
+ });
14
+ } else {
15
+ // Other environment (usually <script> tag): plug in to global chai instance directly.
16
+ chai.use(chaiAsPromised);
17
+ }
18
+ }(function chaiAsPromised(chai, utils) {
19
+ "use strict";
20
+
21
+ var Assertion = chai.Assertion;
22
+ var assert = chai.assert;
23
+
24
+ function assertIsAboutPromise(assertion) {
25
+ if (typeof assertion._obj.then !== "function") {
26
+ throw new TypeError(utils.inspect(assertion._obj) + " is not a promise!");
27
+ }
28
+ if (typeof assertion._obj.pipe === "function") {
29
+ throw new TypeError("Chai as Promised is incompatible with jQuery's so-called “promises.” Sorry!");
30
+ }
31
+ }
32
+
33
+ function property(name, asserter) {
34
+ utils.addProperty(Assertion.prototype, name, function () {
35
+ assertIsAboutPromise(this);
36
+ return asserter.apply(this, arguments);
37
+ });
38
+ }
39
+
40
+ function method(name, asserter) {
41
+ utils.addMethod(Assertion.prototype, name, function () {
42
+ assertIsAboutPromise(this);
43
+ return asserter.apply(this, arguments);
44
+ });
45
+ }
46
+
47
+ function notify(promise, callback) {
48
+ return promise.then(function () { callback(); }, callback);
49
+ }
50
+
51
+ function addNotifyMethod(extensiblePromise) {
52
+ extensiblePromise.notify = function (callback) {
53
+ return notify(extensiblePromise, callback);
54
+ };
55
+ }
56
+
57
+ var fulfilledAsserter = function () {
58
+ var transformedPromise = this._obj.then(
59
+ function (value) {
60
+ if (utils.flag(this, "negate")) {
61
+ // If we're negated, `this.assert`'s behavior is actually flipped, so `this.assert(true, ...)` will
62
+ // throw an error, as desired.
63
+ this.assert(true, null, "expected promise to be rejected but it was fulfilled with " +
64
+ utils.inspect(value));
65
+ }
66
+
67
+ return value;
68
+ }.bind(this),
69
+ function (reason) {
70
+ // If we're in a negated state (i.e. `.not.fulfilled`) then this assertion will get flipped and thus
71
+ // pass, as desired.
72
+ this.assert(false, "expected promise to be fulfilled but it was rejected with " +
73
+ utils.inspect(reason));
74
+ }.bind(this)
75
+ );
76
+
77
+ return makeAssertionPromise(transformedPromise, this);
78
+ };
79
+
80
+ var rejectedAsserter = function () {
81
+ // THIS SHIT IS COMPLICATED. Best illustrated by exhaustive example.
82
+ ////////////////////////////////////////////////////////////////////
83
+ // `fulfilledPromise.should.be.rejected`:
84
+ // `onOriginalFulfilled` → `this.assert(false, …)` throws → rejects
85
+ // `fulfilledPromise.should.not.be.rejected`:
86
+ // `onOriginalFulfilled` → `this.assert(false, …)` does nothing → fulfills
87
+ // `rejectedPromise.should.be.rejected`:
88
+ // `onOriginalRejected` does nothing relevant → fulfills
89
+ // `rejectedPromise.should.not.be.rejected`:
90
+ // `onOriginalRejected` → `this.assert(true, …)` throws → rejects
91
+ // `rejectedPromise.should.be.rejected.with(xxx)`:
92
+ // `onOriginalRejected` saves `rejectionReason` → fulfills →
93
+ // `with(xxx)` called → `onTransformedFulfilled` → assert about xxx → fulfills/rejects based on asserts
94
+ // `rejectedPromise.should.not.be.rejected.with(xxx)`:
95
+ // `onOriginalRejected` saves `rejectionReason`, `this.assert(true, …)` throws → rejects →
96
+ // `with(xxx)` called → `onTransformedRejected` → assert about xxx → fulfills/rejects based on asserts
97
+ // `fulfilledPromise.should.be.rejected.with(xxx)`:
98
+ // `onOriginalFulfilled` → `this.assert(false, …)` throws → rejects →
99
+ // `with(xxx)` called → `onTransformedRejected` → `this.assert(false, …)` throws → rejected
100
+ // `fulfilledPromise.should.not.be.rejected.with(xxx)`:
101
+ // `onOriginalFulfilled` → `this.assert(false, …)` does nothing → fulfills →
102
+ // `with(xxx)` called → `onTransformedFulfilled` → fulfills
103
+
104
+ var rejectionReason = null;
105
+
106
+ var onOriginalFulfilled = function (value) {
107
+ this.assert(false, "expected promise to be rejected but it was fulfilled with " + utils.inspect(value));
108
+ }.bind(this);
109
+
110
+ var onOriginalRejected = function (reason) {
111
+ // Store the reason so that `with` can look at it later. Be sure to do this before asserting, since
112
+ // throwing an error from the assert would cancel the process.
113
+ rejectionReason = reason;
114
+
115
+ if (utils.flag(this, "negate")) {
116
+ this.assert(true, null, "expected promise to be fulfilled but it was rejected with " +
117
+ utils.inspect(reason));
118
+ }
119
+
120
+ // If we didn't throw from the assert, transform rejections into fulfillments, by not re-throwing the
121
+ // reason.
122
+ }.bind(this);
123
+
124
+ var withMethod = function (Constructor, message) {
125
+ var desiredReason = null;
126
+
127
+ if (Constructor instanceof RegExp || typeof Constructor === "string") {
128
+ message = Constructor;
129
+ Constructor = null;
130
+ } else if (Constructor && Constructor instanceof Error) {
131
+ desiredReason = Constructor;
132
+ Constructor = null;
133
+ message = null;
134
+ }
135
+
136
+ var messageVerb = null;
137
+ var messageIsGood = null;
138
+
139
+ if (message instanceof RegExp) {
140
+ messageVerb = "matching";
141
+ messageIsGood = function () {
142
+ return message.test(rejectionReason.message);
143
+ };
144
+ } else {
145
+ messageVerb = "including";
146
+ messageIsGood = function () {
147
+ return rejectionReason.message.indexOf(message) !== -1;
148
+ };
149
+ }
150
+
151
+ function constructorIsGood() {
152
+ return rejectionReason instanceof Constructor;
153
+ }
154
+
155
+ function matchesDesiredReason() {
156
+ return rejectionReason === desiredReason;
157
+ }
158
+
159
+ var onTransformedFulfilled = function () {
160
+ if (!utils.flag(this, "negate")) {
161
+ if (desiredReason) {
162
+ this.assert(matchesDesiredReason(),
163
+ null,
164
+ "expected promise to be rejected with " + utils.inspect(desiredReason) + " but " +
165
+ "it was rejected with " + utils.inspect(rejectionReason));
166
+ }
167
+
168
+ if (Constructor) {
169
+ this.assert(constructorIsGood(),
170
+ "expected promise to be rejected with " + Constructor.prototype.name + " but it " +
171
+ "was rejected with " + utils.inspect(rejectionReason));
172
+ }
173
+
174
+ if (message) {
175
+ this.assert(messageIsGood(),
176
+ "expected promise to be rejected with an error " + messageVerb + " " + message +
177
+ " but got " + utils.inspect(rejectionReason.message));
178
+ }
179
+ }
180
+ }.bind(this);
181
+
182
+ var onTransformedRejected = function () {
183
+ if (utils.flag(this, "negate")) {
184
+ if (desiredReason) {
185
+ this.assert(matchesDesiredReason(),
186
+ null,
187
+ "expected promise to not be rejected with " + utils.inspect(desiredReason));
188
+ }
189
+
190
+ if (Constructor) {
191
+ this.assert(constructorIsGood(),
192
+ null,
193
+ "expected promise to not be rejected with " + Constructor.prototype.name);
194
+ }
195
+
196
+ if (message) {
197
+ this.assert(messageIsGood(),
198
+ null,
199
+ "expected promise to be not be rejected with an error " + messageVerb + " " +
200
+ message);
201
+ }
202
+ } else {
203
+ if (desiredReason) {
204
+ this.assert(false,
205
+ "expected promise to be rejected with " + utils.inspect(desiredReason) +
206
+ " but it was fulfilled");
207
+ }
208
+
209
+ if (Constructor) {
210
+ this.assert(false, "expected promise to be rejected with " + Constructor.prototype.name +
211
+ " but it was fulfilled");
212
+ }
213
+
214
+ if (message) {
215
+ this.assert(false, "expected promise to be rejected with an error " + messageVerb + " " +
216
+ message + " but it was fulfilled");
217
+ }
218
+ }
219
+ }.bind(this);
220
+
221
+ return makeAssertionPromise(transformedPromise.then(onTransformedFulfilled, onTransformedRejected), this);
222
+ }.bind(this);
223
+
224
+ var transformedPromise = makeAssertionPromise(this._obj.then(onOriginalFulfilled, onOriginalRejected), this);
225
+ Object.defineProperty(transformedPromise, "with", { enumerable: true, configurable: true, value: withMethod });
226
+
227
+ return transformedPromise;
228
+ };
229
+
230
+ function isChaiAsPromisedAsserter(asserterName) {
231
+ return ["fulfilled", "rejected", "broken", "eventually", "become"].indexOf(asserterName) !== -1;
232
+ }
233
+
234
+ function makeAssertionPromiseToDoAsserter(currentAssertionPromise, previousAssertionPromise, doAsserter) {
235
+ var promiseToDoAsserter = currentAssertionPromise.then(function (fulfillmentValue) {
236
+ // The previous assertion promise might have picked up some flags while waiting for fulfillment.
237
+ utils.transferFlags(previousAssertionPromise, currentAssertionPromise);
238
+
239
+ // Replace the object flag with the fulfillment value, so that doAsserter can operate properly.
240
+ utils.flag(currentAssertionPromise, "object", fulfillmentValue);
241
+
242
+ // Perform the actual asserter action and return the result of it.
243
+ return doAsserter();
244
+ });
245
+ return makeAssertionPromise(promiseToDoAsserter, currentAssertionPromise);
246
+ }
247
+
248
+ function makeAssertionPromise(promise, baseAssertion) {
249
+ // An assertion-promise is an (extensible!) promise with the following additions:
250
+ var assertionPromise = Object.create(promise);
251
+
252
+ // 1. A `notify` method.
253
+ addNotifyMethod(assertionPromise);
254
+
255
+ // 2. An `assert` method that acts exactly as it would on an assertion. This is called by promisified
256
+ // asserters after the promise fulfills.
257
+ assertionPromise.assert = function () {
258
+ return Assertion.prototype.assert.apply(assertionPromise, arguments);
259
+ };
260
+
261
+ // 3. Chai asserters, which act upon the promise's fulfillment value.
262
+ var asserterNames = Object.getOwnPropertyNames(Assertion.prototype);
263
+ asserterNames.forEach(function (asserterName) {
264
+ // We already added `notify` and `assert`; don't mess with those.
265
+ if (asserterName === "notify" || asserterName === "assert") {
266
+ return;
267
+ }
268
+
269
+ // Only add asserters for other libraries; poison-pill Chai as Promised ones.
270
+ if (isChaiAsPromisedAsserter(asserterName)) {
271
+ utils.addProperty(assertionPromise, asserterName, function () {
272
+ throw new Error("Cannot use Chai as Promised asserters more than once in an assertion.");
273
+ });
274
+ return;
275
+ }
276
+
277
+ // The asserter will need to be added differently depending on its type. In all cases we use
278
+ // `makeAssertionPromiseToDoAsserter`, which, given this current `assertionPromise` we are going to
279
+ // return, plus the `baseAssertion` we are basing it off of, will return a new assertion-promise that
280
+ // builds off of `assertionPromise` and `baseAssertion` to perform the actual asserter action upon
281
+ // fulfillment.
282
+ var propertyDescriptor = Object.getOwnPropertyDescriptor(Assertion.prototype, asserterName);
283
+
284
+ if (typeof propertyDescriptor.value === "function") {
285
+ // Case 1: simple method asserters
286
+ utils.addMethod(assertionPromise, asserterName, function () {
287
+ var args = arguments;
288
+
289
+ return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
290
+ return propertyDescriptor.value.apply(assertionPromise, args);
291
+ });
292
+ });
293
+ } else if (typeof propertyDescriptor.get === "function") {
294
+ // Case 2: property asserters. These break down into two subcases: chainable methods, and pure
295
+ // properties. An example of the former is `a`/`an`: `.should.be.an.instanceOf` vs.
296
+ // `should.be.an("object")`.
297
+ var isChainableMethod = false;
298
+ try {
299
+ isChainableMethod = typeof propertyDescriptor.get.call({}) === "function";
300
+ } catch (e) { }
301
+
302
+ if (isChainableMethod) {
303
+ // Case 2A: chainable methods. Recreate the chainable method, but operating on the augmented
304
+ // promise. We need to copy both the assertion behavior and the chaining behavior, since the
305
+ // chaining behavior might for example set flags on the object.
306
+ utils.addChainableMethod(
307
+ assertionPromise,
308
+ asserterName,
309
+ function () {
310
+ var args = arguments;
311
+
312
+ return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
313
+ return propertyDescriptor.get().apply(assertionPromise, args);
314
+ });
315
+ },
316
+ function () {
317
+ return propertyDescriptor.get.call(assertionPromise);
318
+ }
319
+ );
320
+ } else {
321
+ // Case 2B: pure property case
322
+ utils.addProperty(assertionPromise, asserterName, function () {
323
+ return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
324
+ return propertyDescriptor.get.call(assertionPromise);
325
+ });
326
+ });
327
+ }
328
+ }
329
+ });
330
+
331
+ return assertionPromise;
332
+ }
333
+
334
+ property("fulfilled", fulfilledAsserter);
335
+ property("rejected", rejectedAsserter);
336
+ property("broken", rejectedAsserter);
337
+
338
+ property("eventually", function () {
339
+ return makeAssertionPromise(this._obj, this);
340
+ });
341
+
342
+ method("become", function (value) {
343
+ return this.eventually.eql(value);
344
+ });
345
+
346
+ method("notify", function (callback) {
347
+ return notify(this._obj, callback);
348
+ });
349
+
350
+ // Now use the Assertion framework to build an `assert` interface.
351
+ var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
352
+ return typeof assert[propName] === "function";
353
+ });
354
+
355
+ assert.isFulfilled = function (promise, message) {
356
+ return (new Assertion(promise, message)).to.be.fulfilled;
357
+ };
358
+
359
+ assert.isRejected = assert.isBroken = function (promise, toTestAgainst, message) {
360
+ if (typeof toTestAgainst === "string") {
361
+ message = toTestAgainst;
362
+ toTestAgainst = null;
363
+ }
364
+
365
+ var shouldBeRejectedPromise = (new Assertion(promise, message)).to.be.rejected;
366
+ return toTestAgainst ? shouldBeRejectedPromise.with(toTestAgainst) : shouldBeRejectedPromise;
367
+ };
368
+
369
+ assert.eventually = {};
370
+ originalAssertMethods.forEach(function (assertMethodName) {
371
+ assert.eventually[assertMethodName] = function (promise) {
372
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
373
+
374
+ var promiseToAssert = promise.then(function (fulfillmentValue) {
375
+ return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
376
+ });
377
+
378
+ var augmentedPromiseToAssert = Object.create(promiseToAssert);
379
+ addNotifyMethod(augmentedPromiseToAssert);
380
+ return augmentedPromiseToAssert;
381
+ };
382
+ });
383
+
384
+ assert.becomes = function (promise, value) {
385
+ return assert.eventually.deepEqual(promise, value);
386
+ };
387
+
388
+ assert.doesNotBecome = function (promise, value) {
389
+ return assert.eventually.notDeepEqual(promise, value);
390
+ };
391
+ }));