Skip to content
This repository has been archived by the owner on Mar 14, 2020. It is now read-only.

Commit

Permalink
Add create, update and destroy methods on the AJAX Adapter.
Browse files Browse the repository at this point in the history
  • Loading branch information
haydn committed Sep 3, 2015
1 parent fc4276b commit c87cba8
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 77 deletions.
10 changes: 8 additions & 2 deletions spec/adapters/ajax/create-spec.js
Expand Up @@ -2,7 +2,7 @@ import test from "tape-catch";
import sinon from "sinon";
import Store from "../../../src/store";

test.skip("create must post a resource to the server and add it to the store", function (t) {
test("create must post a resource to the server and add it to the store on success", function (t) {
var server = sinon.fakeServer.create({ autoRespond: true });
var adapter = new Store.AjaxAdapter();
var store = new Store(adapter);
Expand All @@ -26,9 +26,15 @@ test.skip("create must post a resource to the server and add it to the store", f
}
})
]);
store.create("products", { title: "My Book" }, function (product) {
store.create({ type: "products", title: "My Book" }, function (product) {
t.equal(product.title, "My Book");
t.equal(store.find("products", "9").title, "My Book");
});
server.restore();
});

test.skip("must throw an error if resource is missing a 'type' property");

test.skip("must call the error callback if an undefined type is included");

test.skip("must call the error callback if the server responds with a non-2xx code");
30 changes: 28 additions & 2 deletions spec/adapters/ajax/destroy-spec.js
@@ -1,2 +1,28 @@
// import test from "tape-catch";
// import Store from "../../../src/store";
import test from "tape-catch";
import sinon from "sinon";
import Store from "../../../src/store";

test("destroy must delete a resource from the server and remove it from the store on success", function (t) {
var server = sinon.fakeServer.create({ autoRespond: true });
var adapter = new Store.AjaxAdapter();
var store = new Store(adapter);
t.plan(2);
t.timeoutAfter(1000);
store.define("products", {});
server.respondWith("DELETE", "/products/6", [
204,
{
"Content-Type": "application/vnd.api+json"
},
""
]);
store.add({
type: "products",
id: "6"
});
t.equal(store.find("products").length, 1);
store.destroy(store.find("products", "6"), function () {
t.equal(store.find("products").length, 0);
});
server.restore();
});
41 changes: 38 additions & 3 deletions spec/adapters/ajax/load-spec.js
Expand Up @@ -6,11 +6,15 @@ test("load must fetch a single resource from the server and add it to the store"
var server = sinon.fakeServer.create({ autoRespond: true });
var adapter = new Store.AjaxAdapter();
var store = new Store(adapter);
t.plan(2);
t.plan(5);
t.timeoutAfter(1000);
store.define("products", {
title: Store.attr()
title: Store.attr(),
category: Store.hasOne(),
comments: Store.hasMany()
});
store.define("categories", {});
store.define("comments", {});
server.respondWith("GET", "/products/12", [
200,
{
Expand All @@ -22,13 +26,36 @@ test("load must fetch a single resource from the server and add it to the store"
id: "12",
attributes: {
title: "An Awesome Book"
},
relationships: {
category: {
data: {
id: "6",
type: "categories"
}
},
comments: {
data: [
{
id: "2",
type: "comments"
},
{
id: "4",
type: "comments"
}
]
}
}
}
})
]);
store.load("products", "12", function (product) {
t.equal(product.title, "An Awesome Book");
t.equal(store.find("products", "12"), product);
t.equal(store.find("products", "12").title, "An Awesome Book");
t.equal(store.find("products", "12").category, store.find("categories", "6"));
t.deepEqual(store.find("products", "12").comments.map(c => c.id).sort(), [ "2", "4" ]);
t.deepEqual(store.find("products", "12").comments.map(c => c.type).sort(), [ "comments", "comments" ]);
});
server.restore();
});
Expand Down Expand Up @@ -77,6 +104,14 @@ test("load must fetch a collection of resources from the server and add them to
t.equal(products.length, 3);
t.equal(store.find("products").length, 3);
t.deepEqual(store.find("products").map(a => a.title).sort(), [ "A Book", "B Book", "C Book" ]);
}, function (error) {
t.fail(error);
});
server.restore();
});

