remove jasmine, added chai+mocha with promise support
This commit is contained in:
Родитель
9dc1a87892
Коммит
a52765498a
|
@ -6,10 +6,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// Gombot is optional
|
||||
// Gombot and importedModules are optional. (However, importedModules need to be
|
||||
// provided in Firefox and defined in some other way for other platforms.)
|
||||
var _Gombot = function(importedModules, Gombot) {
|
||||
|
||||
Gombot = Gombot || {};
|
||||
importedModules = importedModules || {};
|
||||
|
||||
function getModule(name) {
|
||||
if (typeof window !== "undefined" && typeof window[name] !== "undefined") {
|
||||
|
@ -54,7 +56,6 @@ var _Gombot = function(importedModules, Gombot) {
|
|||
Gombot.CapturedCredentialStorage = getModule("CapturedCredentialStorage")(Gombot, getModule("Uri"));
|
||||
Gombot.Linker = getModule("Linker")(Gombot);
|
||||
Gombot.AccountManager = getModule("AccountManager")(Gombot, _);
|
||||
Gombot.CommandHandler = getModule("CommandHandler")(Gombot, Gombot.Messaging, _);
|
||||
Gombot.Pages = getModule("Pages")(Gombot);
|
||||
Gombot.Crypto = getModule("GombotCrypto");
|
||||
Gombot.User = getModule("User")(Backbone, _, Gombot);
|
||||
|
@ -82,6 +83,12 @@ var _Gombot = function(importedModules, Gombot) {
|
|||
options = options || {};
|
||||
options.storeName = options.storeName || "users";
|
||||
options.callback = options.callback || checkFirstRun;
|
||||
if (!options.testing) {
|
||||
Gombot.CommandHandler = getModule("CommandHandler")(Gombot, Gombot.Messaging, _);
|
||||
}
|
||||
// TODO: refactor this code (maybe using promises?) so the SyncAdapter
|
||||
// and UserCollection don't need to be created inside this init function.
|
||||
// Also maybe move the storage creation out of here
|
||||
new Gombot.Storage(options.storeName, function(store) {
|
||||
Gombot.SyncAdapter = getModule("SyncAdapter")(Gombot, Gombot.Crypto, store, _);
|
||||
Gombot.UserCollection = getModule("UserCollection")(Backbone, _, Gombot, store);
|
||||
|
@ -113,6 +120,6 @@ var _Gombot = function(importedModules, Gombot) {
|
|||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = _Gombot; // export namespace constructor, for Firefox
|
||||
} else { // otherwise, just create the global Gombot namespace and init
|
||||
var Gombot = _Gombot({});
|
||||
Gombot.init();
|
||||
var gGombot = _Gombot({});
|
||||
gGombot.init();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ var Pages = function(Gombot) {
|
|||
|
||||
const PAGES_PATH="pages/first_run/";
|
||||
|
||||
// Firefox specific parts
|
||||
if (typeof chrome === "undefined") {
|
||||
// define a pageMod for resource urls
|
||||
var pageMod = require("page-mod");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="../../lib/bootstrap.css" rel="stylesheet">
|
||||
<link href="../../../lib/bootstrap.css" rel="stylesheet">
|
||||
<script src="../../lib/jquery.js"></script>
|
||||
<script src="debug_settings.js"></script>
|
||||
<style>
|
||||
|
@ -16,6 +16,7 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<a href="../../../spec/runner.html">Tests</a>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span8">
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1 +1 @@
|
|||
Subproject commit 081416b488a5e9f07893644c9b033d5b5a105d45
|
||||
Subproject commit 4fe6d2ae6e8c30237498e067f6ac91faaee65d2f
|
|
@ -0,0 +1,67 @@
|
|||
var SpecHelpers = function(Gombot) {
|
||||
var LocalStorage = Gombot.LocalStorage;
|
||||
var User = Gombot.User;
|
||||
var self = {
|
||||
STORE_NAME: "testUsers",
|
||||
TEST_PASSWORD: "pässwörd",
|
||||
TEST_PIN: "1234",
|
||||
|
||||
generateTestEmail: function() {
|
||||
return "test+"+Math.floor((1+Math.random())*1000000)+"@test.com";
|
||||
},
|
||||
|
||||
getLocalStorageKeyForUser: function(model) {
|
||||
return this.STORE_NAME+"-"+model.id;
|
||||
},
|
||||
|
||||
getLocalStorageItem: function(name) {
|
||||
var dfd = Q.defer();
|
||||
LocalStorage.getItem(name, function(value) {
|
||||
dfd.resolve(value);
|
||||
});
|
||||
return dfd.promise;
|
||||
},
|
||||
|
||||
validateUserAgainstLocalStorage: function(user) {
|
||||
describe("User model matches data in LocalStorage", function() {
|
||||
it("should have created a record for the User", function() {
|
||||
var indexOfUserRecord = -1;
|
||||
runs(function() {
|
||||
LocalStorage.getItem(self.STORE_NAME, function(store) {
|
||||
var records = (store && store.split(",")) || [];
|
||||
indexOfUserRecord = records.indexOf(user.id) >= 0
|
||||
});
|
||||
});
|
||||
waitsFor(function() { return indexOfUserRecord >=0; },
|
||||
"Could not find User record",
|
||||
100);
|
||||
});
|
||||
});
|
||||
// LocalStorage.getItem(getStorageKeyForUser(user), function(json) {
|
||||
// var attrs = JSON.parse(json);
|
||||
// passed = passed && (user.get("email").length > 0) && (user.get("email") === attrs.email);
|
||||
// passed = passed && (user.get("version").length > 0) && (user.get("version") === attrs.version)
|
||||
// passed = passed && (user.id.length > 0) && (user.id === attrs.id);
|
||||
// passed = passed && (attrs.ciphertext.length > 0);
|
||||
// passed = passed && (typeof attrs.pin === "undefined"); // pin should be missing
|
||||
// cb(test, passed);
|
||||
// });
|
||||
},
|
||||
|
||||
createUser: function(options) {
|
||||
var dfd = Q.defer();
|
||||
options = options || {};
|
||||
var email = options.email || self.generateTestEmail();
|
||||
var user = new User({ email: email, pin: self.TEST_PIN });
|
||||
// TODO: throw in one login entry here
|
||||
var o = _.clone(options);
|
||||
user.save(null, _.extend(o, { success: function() {
|
||||
dfd.resolve(user);
|
||||
}, error: function(err) {
|
||||
dfd.reject(err);
|
||||
}}));
|
||||
return dfd.promise;
|
||||
}
|
||||
};
|
||||
return self;
|
||||
};
|
|
@ -0,0 +1,405 @@
|
|||
(function (chaiAsPromised) {
|
||||
"use strict";
|
||||
|
||||
// Module systems magic dance.
|
||||
|
||||
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
|
||||
// NodeJS
|
||||
module.exports = chaiAsPromised;
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(function () {
|
||||
return chaiAsPromised;
|
||||
});
|
||||
} else {
|
||||
// Other environment (usually <script> tag): plug in to global chai instance directly.
|
||||
chai.use(chaiAsPromised);
|
||||
}
|
||||
}(function chaiAsPromised(chai, utils) {
|
||||
"use strict";
|
||||
|
||||
var Assertion = chai.Assertion;
|
||||
var assert = chai.assert;
|
||||
|
||||
function assertIsAboutPromise(assertion) {
|
||||
if (typeof assertion._obj.then !== "function") {
|
||||
throw new TypeError(utils.inspect(assertion._obj) + " is not a promise!");
|
||||
}
|
||||
if (typeof assertion._obj.pipe === "function") {
|
||||
throw new TypeError("Chai as Promised is incompatible with jQuery's so-called “promises.” Sorry!");
|
||||
}
|
||||
}
|
||||
|
||||
function property(name, asserter) {
|
||||
utils.addProperty(Assertion.prototype, name, function () {
|
||||
assertIsAboutPromise(this);
|
||||
return asserter.apply(this, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
function method(name, asserter) {
|
||||
utils.addMethod(Assertion.prototype, name, function () {
|
||||
assertIsAboutPromise(this);
|
||||
return asserter.apply(this, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
function notify(promise, callback) {
|
||||
return promise.then(function () { callback(); }, callback);
|
||||
}
|
||||
|
||||
function addNotifyMethod(extensiblePromise) {
|
||||
extensiblePromise.notify = function (callback) {
|
||||
return notify(extensiblePromise, callback);
|
||||
};
|
||||
}
|
||||
|
||||
function fulfilledAsserter() {
|
||||
/*jshint validthis:true */
|
||||
var assertion = this;
|
||||
|
||||
var transformedPromise = assertion._obj.then(
|
||||
function (value) {
|
||||
if (utils.flag(assertion, "negate")) {
|
||||
// If we're negated, `this.assert`'s behavior is actually flipped, so `this.assert(true, ...)` will
|
||||
// throw an error, as desired.
|
||||
assertion.assert(true, null, "expected promise to be rejected but it was fulfilled with " +
|
||||
utils.inspect(value));
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
function (reason) {
|
||||
// If we're in a negated state (i.e. `.not.fulfilled`) then this assertion will get flipped and thus
|
||||
// pass, as desired.
|
||||
assertion.assert(false, "expected promise to be fulfilled but it was rejected with " +
|
||||
utils.inspect(reason));
|
||||
}
|
||||
);
|
||||
|
||||
return makeAssertionPromise(transformedPromise, assertion);
|
||||
}
|
||||
|
||||
function rejectedAsserter() {
|
||||
// THIS SHIT IS COMPLICATED. Best illustrated by exhaustive example.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// `fulfilledPromise.should.be.rejected`:
|
||||
// `onOriginalFulfilled` → `this.assert(false, …)` throws → rejects
|
||||
// `fulfilledPromise.should.not.be.rejected`:
|
||||
// `onOriginalFulfilled` → `this.assert(false, …)` does nothing → fulfills
|
||||
// `rejectedPromise.should.be.rejected`:
|
||||
// `onOriginalRejected` does nothing relevant → fulfills
|
||||
// `rejectedPromise.should.not.be.rejected`:
|
||||
// `onOriginalRejected` → `this.assert(true, …)` throws → rejects
|
||||
// `rejectedPromise.should.be.rejected.with(xxx)`:
|
||||
// `onOriginalRejected` saves `rejectionReason` → fulfills →
|
||||
// `with(xxx)` called → `onTransformedFulfilled` → assert about xxx → fulfills/rejects based on asserts
|
||||
// `rejectedPromise.should.not.be.rejected.with(xxx)`:
|
||||
// `onOriginalRejected` saves `rejectionReason`, `this.assert(true, …)` throws → rejects →
|
||||
// `with(xxx)` called → `onTransformedRejected` → assert about xxx → fulfills/rejects based on asserts
|
||||
// `fulfilledPromise.should.be.rejected.with(xxx)`:
|
||||
// `onOriginalFulfilled` → `this.assert(false, …)` throws → rejects →
|
||||
// `with(xxx)` called → `onTransformedRejected` → `this.assert(false, …)` throws → rejected
|
||||
// `fulfilledPromise.should.not.be.rejected.with(xxx)`:
|
||||
// `onOriginalFulfilled` → `this.assert(false, …)` does nothing → fulfills →
|
||||
// `with(xxx)` called → `onTransformedFulfilled` → fulfills
|
||||
|
||||
/*jshint validthis:true */
|
||||
var assertion = this;
|
||||
var rejectionReason = null;
|
||||
|
||||
function onOriginalFulfilled(value) {
|
||||
assertion.assert(false, "expected promise to be rejected but it was fulfilled with " + utils.inspect(value));
|
||||
}
|
||||
|
||||
function onOriginalRejected(reason) {
|
||||
// Store the reason so that `with` can look at it later. Be sure to do this before asserting, since
|
||||
// throwing an error from the assert would cancel the process.
|
||||
rejectionReason = reason;
|
||||
|
||||
if (utils.flag(assertion, "negate")) {
|
||||
assertion.assert(true, null, "expected promise to be fulfilled but it was rejected with " +
|
||||
utils.inspect(reason));
|
||||
}
|
||||
|
||||
// If we didn't throw from the assert, transform rejections into fulfillments, by not re-throwing the
|
||||
// reason.
|
||||
}
|
||||
|
||||
function withMethod(Constructor, message) {
|
||||
var desiredReason = null;
|
||||
|
||||
if (Constructor instanceof RegExp || typeof Constructor === "string") {
|
||||
message = Constructor;
|
||||
Constructor = null;
|
||||
} else if (Constructor && Constructor instanceof Error) {
|
||||
desiredReason = Constructor;
|
||||
Constructor = null;
|
||||
message = null;
|
||||
}
|
||||
|
||||
var messageVerb = null;
|
||||
var messageIsGood = null;
|
||||
|
||||
if (message instanceof RegExp) {
|
||||
messageVerb = "matching";
|
||||
messageIsGood = function () {
|
||||
return message.test(rejectionReason.message);
|
||||
};
|
||||
} else {
|
||||
messageVerb = "including";
|
||||
messageIsGood = function () {
|
||||
return rejectionReason.message.indexOf(message) !== -1;
|
||||
};
|
||||
}
|
||||
|
||||
function constructorIsGood() {
|
||||
return rejectionReason instanceof Constructor;
|
||||
}
|
||||
|
||||
function matchesDesiredReason() {
|
||||
return rejectionReason === desiredReason;
|
||||
}
|
||||
|
||||
function onTransformedFulfilled() {
|
||||
if (!utils.flag(assertion, "negate")) {
|
||||
if (desiredReason) {
|
||||
assertion.assert(matchesDesiredReason(),
|
||||
null,
|
||||
"expected promise to be rejected with " + utils.inspect(desiredReason) + " but " +
|
||||
"it was rejected with " + utils.inspect(rejectionReason));
|
||||
}
|
||||
|
||||
if (Constructor) {
|
||||
assertion.assert(constructorIsGood(),
|
||||
"expected promise to be rejected with " + Constructor.prototype.name + " but it " +
|
||||
"was rejected with " + utils.inspect(rejectionReason));
|
||||
}
|
||||
|
||||
if (message) {
|
||||
assertion.assert(messageIsGood(),
|
||||
"expected promise to be rejected with an error " + messageVerb + " " + message +
|
||||
" but got " + utils.inspect(rejectionReason.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onTransformedRejected() {
|
||||
if (utils.flag(assertion, "negate")) {
|
||||
if (desiredReason) {
|
||||
assertion.assert(matchesDesiredReason(),
|
||||
null,
|
||||
"expected promise to not be rejected with " + utils.inspect(desiredReason));
|
||||
}
|
||||
|
||||
if (Constructor) {
|
||||
assertion.assert(constructorIsGood(),
|
||||
null,
|
||||
"expected promise to not be rejected with " + Constructor.prototype.name);
|
||||
}
|
||||
|
||||
if (message) {
|
||||
assertion.assert(messageIsGood(),
|
||||
null,
|
||||
"expected promise to be not be rejected with an error " + messageVerb + " " +
|
||||
message);
|
||||
}
|
||||
} else {
|
||||
if (desiredReason) {
|
||||
assertion.assert(false,
|
||||
"expected promise to be rejected with " + utils.inspect(desiredReason) +
|
||||
" but it was fulfilled");
|
||||
}
|
||||
|
||||
if (Constructor) {
|
||||
assertion.assert(false, "expected promise to be rejected with " + Constructor.prototype.name +
|
||||
" but it was fulfilled");
|
||||
}
|
||||
|
||||
if (message) {
|
||||
assertion.assert(false, "expected promise to be rejected with an error " + messageVerb + " " +
|
||||
message + " but it was fulfilled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return makeAssertionPromise(
|
||||
transformedPromise.then(onTransformedFulfilled, onTransformedRejected),
|
||||
assertion
|
||||
);
|
||||
}
|
||||
|
||||
var derivedPromise = assertion._obj.then(onOriginalFulfilled, onOriginalRejected);
|
||||
var transformedPromise = makeAssertionPromise(derivedPromise, assertion);
|
||||
Object.defineProperty(transformedPromise, "with", { enumerable: true, configurable: true, value: withMethod });
|
||||
|
||||
return transformedPromise;
|
||||
}
|
||||
|
||||
function isChaiAsPromisedAsserter(asserterName) {
|
||||
return ["fulfilled", "rejected", "broken", "eventually", "become"].indexOf(asserterName) !== -1;
|
||||
}
|
||||
|
||||
function makeAssertionPromiseToDoAsserter(currentAssertionPromise, previousAssertionPromise, doAsserter) {
|
||||
var promiseToDoAsserter = currentAssertionPromise.then(function (fulfillmentValue) {
|
||||
// The previous assertion promise might have picked up some flags while waiting for fulfillment.
|
||||
utils.transferFlags(previousAssertionPromise, currentAssertionPromise);
|
||||
|
||||
// Replace the object flag with the fulfillment value, so that doAsserter can operate properly.
|
||||
utils.flag(currentAssertionPromise, "object", fulfillmentValue);
|
||||
|
||||
// Perform the actual asserter action and return the result of it.
|
||||
return doAsserter();
|
||||
});
|
||||
return makeAssertionPromise(promiseToDoAsserter, currentAssertionPromise);
|
||||
}
|
||||
|
||||
function makeAssertionPromise(promise, baseAssertion) {
|
||||
// An assertion-promise is an (extensible!) promise with the following additions:
|
||||
var assertionPromise = Object.create(promise);
|
||||
|
||||
// 1. A `notify` method.
|
||||
addNotifyMethod(assertionPromise);
|
||||
|
||||
// 2. An `assert` method that acts exactly as it would on an assertion. This is called by promisified
|
||||
// asserters after the promise fulfills.
|
||||
assertionPromise.assert = function () {
|
||||
return Assertion.prototype.assert.apply(assertionPromise, arguments);
|
||||
};
|
||||
|
||||
// 3. Chai asserters, which act upon the promise's fulfillment value.
|
||||
var asserterNames = Object.getOwnPropertyNames(Assertion.prototype);
|
||||
asserterNames.forEach(function (asserterName) {
|
||||
// We already added `notify` and `assert`; don't mess with those.
|
||||
if (asserterName === "notify" || asserterName === "assert") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only add asserters for other libraries; poison-pill Chai as Promised ones.
|
||||
if (isChaiAsPromisedAsserter(asserterName)) {
|
||||
utils.addProperty(assertionPromise, asserterName, function () {
|
||||
throw new Error("Cannot use Chai as Promised asserters more than once in an assertion.");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// The asserter will need to be added differently depending on its type. In all cases we use
|
||||
// `makeAssertionPromiseToDoAsserter`, which, given this current `assertionPromise` we are going to
|
||||
// return, plus the `baseAssertion` we are basing it off of, will return a new assertion-promise that
|
||||
// builds off of `assertionPromise` and `baseAssertion` to perform the actual asserter action upon
|
||||
// fulfillment.
|
||||
var propertyDescriptor = Object.getOwnPropertyDescriptor(Assertion.prototype, asserterName);
|
||||
|
||||
if (typeof propertyDescriptor.value === "function") {
|
||||
// Case 1: simple method asserters
|
||||
utils.addMethod(assertionPromise, asserterName, function () {
|
||||
var args = arguments;
|
||||
|
||||
return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
|
||||
return propertyDescriptor.value.apply(assertionPromise, args);
|
||||
});
|
||||
});
|
||||
} else if (typeof propertyDescriptor.get === "function") {
|
||||
// Case 2: property asserters. These break down into two subcases: chainable methods, and pure
|
||||
// properties. An example of the former is `a`/`an`: `.should.be.an.instanceOf` vs.
|
||||
// `should.be.an("object")`.
|
||||
var isChainableMethod = false;
|
||||
try {
|
||||
isChainableMethod = typeof propertyDescriptor.get.call({}) === "function";
|
||||
} catch (e) { }
|
||||
|
||||
if (isChainableMethod) {
|
||||
// Case 2A: chainable methods. Recreate the chainable method, but operating on the augmented
|
||||
// promise. We need to copy both the assertion behavior and the chaining behavior, since the
|
||||
// chaining behavior might for example set flags on the object.
|
||||
utils.addChainableMethod(
|
||||
assertionPromise,
|
||||
asserterName,
|
||||
function () {
|
||||
var args = arguments;
|
||||
|
||||
return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
|
||||
// Due to https://github.com/chaijs/chai/commit/514dd6ce466d7b4110b38345e4073d586c017f3f
|
||||
// we can't use `propertyDescriptor.get().apply`.
|
||||
return Function.prototype.apply.call(propertyDescriptor.get(), assertionPromise, args);
|
||||
});
|
||||
},
|
||||
function () {
|
||||
// As above.
|
||||
return Function.prototype.call.call(propertyDescriptor.get, assertionPromise);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Case 2B: pure property case
|
||||
utils.addProperty(assertionPromise, asserterName, function () {
|
||||
return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
|
||||
return propertyDescriptor.get.call(assertionPromise);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return assertionPromise;
|
||||
}
|
||||
|
||||
property("fulfilled", fulfilledAsserter);
|
||||
property("rejected", rejectedAsserter);
|
||||
property("broken", rejectedAsserter);
|
||||
|
||||
property("eventually", function () {
|
||||
return makeAssertionPromise(this._obj, this);
|
||||
});
|
||||
|
||||
method("become", function (value) {
|
||||
return this.eventually.eql(value);
|
||||
});
|
||||
|
||||
method("notify", function (callback) {
|
||||
return notify(this._obj, callback);
|
||||
});
|
||||
|
||||
// Now use the Assertion framework to build an `assert` interface.
|
||||
var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
|
||||
return typeof assert[propName] === "function";
|
||||
});
|
||||
|
||||
assert.isFulfilled = function (promise, message) {
|
||||
return (new Assertion(promise, message)).to.be.fulfilled;
|
||||
};
|
||||
|
||||
assert.isRejected = assert.isBroken = function (promise, toTestAgainst, message) {
|
||||
if (typeof toTestAgainst === "string") {
|
||||
message = toTestAgainst;
|
||||
toTestAgainst = null;
|
||||
}
|
||||
|
||||
var shouldBeRejectedPromise = (new Assertion(promise, message)).to.be.rejected;
|
||||
|
||||
// Use `['with']` to handle crappy non-ES5 environments like PhantomJS.
|
||||
return toTestAgainst ? shouldBeRejectedPromise['with'](toTestAgainst) : shouldBeRejectedPromise;
|
||||
};
|
||||
|
||||
assert.eventually = {};
|
||||
originalAssertMethods.forEach(function (assertMethodName) {
|
||||
assert.eventually[assertMethodName] = function (promise) {
|
||||
var otherArgs = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
var promiseToAssert = promise.then(function (fulfillmentValue) {
|
||||
return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
|
||||
});
|
||||
|
||||
var augmentedPromiseToAssert = Object.create(promiseToAssert);
|
||||
addNotifyMethod(augmentedPromiseToAssert);
|
||||
return augmentedPromiseToAssert;
|
||||
};
|
||||
});
|
||||
|
||||
assert.becomes = function (promise, value) {
|
||||
return assert.eventually.deepEqual(promise, value);
|
||||
};
|
||||
|
||||
assert.doesNotBecome = function (promise, value) {
|
||||
return assert.eventually.notDeepEqual(promise, value);
|
||||
};
|
||||
}));
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,231 @@
|
|||
@charset "utf-8";
|
||||
|
||||
body {
|
||||
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
padding: 60px 50px;
|
||||
}
|
||||
|
||||
#mocha ul, #mocha li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#mocha ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#mocha h1, #mocha h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#mocha h1 {
|
||||
margin-top: 15px;
|
||||
font-size: 1em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
#mocha h1 a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#mocha h1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#mocha .suite .suite h1 {
|
||||
margin-top: 0;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#mocha .suite {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#mocha .test {
|
||||
margin-left: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#mocha .test.pending:hover h2::after {
|
||||
content: '(pending)';
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
#mocha .test.pass.medium .duration {
|
||||
background: #C09853;
|
||||
}
|
||||
|
||||
#mocha .test.pass.slow .duration {
|
||||
background: #B94A48;
|
||||
}
|
||||
|
||||
#mocha .test.pass::before {
|
||||
content: '✓';
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #00d6b2;
|
||||
}
|
||||
|
||||
#mocha .test.pass .duration {
|
||||
font-size: 9px;
|
||||
margin-left: 5px;
|
||||
padding: 2px 5px;
|
||||
color: white;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-ms-border-radius: 5px;
|
||||
-o-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#mocha .test.pass.fast .duration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha .test.pending {
|
||||
color: #0b97c4;
|
||||
}
|
||||
|
||||
#mocha .test.pending::before {
|
||||
content: '◦';
|
||||
color: #0b97c4;
|
||||
}
|
||||
|
||||
#mocha .test.fail {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test.fail pre {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#mocha .test.fail::before {
|
||||
content: '✖';
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test pre.error {
|
||||
color: #c00;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#mocha .test pre {
|
||||
display: block;
|
||||
float: left;
|
||||
clear: left;
|
||||
font: 12px/1.5 monaco, monospace;
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
border-bottom-color: #ddd;
|
||||
-webkit-border-radius: 3px;
|
||||
-webkit-box-shadow: 0 1px 3px #eee;
|
||||
-moz-border-radius: 3px;
|
||||
-moz-box-shadow: 0 1px 3px #eee;
|
||||
}
|
||||
|
||||
#mocha .test h2 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#mocha .test a.replay {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 0;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
background: #eee;
|
||||
font-size: 15px;
|
||||
-moz-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
-webkit-transition: opacity 200ms;
|
||||
-moz-transition: opacity 200ms;
|
||||
transition: opacity 200ms;
|
||||
opacity: 0.3;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#mocha .test:hover a.replay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#mocha-report.pass .test.fail {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha-report.fail .test.pass {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha-error {
|
||||
color: #c00;
|
||||
font-size: 1.5 em;
|
||||
font-weight: 100;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
#mocha-stats {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
right: 10px;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#mocha-stats .progress {
|
||||
float: right;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#mocha-stats em {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#mocha-stats a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#mocha-stats a:hover {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#mocha-stats li {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
list-style: none;
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
code .comment { color: #ddd }
|
||||
code .init { color: #2F6FAD }
|
||||
code .string { color: #5890AD }
|
||||
code .keyword { color: #8A6343 }
|
||||
code .number { color: #2F6FAD }
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,141 @@
|
|||
(function (mochaAsPromised) {
|
||||
"use strict";
|
||||
|
||||
// Module systems magic dance.
|
||||
|
||||
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
|
||||
// Node.js: plug in automatically, if no argument is provided. This is a good idea since one can run Mocha tests
|
||||
// using the Mocha test runner from either a locally-installed package, or from a globally-installed one.
|
||||
// In the latter case, naively plugging in `require("mocha")` would end up duck-punching the wrong instance,
|
||||
// so we provide this shortcut to auto-detect which Mocha package needs to be duck-punched.
|
||||
module.exports = function (mocha) {
|
||||
if (!mocha) {
|
||||
if (typeof process === "object" && Object.prototype.toString.call(process) === "[object process]") {
|
||||
// We're in *real* Node.js, not in a browserify-like environment. Do automatic detection logic.
|
||||
var path = require("path");
|
||||
|
||||
// `process.argv[1]` is either something like `"/host/package/node_modules/mocha/bin/_mocha`", or
|
||||
// `"/path/to/global/node_modules/mocha/bin/_mocha"`. Verify that, though:
|
||||
var lastThreeSegments = process.argv[1].split(path.sep).slice(-3);
|
||||
if (lastThreeSegments[0] !== "mocha" || lastThreeSegments[1] !== "bin") {
|
||||
throw new Error("Attempted to automatically plug in to Mocha, but was not running through " +
|
||||
"the Mocha test runner. Either run using the Mocha command-line test runner, " +
|
||||
"or plug in manually by passing the running Mocha module.");
|
||||
}
|
||||
|
||||
var mochaPath = path.resolve(process.argv[1], "../..");
|
||||
mocha = (require)(mochaPath); // Trick browserify into not complaining.
|
||||
} else if (typeof Mocha !== "undefined") {
|
||||
// We're in a browserify-like emulation environment. Try the `Mocha` global.
|
||||
mocha = Mocha;
|
||||
} else {
|
||||
throw new Error("Attempted to automatically plug in to Mocha, but could not detect the " +
|
||||
"environment. Plug in manually by passing the running Mocha module.");
|
||||
}
|
||||
}
|
||||
|
||||
mochaAsPromised(mocha);
|
||||
};
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(function () {
|
||||
return mochaAsPromised;
|
||||
});
|
||||
} else {
|
||||
// Other environment (usually <script> tag): plug in global `Mocha` directly and automatically.
|
||||
mochaAsPromised(Mocha);
|
||||
}
|
||||
}((function () {
|
||||
"use strict";
|
||||
|
||||
function isPromise(x) {
|
||||
return typeof x === "object" && x !== null && typeof x.then === "function";
|
||||
}
|
||||
|
||||
var duckPunchedAlready = false;
|
||||
|
||||
return function mochaAsPromised(mocha) {
|
||||
if (duckPunchedAlready) {
|
||||
return;
|
||||
}
|
||||
duckPunchedAlready = true;
|
||||
|
||||
// Soooo this is an awesome hack.
|
||||
|
||||
// Here's the idea: Mocha `Runnable` instances have a `fn` property, representing the test to run. Async tests
|
||||
// in Mocha are done with `fn`s that take a `done` callback, and call it with either nothing (success) or an
|
||||
// error (failure). We want to add another paradigm for async tests: `fn`s that take no arguments, but return a
|
||||
// promise. Promise fulfillment corresponds to success, and rejection to failure.
|
||||
|
||||
// To do this, we translate promise-returning `fn`s into callback-calling ones. So Mocha never sees the promisey
|
||||
// functions, but instead sees a wrapper around them that we provide. The only trick is, how and when to insert
|
||||
// this wrapper into a Mocha `Runnable`?
|
||||
|
||||
// We accomplish this by intercepting all [[Put]]s to the `fn` property of *any* `Runnable`. That is, we define
|
||||
// a setter for `fn` on `Runnable.prototype` (!). So when Mocha sets the `fn` property of a runnable inside the
|
||||
// `Runnable` constructor (i.e. `this.fn = fn`), it is immediately translated into a wrapped version, which is
|
||||
// then stored as a `_wrappedFn` instance property. Finally we define a getter for `fn` on `Runnable.prototype`
|
||||
// as well, so that any retrievals of the `fn` property (e.g. in `Runnable.prototype.run`) return the wrapped
|
||||
// version we've stored.
|
||||
|
||||
// We also need to override the `async` property, since the Mocha constructor sets it by looking at the `fn`
|
||||
// passed in, and not at the `this.fn` property we have control over. We just give it a getter that performs the
|
||||
// same logic as the Mocha constructor, and a no-op setter (so that it silently ignores Mocha's attempts to set
|
||||
// it).
|
||||
|
||||
// ISN'T THIS COOL!?
|
||||
|
||||
Object.defineProperties(mocha.Runnable.prototype, {
|
||||
fn: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return this._wrappedFn;
|
||||
},
|
||||
set: function (fn) {
|
||||
this._wrappedFn = function (done) {
|
||||
// Run the original `fn`, passing along `done` for the case in which it's callback-asynchronous.
|
||||
// Make sure to forward the `this` context, since you can set variables and stuff on it to share
|
||||
// within a suite.
|
||||
var retVal = fn.call(this, done);
|
||||
|
||||
if (isPromise(retVal)) {
|
||||
// If we get a promise back...
|
||||
retVal.then(
|
||||
function () {
|
||||
// On fulfillment, ignore the fulfillment value and call `done()` with no arguments.
|
||||
done();
|
||||
},
|
||||
function (reason) {
|
||||
// On rejection, make sure there's a rejection reason, then call `done` with it.
|
||||
if (reason === null || reason === undefined) {
|
||||
reason = new Error("Promise rejected with no rejection reason.");
|
||||
}
|
||||
done(reason);
|
||||
}
|
||||
);
|
||||
} else if (fn.length === 0) {
|
||||
// If `fn` is synchronous (i.e. didn't have a `done` parameter and didn't return a promise),
|
||||
// call `done` now. (If it's callback-asynchronous, `fn` will call `done` eventually since
|
||||
// we passed it in above.)
|
||||
done();
|
||||
}
|
||||
};
|
||||
this._wrappedFn.toString = function () {
|
||||
return fn.toString();
|
||||
};
|
||||
}
|
||||
},
|
||||
async: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return typeof this._wrappedFn === "function";
|
||||
},
|
||||
set: function () {
|
||||
// Ignore Mocha trying to set this; it doesn't know the whole picture.
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}())));
|
|
@ -0,0 +1 @@
|
|||
../../lib/q-0.8.12.js
|
|
@ -0,0 +1 @@
|
|||
../../lib/underscore.js
|
|
@ -0,0 +1,41 @@
|
|||
describe("Local sync", function() {
|
||||
this.timeout(20000);
|
||||
runUserSpec();
|
||||
});
|
||||
|
||||
|
||||
// exports.testCreateThenFetch = function(test) {
|
||||
// init(function() {
|
||||
// createUser({
|
||||
// callback: function(user) {
|
||||
// var u = new Gombot.User({ id: user.id, email: user.get("email") });
|
||||
// u.fetch({
|
||||
// password: TEST_PASSWORD,
|
||||
// success: function() {
|
||||
// test.pass();
|
||||
// test.done();
|
||||
// },
|
||||
// error: function(err) {
|
||||
// console.log("error:", err);
|
||||
// test.fail();
|
||||
// test.done();
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// password: TEST_PASSWORD
|
||||
// });
|
||||
// });
|
||||
// test.waitUntilDone();
|
||||
// };
|
||||
|
||||
// exports.testUpdateThenFetch = function(test) {
|
||||
// test.pass();
|
||||
// test.done();
|
||||
// };
|
||||
|
||||
// exports.testCreateThenDestroy = function(test) {
|
||||
// test.pass();
|
||||
// test.done();
|
||||
// };
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mocha Tests</title>
|
||||
<link rel="stylesheet" href="lib/mocha-1.8.1.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<script src="lib/underscore.js"></script>
|
||||
<script src="lib/q-0.8.12.js"></script>
|
||||
<script src="lib/chai.js"></script>
|
||||
<script src="lib/chai-as-promised.js"></script>
|
||||
<script src="lib/mocha-1.8.1.js"></script>
|
||||
<script src="lib/mocha-as-promised.js"></script>
|
||||
<script src="helpers.js"></script>
|
||||
<script src="runner.js"></script>
|
||||
<script src="user-spec.js"></script>
|
||||
<script src="local-sync-spec.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
var GombotTest;
|
||||
var SH;
|
||||
var should = chai.should();
|
||||
var assert = chai.assert;
|
||||
|
||||
mocha.setup('bdd');
|
||||
|
||||
// TODO: put some teardown code here to remove usersTest store, etc
|
||||
GombotTest = chrome.extension.getBackgroundPage()._Gombot();
|
||||
GombotTest.init({ storeName: "usersTest", callback: go, testing: true });
|
||||
SH = SpecHelpers(GombotTest);
|
||||
|
||||
function go() {
|
||||
mocha.run();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Jasmine Spec Runner</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="lib/jasmine-1.3.1/jasmine_favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css">
|
||||
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>
|
||||
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>
|
||||
|
||||
<!-- include source files here... -->
|
||||
|
||||
<!-- include spec files here... -->
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var jasmineEnv = jasmine.getEnv();
|
||||
jasmineEnv.updateInterval = 1000;
|
||||
|
||||
var htmlReporter = new jasmine.HtmlReporter();
|
||||
|
||||
jasmineEnv.addReporter(htmlReporter);
|
||||
|
||||
jasmineEnv.specFilter = function(spec) {
|
||||
return htmlReporter.specFilter(spec);
|
||||
};
|
||||
|
||||
var currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
execJasmine();
|
||||
};
|
||||
|
||||
function execJasmine() {
|
||||
jasmineEnv.execute();
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,37 @@
|
|||
function runUserSpec() {
|
||||
describe("User", function() {
|
||||
describe("#create", function() {
|
||||
var userPromise = null,
|
||||
testEmail = SH.generateTestEmail();
|
||||
before(function() {
|
||||
userPromise = SH.createUser({ password: SH.TEST_PASSWORD, email: testEmail });
|
||||
});
|
||||
|
||||
it("should create a model with the attributes used to create it", function() {
|
||||
return Q.all([
|
||||
userPromise.should.eventually.be.an("object"),
|
||||
userPromise.should.eventually.have.deep.property("attributes.email", testEmail),
|
||||
userPromise.should.eventually.have.deep.property("attributes.pin", SH.TEST_PIN)
|
||||
]);
|
||||
});
|
||||
|
||||
it("should create a record for the model in local storage", function() {
|
||||
var user;
|
||||
var lsAttrs;
|
||||
var lsPromise = userPromise.then(function(u) { user = u; return SH.getLocalStorageItem(SH.STORE_NAME); });
|
||||
lsPromise.then(function(attrs) { lsAttrs = attrs; });
|
||||
lsPromise.should.eventually.satisfy(function (lsAttrs) { console.log(user, lsAttrs); return true; } );
|
||||
return lsPromise;
|
||||
// userPromise.should.eventually.satisfy(function(user) {
|
||||
// var indexOfUserRecord = -1;
|
||||
// runs(function() {
|
||||
// LocalStorage.getItem(self.STORE_NAME, function(store) {
|
||||
// var records = (store && store.split(",")) || [];
|
||||
// indexOfUserRecord = records.indexOf(user.id) >= 0
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Загрузка…
Ссылка в новой задаче