opds-web-client
Version:
457 lines (456 loc) • 25.7 kB
JavaScript
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
jest.autoMockOff();
jest.mock("../../DataFetcher");
var React = require("react");
var enzyme_1 = require("enzyme");
var Root_1 = require("../Root");
var Breadcrumbs_1 = require("../Breadcrumbs");
var Collection_1 = require("../Collection");
var UrlForm_1 = require("../UrlForm");
var BookDetails_1 = require("../BookDetails");
var SkipNavigationLink_1 = require("../SkipNavigationLink");
var CatalogLink_1 = require("../CatalogLink");
var Search_1 = require("../Search");
var LoadingIndicator_1 = require("../LoadingIndicator");
var ErrorMessage_1 = require("../ErrorMessage");
var BasicAuthForm_1 = require("../BasicAuthForm");
var collectionData_1 = require("./collectionData");
var routing_1 = require("./routing");
var setCollectionAndBookPromise = new Promise(function (resolve, reject) {
resolve({
collectionData: null,
bookData: null
});
});
var mockSetCollectionAndBook = jest.genMockFunction().mockReturnValue(setCollectionAndBookPromise);
describe("Root", function () {
it("shows skip navigation link", function () {
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, null));
var links = wrapper.find(SkipNavigationLink_1.default);
expect(links.length).toBe(1);
});
it("shows search and treats it as top-level", function () {
var collectionData = Object.assign({}, collectionData_1.ungroupedCollectionData, {
search: {
url: "test search url",
searchData: {
description: "description",
shortName: "shortName",
template: function (s) { return s; }
}
}
});
var fetchSearchDescription = function (url) { };
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData, fetchSearchDescription: fetchSearchDescription}));
var search = wrapper.find(Search_1.default);
expect(search.props().url).toBe(collectionData.search.url);
expect(search.props().searchData).toBe(collectionData.search.searchData);
expect(search.props().fetchSearchDescription).toBe(fetchSearchDescription);
});
it("shows a collection if props include collectionData", function () {
var collectionData = collectionData_1.groupedCollectionData;
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData}));
var collections = wrapper.find(Collection_1.default);
expect(collections.length).toBe(1);
expect(collections.first().props().collection).toBe(collectionData);
});
it("shows a url form if no collection url or book url", function () {
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, null));
var urlForms = wrapper.find(UrlForm_1.default);
expect(urlForms.length).toBe(1);
});
it("doesn't show a url form if collection url", function () {
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionUrl: "test", setCollectionAndBook: mockSetCollectionAndBook}));
var urlForms = wrapper.find(UrlForm_1.default);
expect(urlForms.length).toBe(0);
});
it("doesn't show a url form if book url", function () {
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {bookUrl: "test", setCollectionAndBook: mockSetCollectionAndBook}));
var urlForms = wrapper.find(UrlForm_1.default);
expect(urlForms.length).toBe(0);
});
it("fetches a collection url on mount", function () {
var collectionUrl = "http://feedbooks.github.io/opds-test-catalog/catalog/acquisition/blocks.xml";
var setCollectionAndBook = jest.genMockFunction().mockReturnValue(setCollectionAndBookPromise);
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionUrl: collectionUrl, setCollectionAndBook: setCollectionAndBook}));
expect(setCollectionAndBook.mock.calls.length).toBe(1);
expect(setCollectionAndBook.mock.calls[0][0]).toBe(collectionUrl);
expect(setCollectionAndBook.mock.calls[0][1]).toBeFalsy();
});
it("fetches a book url on mount", function () {
var bookUrl = "http://example.com/book";
var setCollectionAndBook = jest.genMockFunction().mockReturnValue(setCollectionAndBookPromise);
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {bookUrl: bookUrl, setCollectionAndBook: setCollectionAndBook}));
expect(setCollectionAndBook.mock.calls.length).toBe(1);
expect(setCollectionAndBook.mock.calls[0][0]).toBeFalsy();
expect(setCollectionAndBook.mock.calls[0][1]).toBe(bookUrl);
});
it("fetches loans on mount", function (done) {
var collectionUrl = "http://feedbooks.github.io/opds-test-catalog/catalog/acquisition/blocks.xml";
var setCollectionAndBook = jest.genMockFunction().mockImplementation(function (collectionUrl, bookUrl) {
return new Promise(function (resolve, reject) { return resolve({
collectionData: Object.assign({}, collectionData_1.ungroupedCollectionData, { shelfUrl: "loans url" }),
bookData: null
}); });
});
var fetchLoans = jest.genMockFunction();
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionUrl: collectionUrl, setCollectionAndBook: setCollectionAndBook, fetchLoans: fetchLoans, basicAuthCredentials: "credentials"}));
wrapper.instance().componentWillMount().then(function () {
var count = fetchLoans.mock.calls.length;
expect(fetchLoans.mock.calls[count - 1][0]).toBe("loans url");
done();
}).catch(done.fail);
});
it("updates page title on mount", function () {
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {pageTitleTemplate: function (collection, book) { return "page title"; }}));
expect(document.title).toBe("page title");
});
it("sets basic auth credentials on mount", function () {
var saveBasicAuthCredentials = jest.genMockFunction();
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {saveBasicAuthCredentials: saveBasicAuthCredentials, basicAuthCredentials: "credentials"}));
expect(saveBasicAuthCredentials.mock.calls.length).toBe(1);
expect(saveBasicAuthCredentials.mock.calls[0][0]).toBe("credentials");
});
it("fetches a collection url when updated", function () {
var elem = document.createElement("div");
var collectionUrl = "http://feedbooks.github.io/opds-test-catalog/catalog/acquisition/blocks.xml";
var newCollection = "new collection url";
var setCollectionAndBook = jest.genMockFunction().mockReturnValue(setCollectionAndBookPromise);
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionUrl: collectionUrl, setCollectionAndBook: setCollectionAndBook}));
wrapper.setProps({
collectionUrl: newCollection
});
expect(setCollectionAndBook.mock.calls.length).toBe(2);
expect(setCollectionAndBook.mock.calls[1][0]).toBe(newCollection);
expect(setCollectionAndBook.mock.calls[1][1]).toBeFalsy();
});
it("shows loading message", function () {
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {isFetching: true}));
var loadings = wrapper.find(LoadingIndicator_1.default);
expect(loadings.length).toBe(1);
});
it("shows error message", function () {
var fetchError = {
status: 500,
response: "test error",
url: "test error url"
};
var retry = jest.genMockFunction();
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {error: fetchError, retryCollectionAndBook: retry}));
var error = wrapper.find(ErrorMessage_1.default);
expect(error.props().message).toContain(fetchError.url);
expect(error.props().retry).toBe(retry);
});
it("shows basic auth form", function () {
var basicAuth = {
showForm: true,
credentials: "gibberish",
title: "Super Classified Archive",
loginLabel: "Clearance ID",
passwordLabel: "Access Key",
error: "Invalid Clearance ID and/or Access Key",
callback: jest.genMockFunction()
};
var saveBasicAuthCredentials = jest.genMockFunction();
var closeErrorAndHideBasicAuthForm = jest.genMockFunction();
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {basicAuth: basicAuth, saveBasicAuthCredentials: saveBasicAuthCredentials, closeErrorAndHideBasicAuthForm: closeErrorAndHideBasicAuthForm}));
var form = wrapper.find(BasicAuthForm_1.default);
var _a = form.props(), saveCredentials = _a.saveCredentials, hide = _a.hide, callback = _a.callback, title = _a.title, loginLabel = _a.loginLabel, passwordLabel = _a.passwordLabel, error = _a.error;
expect(saveCredentials).toBe(saveBasicAuthCredentials);
expect(hide).toBe(closeErrorAndHideBasicAuthForm);
expect(callback).toBe(basicAuth.callback);
expect(title).toBe(basicAuth.title);
expect(loginLabel).toBe(basicAuth.loginLabel);
expect(passwordLabel).toBe(basicAuth.passwordLabel);
expect(error).toBe(basicAuth.error);
});
it("shows book detail", function () {
var bookData = collectionData_1.groupedCollectionData.lanes[0].books[0];
var loans = [Object.assign({}, bookData, {
availability: { status: "availabile" }
})];
var borrowBook = jest.genMockFunction();
var fulfillBook = jest.genMockFunction();
var indirectFulfillBook = jest.genMockFunction();
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {bookData: bookData, loans: loans, borrowBook: borrowBook, fulfillBook: fulfillBook, indirectFulfillBook: indirectFulfillBook, isSignedIn: true}));
var bookWrapper = wrapper.find(".bookDetailsWrapper");
var book = wrapper.find(BookDetails_1.default);
expect(bookWrapper.length).toBe(1);
expect(book.props().book).toEqual(loans[0]);
expect(book.props().borrowBook).toBe(borrowBook);
expect(book.props().fulfillBook).toBe(fulfillBook);
expect(book.props().indirectFulfillBook).toBe(indirectFulfillBook);
expect(book.props().isSignedIn).toBe(true);
});
it("shows breadcrumbs", function () {
var history = [{
id: "2nd id",
text: "2nd title",
url: "2nd url"
}, {
id: "last id",
text: "last title",
url: "last url"
}];
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData_1.ungroupedCollectionData, history: history}));
var breadcrumbs = wrapper.find(Breadcrumbs_1.default);
var links = history.concat([{
url: collectionData_1.ungroupedCollectionData.url,
text: collectionData_1.ungroupedCollectionData.title
}]);
expect(breadcrumbs.props().links).toEqual(links);
});
it("uses custom computeBreadcrumbs function", function () {
var breadcrumb = {
url: "breacrumb url",
text: "breadcrumb text"
};
var computeBreadcrumbs = function (data) { return [breadcrumb]; };
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData_1.ungroupedCollectionData, computeBreadcrumbs: computeBreadcrumbs}));
var breadcrumbs = wrapper.find(Breadcrumbs_1.default);
expect(breadcrumbs.props().links).toEqual([breadcrumb]);
});
describe("provided a BookDetailsContainer", function () {
var Container = (function (_super) {
__extends(Container, _super);
function Container() {
_super.apply(this, arguments);
}
Container.prototype.render = function () {
return (React.createElement("div", {className: "container"}, this.props.children));
};
return Container;
}(React.Component));
it("renders BookDetailsContainer with urls, refresh, and book details", function () {
var bookData = collectionData_1.groupedCollectionData.lanes[0].books[0];
var refresh = jest.genMockFunction();
var borrowBook = jest.genMockFunction();
var fulfillBook = jest.genMockFunction();
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {bookData: bookData, bookUrl: bookData.url, collectionUrl: "test collection", refreshCollectionAndBook: refresh, setCollectionAndBook: mockSetCollectionAndBook, BookDetailsContainer: Container, borrowBook: borrowBook, fulfillBook: fulfillBook}));
var container = wrapper.find(Container);
var child = container.children().first();
expect(container.props().bookUrl).toBe(bookData.url);
expect(container.props().collectionUrl).toBe("test collection");
expect(container.props().refreshCatalog).toBe(refresh);
expect(container.props().book).toBe(bookData);
expect(child.type()).toBe(BookDetails_1.default);
expect(child.props().book).toBe(bookData);
});
it("does not render BookDetailsContainer if bookUrl and bookData.url are missing", function () {
var bookData = Object.assign({}, collectionData_1.groupedCollectionData.lanes[0].books[0], { url: null });
var refresh = jest.genMockFunction();
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {bookData: bookData, bookUrl: null, collectionUrl: "test collection", refreshCollectionAndBook: refresh, setCollectionAndBook: mockSetCollectionAndBook, BookDetailsContainer: Container}));
var containers = wrapper.find(Container);
expect(containers.length).toBe(0);
});
});
it("sets page title after updating", function () {
var elem = document.createElement("div");
var collectionData = collectionData_1.ungroupedCollectionData;
var bookData = collectionData.books[0];
var pageTitleTemplate = jest.genMockFunction();
pageTitleTemplate.mockImplementation(function (collectionTitle, bookTitle) {
return "testing " + collectionTitle + ", " + bookTitle;
});
var wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData, bookData: bookData, pageTitleTemplate: pageTitleTemplate}));
// template should be invoked by componentWillMount
expect(pageTitleTemplate.mock.calls.length).toBe(1);
expect(pageTitleTemplate.mock.calls[0][0]).toBe(collectionData.title);
expect(pageTitleTemplate.mock.calls[0][1]).toBe(bookData.title);
expect(document.title).toBe("testing " + collectionData.title + ", " + bookData.title);
wrapper.setProps({
collectionData: null,
bookData: null,
pageTitleTemplate: pageTitleTemplate
});
// template should be invoked again by componentWillUpdate
expect(pageTitleTemplate.mock.calls.length).toBe(2);
expect(pageTitleTemplate.mock.calls[1][0]).toBe(null);
expect(pageTitleTemplate.mock.calls[1][1]).toBe(null);
expect(document.title).toBe("testing null, null");
});
it("calls showPrevBook() on left key press but not if meta key is also presssed", function () {
var showPrevBook = jest.genMockFunction();
var context = routing_1.mockRouterContext();
var wrapper = enzyme_1.mount(React.createElement(Root_1.Root, {bookUrl: "test book", collectionUrl: "test collection", setCollectionAndBook: mockSetCollectionAndBook}), { context: context });
wrapper.instance().showPrevBook = showPrevBook;
document.dispatchEvent(new KeyboardEvent("keydown", {
code: "ArrowLeft"
}));
expect(showPrevBook.mock.calls.length).toBe(1);
document.dispatchEvent(new KeyboardEvent("keydown", {
code: "ArrowLeft",
ctrlKey: true
}));
expect(showPrevBook.mock.calls.length).toBe(1);
});
it("calls showNextBook() on right key press but not if meta key is also presssed", function () {
var showNextBook = jest.genMockFunction();
var context = routing_1.mockRouterContext();
var wrapper = enzyme_1.mount(React.createElement(Root_1.Root, {bookUrl: "test book", collectionUrl: "test collection", setCollectionAndBook: mockSetCollectionAndBook}), { context: context });
wrapper.instance().showNextBook = showNextBook;
document.dispatchEvent(new KeyboardEvent("keydown", {
code: "ArrowRight"
}));
expect(showNextBook.mock.calls.length).toBe(1);
document.dispatchEvent(new KeyboardEvent("keydown", {
code: "ArrowRight",
altKey: true
}));
expect(showNextBook.mock.calls.length).toBe(1);
});
describe("when given a header component", function () {
var wrapper;
var collectionData = Object.assign({}, collectionData_1.ungroupedCollectionData, {
search: {
url: "test search url",
searchData: {
description: "description",
shortName: "shortName",
template: function (s) { return s; }
}
}
});
var bookData = collectionData_1.ungroupedCollectionData.books[0];
var showBasicAuthForm;
var clearBasicAuthCredentials;
var Header = (function (_super) {
__extends(Header, _super);
function Header() {
_super.apply(this, arguments);
}
Header.prototype.render = function () {
return (React.createElement("div", {className: "header"}, this.props.children, React.createElement(CatalogLink_1.default, {collectionUrl: "test url"}, "test")));
};
return Header;
}(React.Component));
beforeEach(function () {
wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {Header: Header, collectionData: collectionData, bookData: bookData, fetchSearchDescription: function (url) { }, showBasicAuthForm: showBasicAuthForm, isSignedIn: true, loansUrl: "loans"}));
});
it("renders the header", function () {
var header = wrapper.find(Header);
var search = header.childAt(0);
expect(header.props().collectionTitle).toBe(collectionData.title);
expect(header.props().bookTitle).toBe(bookData.title);
expect(header.props().isSignedIn).toBe(true);
expect(header.props().showBasicAuthForm).toBe(showBasicAuthForm);
expect(header.props().clearBasicAuthCredentials).toBe(clearBasicAuthCredentials);
expect(header.props().loansUrl).toBe("loans");
expect(search.type()).toBe(Search_1.default);
});
});
describe("showNextBook()", function () {
var mockPush;
var context;
var collectionData;
var bookData;
var nextBookData;
var prevBookData;
var wrapper;
beforeEach(function () {
mockPush = jest.genMockFunction();
context = routing_1.mockRouterContext(mockPush);
collectionData = collectionData_1.groupedCollectionData;
});
it("navigates to second book if currently showing first book", function () {
bookData = collectionData_1.groupedCollectionData.lanes[0].books[0];
nextBookData = collectionData_1.groupedCollectionData.lanes[0].books[1];
wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData, bookData: bookData, setCollectionAndBook: mockSetCollectionAndBook}), { context: context });
wrapper.instance().showNextBook();
expect(mockPush.mock.calls.length).toBe(1);
expect(mockPush.mock.calls[0][0]).toBe(context.pathFor(collectionData.url, nextBookData.url));
});
it("navigates to first book if currently showing last book", function () {
var lastIndex = collectionData_1.groupedCollectionData.lanes[0].books.length - 1;
bookData = collectionData_1.groupedCollectionData.lanes[0].books[lastIndex];
nextBookData = collectionData_1.groupedCollectionData.lanes[0].books[0];
wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData, bookData: bookData, setCollectionAndBook: mockSetCollectionAndBook}), { context: context });
wrapper.instance().showNextBook();
expect(mockPush.mock.calls.length).toBe(1);
expect(mockPush.mock.calls[0][0]).toBe(context.pathFor(collectionData.url, nextBookData.url));
});
});
describe("showPrevBook()", function () {
var mockPush;
var context;
var collectionData;
var bookData;
var nextBookData;
var prevBookData;
var wrapper;
beforeEach(function () {
mockPush = jest.genMockFunction();
context = routing_1.mockRouterContext(mockPush);
collectionData = collectionData_1.groupedCollectionData;
});
it("navigates to last book if currently showing first book", function () {
var lastIndex = collectionData_1.groupedCollectionData.lanes[0].books.length - 1;
bookData = collectionData_1.groupedCollectionData.lanes[0].books[0];
prevBookData = collectionData_1.groupedCollectionData.lanes[0].books[lastIndex];
wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData, bookData: bookData, setCollectionAndBook: mockSetCollectionAndBook}), { context: context });
wrapper.instance().showPrevBook();
expect(mockPush.mock.calls.length).toBe(1);
expect(mockPush.mock.calls[0][0]).toBe(context.pathFor(collectionData.url, prevBookData.url));
});
it("navigates to first book if currently showing second book", function () {
bookData = collectionData_1.groupedCollectionData.lanes[0].books[1];
prevBookData = collectionData_1.groupedCollectionData.lanes[0].books[0];
wrapper = enzyme_1.shallow(React.createElement(Root_1.Root, {collectionData: collectionData, bookData: bookData, setCollectionAndBook: mockSetCollectionAndBook}), { context: context });
wrapper.instance().showPrevBook();
expect(mockPush.mock.calls.length).toBe(1);
expect(mockPush.mock.calls[0][0]).toBe(context.pathFor(collectionData.url, prevBookData.url));
});
});
describe("routing", function () {
var store;
var collectionData = collectionData_1.groupedCollectionData;
var bookData = collectionData_1.groupedCollectionData.lanes[0].books[0];
var push, context, childContextTypes;
var wrapper, root;
var history;
beforeEach(function () {
push = jest.genMockFunction();
context = routing_1.mockRouterContext(push);
childContextTypes = {
router: React.PropTypes.object.isRequired,
pathFor: React.PropTypes.func.isRequired
};
history = [{
text: "root title",
url: "root url"
}, {
text: "some title",
url: "some url"
}];
wrapper = enzyme_1.mount(React.createElement(Root_1.Root, {collectionData: collectionData, bookData: null, history: history}), { context: context, childContextTypes: childContextTypes });
});
it("uses router to show a collection", function () {
var collectionLink = wrapper.find(".laneTitle").first();
var collectionUrl = collectionData.lanes[0].url;
collectionLink.simulate("click", { button: 0 });
expect(push.mock.calls.length).toBe(1);
expect(push.mock.calls[0][0]).toBe(context.pathFor(collectionUrl, null));
});
it("uses router to show a book", function () {
var bookLink = wrapper.find(".laneBookLink").first();
var collectionUrl = collectionData.url;
var bookUrl = collectionData.lanes[0].books[0].url;
bookLink.simulate("click", { button: 0 });
expect(push.mock.calls.length).toBe(1);
expect(push.mock.calls[0][0]).toBe(context.pathFor(collectionUrl, bookUrl));
});
it("uses router to hide a book", function () {
wrapper.setProps({ bookData: bookData });
var collectionLink = wrapper.find("ol.breadcrumb").find(CatalogLink_1.default).last();
var collectionUrl = collectionData.url;
collectionLink.simulate("click", { button: 0 });
expect(push.mock.calls.length).toBe(1);
expect(push.mock.calls[0][0]).toBe(context.pathFor(collectionUrl, null));
});
});
});
;