test.skip("must call the error callback if an undefined type is included");

test.skip("must call the error callback if the server responds with a non-2xx code");

test.skip("must return a promise if no callbacks are provided");
35 changes: 33 additions & 2 deletions spec/adapters/ajax/update-spec.js
@@ -1,2 +1,33 @@
// import test from "tape-catch";
// import Store from "../../../src/store";
import test from "tape-catch";
import sinon from "sinon";
import Store from "../../../src/store";

test("update must update a resource on the server and add reflect the changes in the store on success", function (t) {
var server = sinon.fakeServer.create({ autoRespond: true });
var adapter = new Store.AjaxAdapter();
var store = new Store(adapter);
t.plan(2);
t.timeoutAfter(1000);
store.define("products", {
title: Store.attr()
});
server.respondWith("PATCH", "/products/9", [
204,
{
"Content-Type": "application/vnd.api+json"
},
""
]);
store.add({
type: "products",
id: "9",
attributes: {
title: "My Book"
}
});
store.update({ type: "products", id: "9", title: "My Book!" }, function (product) {
t.equal(product.title, "My Book!");
t.equal(store.find("products", "9").title, "My Book!");
});
server.restore();
});
15 changes: 1 addition & 14 deletions spec/client.js
Expand Up @@ -2,17 +2,4 @@ import "./adapters/ajax/create-spec";
import "./adapters/ajax/destroy-spec";
import "./adapters/ajax/load-spec";
import "./adapters/ajax/update-spec";
import "./store/clud/create-spec";
import "./store/clud/destroy-spec";
import "./store/clud/load-spec";
import "./store/clud/update-spec";
import "./store/core/add-spec";
import "./store/core/define-spec";
import "./store/core/find-spec";
import "./store/core/push-spec";
import "./store/core/remove-spec";
import "./store/events/off-spec";
import "./store/events/on-spec";
import "./store/fields/attr-spec";
import "./store/fields/has-many-spec";
import "./store/fields/has-one-spec";
import "./shared";
15 changes: 1 addition & 14 deletions spec/server.js
@@ -1,14 +1 @@
import "./store/clud/create-spec";
import "./store/clud/destroy-spec";
import "./store/clud/load-spec";
import "./store/clud/update-spec";
import "./store/core/add-spec";
import "./store/core/define-spec";
import "./store/core/find-spec";
import "./store/core/push-spec";
import "./store/core/remove-spec";
import "./store/events/off-spec";
import "./store/events/on-spec";
import "./store/fields/attr-spec";
import "./store/fields/has-many-spec";
import "./store/fields/has-one-spec";
import "./shared";
15 changes: 15 additions & 0 deletions spec/shared.js
@@ -0,0 +1,15 @@
import "./store/clud/create-spec";
import "./store/clud/destroy-spec";
import "./store/clud/load-spec";
import "./store/clud/update-spec";
import "./store/core/add-spec";
import "./store/core/convert-spec";
import "./store/core/define-spec";
import "./store/core/find-spec";
import "./store/core/push-spec";
import "./store/core/remove-spec";
import "./store/events/off-spec";
import "./store/events/on-spec";
import "./store/fields/attr-spec";
import "./store/fields/has-many-spec";
import "./store/fields/has-one-spec";
4 changes: 2 additions & 2 deletions spec/store/clud/create-spec.js
Expand Up @@ -17,7 +17,7 @@ test("create must call the create method prodvided by the adapter", function (t)
var cb = function () {};
t.plan(2);
t.doesNotThrow(function () {
store.create("foo", a, cb);
store.create(a, cb);
}, "should not throw an error");
t.ok(adatper.create.calledWith(store, "foo", a, cb), "should call adapter with the same params");
t.ok(adatper.create.calledWith(store, a, cb), "should call adapter with the same params");
});
5 changes: 3 additions & 2 deletions spec/store/clud/destroy-spec.js
Expand Up @@ -13,10 +13,11 @@ test("destroy must throw an error if it is called when there isn't an adapter",
test("destroy must call the destroy method prodvided by the adapter", function (t) {
var adatper = { destroy: sinon.spy() };
var store = new Store(adatper);
var a = {};
var cb = function () {};
t.plan(2);
t.doesNotThrow(function () {
store.destroy("foo", "1", cb);
store.destroy(a, cb);
}, "should not throw an error");
t.ok(adatper.destroy.calledWith(store, "foo", "1", cb), "should call adapter with the same params");
t.ok(adatper.destroy.calledWith(store, a, cb), "should call adapter with the same params");
});
4 changes: 2 additions & 2 deletions spec/store/clud/update-spec.js
Expand Up @@ -17,7 +17,7 @@ test("update must call the update method prodvided by the adapter", function (t)
var cb = function () {};
t.plan(2);
t.doesNotThrow(function () {
store.update("foo", "1", a, cb);
store.update(a, cb);
}, "should not throw an error");
t.ok(adatper.update.calledWith(store, "foo", "1", a, cb), "should call adapter with the same params");
t.ok(adatper.update.calledWith(store, a, cb), "should call adapter with the same params");
});
25 changes: 25 additions & 0 deletions spec/store/core/convert-spec.js
@@ -0,0 +1,25 @@
import test from "tape-catch";
import sinon from "sinon";
import Store from "../../../src/store";

