UNPKG

opds-web-client

Version:
457 lines (456 loc) 25.7 kB
"use strict"; 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)); }); }); });