diff --git a/__tests__/src/components/OpenSeadragonViewer.test.js b/__tests__/src/components/OpenSeadragonViewer.test.js index 7dfa0aae873b665d221d505927f71ee30034ec2b..29566d2e677838560fc16a23c950c203d8e4bafa 100644 --- a/__tests__/src/components/OpenSeadragonViewer.test.js +++ b/__tests__/src/components/OpenSeadragonViewer.test.js @@ -10,46 +10,52 @@ const canvases = Utils.parseManifest(fixture).getSequences()[0].getCanvases(); jest.mock('openseadragon'); +/** + * Helper function to create a shallow wrapper around OpenSeadragonViewer + */ +function createWrapper(props) { + return shallow( + <OpenSeadragonViewer + classes={{}} + infoResponses={[{ + id: 'a', + json: { + '@id': 'http://foo', + height: 200, + width: 100, + }, + }, { + id: 'b', + json: { + '@id': 'http://bar', + height: 201, + width: 150, + }, + }]} + nonTiledImages={[{ + getProperty: () => {}, + id: 'http://foo', + }]} + windowId="base" + config={{}} + updateViewport={jest.fn()} + t={k => k} + canvasWorld={new CanvasWorld(canvases)} + {...props} + > + <div className="foo" /> + <div className="bar" /> + </OpenSeadragonViewer>, + ); +} + describe('OpenSeadragonViewer', () => { let wrapper; let updateViewport; beforeEach(() => { OpenSeadragon.mockClear(); - - updateViewport = jest.fn(); - - wrapper = shallow( - <OpenSeadragonViewer - classes={{}} - infoResponses={[{ - id: 'a', - json: { - '@id': 'http://foo', - height: 200, - width: 100, - }, - }, { - id: 'b', - json: { - '@id': 'http://bar', - height: 201, - width: 150, - }, - }]} - nonTiledImages={[{ - getProperty: () => {}, - id: 'http://foo', - }]} - windowId="base" - config={{}} - updateViewport={updateViewport} - t={k => k} - canvasWorld={new CanvasWorld(canvases)} - > - <div className="foo" /> - <div className="bar" /> - </OpenSeadragonViewer>, - ); + wrapper = createWrapper({}); + updateViewport = wrapper.instance().props.updateViewport; }); it('renders the component', () => { expect(wrapper.find('.mirador-osd-container').length).toBe(1); @@ -70,10 +76,7 @@ describe('OpenSeadragonViewer', () => { expect(wrapper.instance().infoResponsesMatch([])).toBe(false); }); it('with an empty array', () => { - wrapper.instance().viewer = { - close: () => {}, - }; - wrapper.setProps({ infoResponses: [] }); + wrapper = createWrapper({ infoResponses: [] }); expect(wrapper.instance().infoResponsesMatch([])).toBe(true); }); it('when the @ids do match', () => { @@ -87,7 +90,7 @@ describe('OpenSeadragonViewer', () => { expect(wrapper.instance().infoResponsesMatch([{ id: 'a', json: { '@id': 'http://foo-degraded' } }])).toBe(false); }); it('when the id props match', () => { - wrapper.setProps({ + wrapper = createWrapper({ infoResponses: [{ id: 'a', json: { @@ -106,10 +109,7 @@ describe('OpenSeadragonViewer', () => { expect(wrapper.instance().nonTiledImagedMatch([])).toBe(false); }); it('with an empty array', () => { - wrapper.instance().viewer = { - close: () => {}, - }; - wrapper.setProps({ nonTiledImages: [] }); + wrapper = createWrapper({ nonTiledImages: [] }); expect(wrapper.instance().nonTiledImagedMatch([])).toBe(true); }); it('when the ids do match', () => { @@ -118,21 +118,17 @@ describe('OpenSeadragonViewer', () => { }); describe('addAllImageSources', () => { - it('calls addTileSource for every tileSources and then zoomsToWorld', () => { - wrapper.instance().viewer = { - close: () => {}, - }; - wrapper.setProps({ infoResponses: [1, 2, 3, 4] }); + it('calls addTileSource for every tileSources and then zoomsToWorld', async () => { + wrapper = createWrapper({ infoResponses: [1, 2, 3, 4] }); + wrapper.setState({ viewer: { viewport: { fitBounds: () => {} }, world: { getItemCount: () => 0 } } }); const mockAddTileSource = jest.fn(); wrapper.instance().addTileSource = mockAddTileSource; - wrapper.instance().addAllImageSources(); + await wrapper.instance().addAllImageSources(); expect(mockAddTileSource).toHaveBeenCalledTimes(4); }); - it('calls addNonTileSource for every nonTiledImage and then zoomsToWorld', () => { - wrapper.instance().viewer = { - close: () => {}, - }; - wrapper.setProps({ + + it('calls addNonTileSource for every nonTiledImage and then zoomsToWorld', async () => { + wrapper = createWrapper({ nonTiledImages: [ { getProperty: () => 'Image' }, { getProperty: () => 'Image' }, @@ -140,22 +136,18 @@ describe('OpenSeadragonViewer', () => { { getProperty: () => 'Image' }, ], }); + const instance = wrapper.instance(); const mockAddNonTiledImage = jest.fn(); wrapper.instance().addNonTiledImage = mockAddNonTiledImage; - wrapper.instance().addAllImageSources(); + await instance.addAllImageSources(); expect(mockAddNonTiledImage).toHaveBeenCalledTimes(4); }); }); describe('addTileSource', () => { - it('calls addTiledImage asynchronously on the OSD viewer', async () => { - wrapper.instance().addTileSource({}).then((event) => { - expect(event).toBe('event'); - }); - }); - it('when a viewer is not available, returns an unresolved Promise', () => { - expect(wrapper.instance().addTileSource({})).toEqual(expect.any(Promise)); - }); + it('when a viewer is not available, returns an unresolved Promise', () => ( + expect(wrapper.instance().addTileSource({})).rejects.toBeUndefined() + )); }); describe('addNonTiledImage', () => { @@ -189,17 +181,15 @@ describe('OpenSeadragonViewer', () => { layerIndexOfImageResource: i => 1 - i, layerOpacityOfImageResource: i => 0.5, }; - wrapper.setProps({ canvasWorld }); + wrapper = createWrapper({ canvasWorld }); wrapper.instance().loaded = true; - wrapper.setState({ - viewer: { - world: { - getItemAt: i => ({ setOpacity, source: { id: i } }), - getItemCount: () => 2, - setItemIndex, - }, + wrapper.instance().state.viewer = { + world: { + getItemAt: i => ({ setOpacity, source: { id: i } }), + getItemCount: () => 2, + setItemIndex, }, - }); + }; wrapper.instance().refreshTileProperties(); diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js index d6353a624e52c31d715a76b1541b4f43e734c8ce..9fcf686288aa914dd32bec1247819a19c060e86f 100644 --- a/src/components/OpenSeadragonViewer.js +++ b/src/components/OpenSeadragonViewer.js @@ -167,10 +167,11 @@ export class OpenSeadragonViewer extends Component { /** */ addAllImageSources(zoomAfterAdd = true) { const { nonTiledImages, infoResponses } = this.props; - Promise.all( - infoResponses.map(infoResponse => this.addTileSource(infoResponse)), - nonTiledImages.map(image => this.addNonTiledImage(image)), - ).then(() => { + + return Promise.allSettled([ + ...infoResponses.map(infoResponse => this.addTileSource(infoResponse)), + ...nonTiledImages.map(image => this.addNonTiledImage(image)), + ]).then(() => { if (infoResponses[0] || nonTiledImages[0]) { if (zoomAfterAdd) this.zoomToWorld(); this.refreshTileProperties(); @@ -193,7 +194,7 @@ export class OpenSeadragonViewer extends Component { reject(); } - viewer.addSimpleImage({ + resolve(viewer.addSimpleImage({ error: event => reject(event), fitBounds: new OpenSeadragon.Rect( ...canvasWorld.contentResourceToWorldCoordinates(contentResource), @@ -202,7 +203,7 @@ export class OpenSeadragonViewer extends Component { opacity: canvasWorld.layerOpacityOfImageResource(contentResource), success: event => resolve(event), url: contentResource.id, - }); + })); }); } @@ -238,7 +239,11 @@ export class OpenSeadragonViewer extends Component { /** */ refreshTileProperties() { const { canvasWorld } = this.props; - const { viewer: { world } } = this.state; + const { viewer } = this.state; + + if (!viewer) return; + + const { world } = viewer; const items = []; for (let i = 0; i < world.getItemCount(); i += 1) { @@ -259,7 +264,7 @@ export class OpenSeadragonViewer extends Component { fitBounds(x, y, w, h, immediately = true) { const { viewer } = this.state; - viewer.viewport.fitBounds( + viewer && viewer.viewport && viewer.viewport.fitBounds( new OpenSeadragon.Rect(x, y, w, h), immediately, ); diff --git a/src/lib/CanvasWorld.js b/src/lib/CanvasWorld.js index cb1e0078ce34bb39f03cc0ab81e229eb957cbda4..e130f3f3d20c727b27f881793d476998fa451004 100644 --- a/src/lib/CanvasWorld.js +++ b/src/lib/CanvasWorld.js @@ -136,7 +136,7 @@ export default class CanvasWorld { /** Get the IIIF content resource for an image */ contentResource(infoResponseId) { const miradorCanvas = this.canvases.find(c => c.imageServiceIds.some(id => ( - normalizeUrl(id, { stripAuthentication: false }) + id && infoResponseId && normalizeUrl(id, { stripAuthentication: false }) === normalizeUrl(infoResponseId, { stripAuthentication: false })))); if (!miradorCanvas) return undefined; return miradorCanvas.imageResources