test("convert must use serialize functions provided by type definitions", function (t) {
var store = new Store();
var serialize = sinon.spy(function (resource, data) {
data.attributes.title = resource.title + "!";
});
var resource = {
type: "products",
id: "44",
title: "Example"
};
t.plan(3);
store.define("products", {
title: {
serialize: serialize
}
});
store.convert(resource).attributes.title
t.equal(serialize.firstCall.args[0], resource);
t.deepEqual(serialize.firstCall.args[1], { type: "products", id: "44", attributes: { title: "Example!" }, relationships: {} });
t.equal(serialize.firstCall.args[2], "title");
});
8 changes: 8 additions & 0 deletions spec/store/core/define-spec.js
Expand Up @@ -36,3 +36,11 @@ test("define must throw an error if you try to define a type that has already be
store.define([ "sample", "example"], {});
}, /The type 'example' has already been defined\./);
});

test("define must throw an error if you try to define a type that without providing a definition", function (t) {
var store = new Store();
t.plan(1);
t.throws(function () {
store.define("example");
}, /You must provide a definition for the type 'example'\./);
});
39 changes: 38 additions & 1 deletion spec/store/fields/attr-spec.js
Expand Up @@ -6,7 +6,7 @@ test("attr must return the correct type attribute", function (t) {
t.equal(Store.attr().type, "attr");
});

test("attr must return a deserialize function that passes on a default option", function (t) {
test("attr must return a definition that has the default option passed", function (t) {
t.plan(2);
t.equal(Store.attr({ default: "foo" }).default, "foo");
t.equal(Store.attr("example", { default: "foo" }).default, "foo");
Expand Down Expand Up @@ -39,6 +39,43 @@ test("attr must return a deserialize function that maps to the key if no attribu
t.equal(field.deserialize.call(store, data, "title"), "Example");
});

test("attr must return a serialize function that maps to the attribute provided", function (t) {
var field = Store.attr("example-title");
var resource = {
"type": "products",
"id": "1",
"example-title": "Example"
};
var data = {
type: "products",
id: "1",
attributes: {},
relationships: {}
};
t.plan(1);
field.serialize.call(this, resource, data, "example-title");
t.equal(data.attributes["example-title"], "Example");
});

test("attr must return a serialize function that maps to the key if no attribute name is provided", function (t) {
var store = new Store();
var field = Store.attr();
var resource = {
"type": "products",
"id": "1",
"title": "Example"
};
var data = {
type: "products",
id: "1",
attributes: {},
relationships: {}
};
t.plan(1);
field.serialize.call(store, resource, data, "title");
t.equal(data.attributes["title"], "Example");
});

test("attr must return undefined when the attribute is missing from the data", function (t) {
var store = new Store();
var field = Store.attr("title");
Expand Down

0 comments on commit c87cba8

Please sign in to comment.