Skip to content
Snippets Groups Projects
Commit cf520c28 authored by Jack Reed's avatar Jack Reed
Browse files

Merge branch 'master' of ../research-and-demos into update-from-poc

parents 7188cdec 40b41854
No related branches found
No related tags found
No related merge requests found
Showing
with 699 additions and 47 deletions
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
dist/
config/
coverage/
{ {
"extends": "airbnb-base/legacy", "env": {
"jest/globals": true
},
"extends": ["airbnb","react-app"],
"globals": { "globals": {
"Mirador": true, "page": true,
"jQuery": true "document": true
}, },
"parser": "babel-eslint",
"plugins": ["jest"],
"rules": { "rules": {
"no-param-reassign": [ "no-console": "off",
2, { "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"props": true, "require-jsdoc": ["error", {
"ignorePropertyModificationsFor": [ "require": {
"$" "FunctionDeclaration": true,
] "MethodDefinition": true,
"ClassDeclaration": true,
"ArrowFunctionExpression": true,
"FunctionExpression": true
} }
] }],
"react/prefer-stateless-function": "off"
} }
} }
.grunt/ dist/
_SpecRunner.html
bkp
css/discovery.css
css/mirador.css
css/jquery-ui-scoped.css
dev.html
discovery.html
js/discovery
node_modules/
npm-debug.log
coverage/ coverage/
data/ecodices node_modules/
data/BnF package-lock.json
data/Harvard
build/
.idea/
bower_components/
LICENSE 0 → 100644
Copyright 2018 The Board of Trustees of the Leland Stanford Junior University
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
[![Build Status](https://travis-ci.org/ProjectMirador/mirador.svg?branch=master)](https://travis-ci.org/ProjectMirador/mirador?branch=master) ## Running Mirador locally
[![Stories in Ready](https://badge.waffle.io/ProjectMirador/mirador.svg?label=ready&title=Ready)](http://waffle.io/iiif/mirador)
# Mirador 1. Run `npm install` to install the dependencies.
![mirador banner](https://projectmirador.github.io/mirador/img/banner.jpg)
**Mirador is a multi-repository, configurable, extensible, and easy-to-integrate viewer and annotation creation and comparison environment for IIIF resources, ranging from deep-zooming artwork, to complex manuscript objects. It provides a tiling windowed environment for comparing multiple image-based resources, synchronised structural and visual navigation of content using openSeadragon, Open Annotation compliant annotation creation and viewing on deep-zoomable canvases, metadata display, bookreading, and bookmarking.**
### [See a Demo](http://projectmirador.org/demo/) ## Starting the project
### [Getting Started](http://projectmirador.org/docs/docs/getting-started.html)
### Run in Development ```sh
Mirador uses [Node.js](https://nodejs.org/) and a build system to assemble, test, and manage the development resources. If you have never used these tools before, you may need to install them. $ npm start
```
1. Install [Node.js](https://nodejs.org/) Then navigate to [http://127.0.0.1:4444/__tests__/integration/mirador/](http://127.0.0.1:4444/__tests__/integration/mirador/)
2. Install the Grunt command line runner i.e. `npm install -g grunt-cli`
1. Clone the Mirador repository
1. Change into the Mirador directory
1. Install all dependencies with `npm install`. Run `npm start`.
### Run Tests ### Instantiating Mirador
`npm test`
For more information, see the [Docs](http://projectmirador.org/docs/docs/getting-started.html), submit an [issue](https://github.com/projectmirador/mirador/issues), or ask on [Slack](http://bit.ly/iiif-slack). ```javascript
var miradorInstance = Mirador.viewer({
id: 'mirador' // id selector where Mirador should be instantiated
});
### Project Diagnostics > miradorInstance
[![Coverage Status](https://coveralls.io/repos/github/ProjectMirador/mirador/badge.svg?branch=master&upToDate=true)](https://coveralls.io/github/ProjectMirador/mirador?branch=master&upToDate=true) { actions, store }
```
### Example Action
Add a window:
```javascript
store.dispatch(actions.addWindow());
```
To focus a window run:
```javascript
store.dispatch(actions.focusWindow('window-1'))
```
### Check current state
```javascript
store.getState()
```
## Running the tests
```sh
$ npm test # For headless CI=true npm test
```
or to continually watch the source files
```sh
$ npm run test:watch
```
## Linting the project
```sh
$ npm run lint
```
## Debugging
Useful browser extensions for debugging/development purposes
- [React DevTools](https://github.com/facebook/react-devtools)
- [Redux DevTools](https://github.com/zalmoxisus/redux-devtools-extension)
{
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id": "http://iiif.io/api/presentation/2.0/example/fixtures/2/manifest.json",
"@type": "sc:Manifest",
"label": "Test 2 Manifest: Metadata Pairs",
"metadata": [
{
"label": "date",
"value": "some date"
}
],
"within": "http://iiif.io/api/presentation/2.0/example/fixtures/collection.json",
"sequences": [
{
"@type": "sc:Sequence",
"label": "Test 2 Sequence 1",
"canvases": [
{
"@id": "http://iiif.io/api/presentation/2.0/example/fixtures/canvas/2/c1.json",
"@type": "sc:Canvas",
"label": "Test 2 Canvas: 1",
"height": 1800,
"width": 1200,
"images": [
{
"@type": "oa:Annotation",
"motivation": "sc:painting",
"resource": {
"@id": "http://iiif.io/api/presentation/2.0/example/fixtures/resources/page1-full.png",
"@type": "dctypes:Image",
"height": 1800,
"width": 1200
},
"on": "http://iiif.io/api/presentation/2.0/example/fixtures/canvas/2/c1.json"
}
]
}
]
}
]
}
{
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id": "http://iiif.io/api/presentation/2.0/example/fixtures/24/manifest.json",
"@type": "sc:Manifest",
"label": "Test 24 Manifest: Image with IIIF Service - adapted with real image",
"within": "http://iiif.io/api/presentation/2.0/example/fixtures/collection.json",
"sequences": [
{
"@type": "sc:Sequence",
"label": "Test 24 Sequence 1",
"canvases": [
{
"@id": "http://iiif.io/api/presentation/2.0/example/fixtures/canvas/24/c1.json",
"@type": "sc:Canvas",
"label": "Test 24 Canvas: 1",
"height": 1800,
"width": 1200,
"images": [
{
"@type": "oa:Annotation",
"motivation": "sc:painting",
"resource": {
"@id": "https://stacks.stanford.edu/image/iiif/hg676jb4964%2F0380_796-44/full/full/0/default.jpg",
"@type": "dctypes:Image",
"format": "image/jpeg",
"height": 3820,
"width": 5426,
"service": {
"@context": "http://iiif.io/api/image/2/context.json",
"@id": "https://stacks.stanford.edu/image/iiif/hg676jb4964%2F0380_796-44",
"profile": "http://iiif.io/api/image/2/level2.json"
}
},
"on": "http://iiif.io/api/presentation/2.0/example/fixtures/canvas/24/c1.json"
}
]
}
]
}
]
}
describe('Basic end to end Mirador', () => {
beforeAll(async () => {
await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/');
});
it('has the correct page title', async () => {
const title = await page.title();
expect(title).toBe('Mirador');
});
it('loads a manifest and displays it', async () => {
await expect(page).toFill('#manifestURL', 'https://purl.stanford.edu/sn904cj3429/iiif/manifest');
await expect(page).toClick('#fetchBtn');
// TODO: Refactor the app so we get rid of the wait
await page.waitFor(1000);
await expect(page).toMatchElement('li', { text: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest' });
await expect(page).toMatchElement(
'h3',
"Peter's San Francisco Locator. The Birds-Eye-View Map of the Exposition City. Published by Locator Publishing Co",
);
await expect(page).toMatchElement('div', /Color/);
});
});
/* global miradorInstance */
describe('Config updating from instance', () => {
beforeAll(async () => {
await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/');
});
it('can modify the config via api', async () => {
await page.evaluate(() => {
const a = miradorInstance.actions.updateConfig({ foo: 'bat' });
miradorInstance.store.dispatch(a);
});
const config = await page.evaluate(() => (
miradorInstance.store.getState().config
));
await expect(config.foo).toBe('bat');
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<title>Mirador</title>
</head>
<body>
<div id="mirador"></div>
<script>document.write("<script type='text/javascript' src='../../../dist/mirador.min.js?v=" + Date.now() + "'><\/script>");</script>
<script type="text/javascript">
var miradorInstance = Mirador.viewer({
id: 'mirador'
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<title>Mirador</title>
</head>
<body>
<div id="mirador"></div>
<script src="../../../node_modules/react/umd/react.development.js"></script>
<script src="../../../node_modules/react-dom/umd/react-dom.development.js"></script>
<script>document.write("<script type='text/javascript' src='../../../dist/mirador.min.js?v=" + Date.now() + "'><\/script>");</script>
<script type="text/javascript">
class MiradorShareButton extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert('Share this stuff')
}
render() {
return React.createElement('button', { className: 'share', onClick: this.handleClick}, 'Share');
}
}
const miradorShareButton = {
name: 'miradorShareButton',
component: MiradorShareButton,
parent: 'WindowTopBarButtons',
}
Mirador.plugins.miradorShareButton = miradorShareButton;
class MiradorRuler extends React.Component {
constructor(props) {
super(props);
this._isMounted = false;
this.state = {
zooming: false,
}
this.zoomToColor = this.zoomToColor.bind(this);
}
componentDidMount() {
this._isMounted = true;
const that = this;
this.props.pluginParent().viewer.addHandler('zoom', (e) => {
if (that._isMounted) {
that.setState({
zooming: true
})
}
})
// Super hacky don't do this for real
function resetStyle() {
if (that._isMounted) {
that.setState({
zooming: false
})
}
setTimeout(resetStyle, 750)
}
resetStyle();
}
componentWillUnmount() {
this._isMounted = false;
if (this.props.pluginParent()) {
this.props.pluginParent().viewer.removeHandler('zoom');
}
}
zoomToColor(zooming) {
if (zooming) {
return 'red'
}
return 'black'
}
render() {
return React.createElement('div', {className: 'mirador-ruler', style: { position: 'absolute', bottom: 0, color: this.zoomToColor(this.state.zooming)}}, 'I am a ruler')
}
}
const miradorRuler = {
name: 'miradorRuler',
component: MiradorRuler,
parent: 'OpenSeadragonViewer',
mapStateToProps: ({ manifests }, props) => {
return {
manifests // return the part of the state I need here.
}
},
mapDispatchToProps: (dispatch) => {
return {}
},
}
Mirador.plugins.miradorRuler = miradorRuler;
var miradorInstance = Mirador.viewer({
id: 'mirador',
plugins: ['miradorShareButton', 'miradorRuler']
});
</script>
</body>
</html>
describe('Mirador plugin use', () => {
beforeAll(async () => {
await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins.html');
await expect(page).toFill('#manifestURL', 'https://purl.stanford.edu/sn904cj3429/iiif/manifest');
await expect(page).toClick('#fetchBtn');
// TODO: Refactor the app so we get rid of the wait
await page.waitFor(1000);
await expect(page).toClick('li button');
});
it('displays "Share Button" plugin', async () => {
await expect(page).toMatchElement('button', { text: 'Share' });
page.on('dialog', async (dialog) => {
expect(dialog.message()).toBe('Share this stuff');
await dialog.dismiss();
});
await expect(page).toClick('button.share');
});
it('displays "Ruler" plugin', async () => {
await expect(page).toMatchElement('.mirador-ruler');
});
});
describe('Window actions', () => {
beforeAll(async () => {
await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/');
});
it('opens a window and closes it', async () => {
await expect(page).toFill('#manifestURL', 'https://purl.stanford.edu/sn904cj3429/iiif/manifest');
await expect(page).toClick('#fetchBtn');
// TODO: Refactor the app so we get rid of the wait
await page.waitFor(1000);
await expect(page).toClick('li button');
await expect(page).toMatchElement('.mirador-window');
await expect(page).toClick('.mirador-window-close');
await expect(page).not.toMatchElement('.mirador-window');
});
});
describe('Plain JavaScript example', () => {
beforeAll(async () => {
await page.goto('http://127.0.0.1:4488/__tests__/integration/vanilla-js/');
});
it('has the correct page title', async () => {
const title = await page.title();
expect(title).toBe('Examples');
});
it('loads a manifest and displays it', async () => {
await expect(page).toFill('#manifestURL', 'https://purl.stanford.edu/sn904cj3429/iiif/manifest');
await expect(page).toClick('#fetchBtn');
// TODO: Refactor the app so we get rid of the wait
await page.waitFor(1000);
const manifest = await page.$eval('#exampleManifest', e => e.innerHTML);
await expect(manifest).toMatch(/http:\/\/iiif\.io\/api\/presentation\/2\/context\.json/);
});
});
## Notes
To load external manifests, you will need to run the example pages from a serve. One easy way to do this is to run PythonHTTPServer:
`python -m SimpleHTTPServer`
To inspect the Mirador 3 prototype in the web console with (code completion), you can refer to it as:
`m3core`
This name is found in the `minimal_redux_poc/webpack.config.js` as the value of the `library` property.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Examples</title>
<script src="../../../dist/m3core.umd.js"></script>
</head>
<body>
<input id="manifestURL" type="text"/><button id="fetchBtn" type="submit">Fetch Manifest</button>
<pre id="exampleManifest">
</pre>
<script>
document.getElementById("fetchBtn").addEventListener("click", function(){
let val = document.getElementById("manifestURL").value;
let f = m3core.actions.fetchManifest(val);
m3core.store.dispatch(f);
});
m3core.store.subscribe(() => {
let contents = ''
let state = m3core.store.getState();
let manifest = state.manifests[document.getElementById("manifestURL").value];
switch (manifest.isFetching) {
case true:
contents = 'spinner';
break;
case false:
if(manifest.error){
contents = manifest.error.message;
} else {
contents = JSON.stringify(manifest.manifestation.__jsonld, 0, 3);
}
break;
default: contents = ''
}
document.getElementById("exampleManifest").innerHTML = contents;
});
</script>
</body>
</html>
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import * as actions from '../../../src/actions/index';
import ActionTypes from '../../../src/action-types';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('actions', () => {
describe('addWindow', () => {
it('should create a new window with merged defaults', () => {
const options = {
id: 'helloworld',
canvasIndex: 1,
};
const expectedAction = {
type: ActionTypes.ADD_WINDOW,
payload: {
id: 'helloworld',
canvasIndex: 1,
collectionIndex: 0,
manifestId: null,
rangeId: null,
xywh: [0, 0, 400, 400],
rotation: null,
},
};
expect(actions.addWindow(options)).toEqual(expectedAction);
});
});
describe('removeWindow', () => {
it('removes the window and returns windowId', () => {
const id = 'abc123';
const expectedAction = {
type: ActionTypes.REMOVE_WINDOW,
windowId: id,
};
expect(actions.removeWindow(id)).toEqual(expectedAction);
});
});
describe('nextCanvas', () => {
it('moves to the next canvas', () => {
const id = 'abc123';
const expectedAction = {
type: ActionTypes.NEXT_CANVAS,
windowId: id,
};
expect(actions.nextCanvas(id)).toEqual(expectedAction);
});
});
describe('previousCanvas', () => {
it('moves to the previous canvas', () => {
const id = 'abc123';
const expectedAction = {
type: ActionTypes.PREVIOUS_CANVAS,
windowId: id,
};
expect(actions.previousCanvas(id)).toEqual(expectedAction);
});
});
describe('requestManifest', () => {
it('requests a manifest given a url', () => {
const id = 'abc123';
const expectedAction = {
type: ActionTypes.REQUEST_MANIFEST,
manifestId: id,
};
expect(actions.requestManifest(id)).toEqual(expectedAction);
});
});
describe('receiveManifest', () => {
it('moves to the previous canvas', () => {
const id = 'abc123';
const json = {
id,
content: 'lots of metadata, canvases, and other IIIFy things',
};
const expectedAction = {
type: ActionTypes.RECEIVE_MANIFEST,
manifestId: id,
manifestJson: json,
};
expect(actions.receiveManifest(id, json)).toEqual(expectedAction);
});
});
describe('fetchManifest', () => {
let store = null;
beforeEach(() => {
store = mockStore({});
});
describe('success response', () => {
beforeEach(() => {
fetch.mockResponseOnce(JSON.stringify({ data: '12345' })); // eslint-disable-line no-undef
});
it('dispatches the REQUEST_MANIFEST action', () => {
store.dispatch(actions.fetchManifest('https://purl.stanford.edu/sn904cj3429/iiif/manifest'));
expect(store.getActions()).toEqual([
{ manifestId: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', type: 'REQUEST_MANIFEST' },
]);
});
it('dispatches the REQUEST_MANIFEST and then RECEIVE_MANIFEST', () => {
store.dispatch(actions.fetchManifest('https://purl.stanford.edu/sn904cj3429/iiif/manifest'))
.then(() => {
const expectedActions = store.getActions();
expect(expectedActions).toEqual([
{ manifestId: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', type: 'REQUEST_MANIFEST' },
{ manifestId: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', manifestJson: { data: '12345' }, type: 'RECEIVE_MANIFEST' },
]);
});
});
});
describe('error response', () => {
it('dispatches the REQUEST_MANIFEST and then RECEIVE_MANIFEST', () => {
store.dispatch(actions.fetchManifest('https://purl.stanford.edu/sn904cj3429/iiif/manifest'))
.then(() => {
const expectedActions = store.getActions();
expect(expectedActions).toEqual([
{ manifestId: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', type: 'REQUEST_MANIFEST' },
{ manifestId: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', error: new Error('invalid json response body at undefined reason: Unexpected end of JSON input'), type: 'RECEIVE_MANIFEST_FAILURE' },
]);
});
});
});
});
describe('removeManifest', () => {
it('removes an existing manifest', () => {
const expectedAction = {
type: ActionTypes.REMOVE_MANIFEST,
manifestId: 'foo',
};
expect(actions.removeManifest('foo')).toEqual(expectedAction);
});
});
describe('requestInfoResponse', () => {
it('requests an infoResponse from given a url', () => {
const id = 'abc123';
const expectedAction = {
type: ActionTypes.REQUEST_INFO_RESPONSE,
infoId: id,
};
expect(actions.requestInfoResponse(id)).toEqual(expectedAction);
});
});
describe('receiveInfoResponse', () => {
it('recieves an infoResponse', () => {
const id = 'abc123';
const json = {
id,
content: 'image information request',
};
const expectedAction = {
type: ActionTypes.RECEIVE_INFO_RESPONSE,
infoId: id,
infoJson: json,
};
expect(actions.receiveInfoResponse(id, json)).toEqual(expectedAction);
});
});
describe('fetchInfoResponse', () => {
let store = null;
beforeEach(() => {
store = mockStore({});
});
describe('success response', () => {
beforeEach(() => {
fetch.mockResponseOnce(JSON.stringify({ data: '12345' })); // eslint-disable-line no-undef
});
it('dispatches the REQUEST_MANIFEST action', () => {
store.dispatch(actions.fetchInfoResponse('https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/info.json'));
expect(store.getActions()).toEqual([
{ infoId: 'https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/info.json', type: 'REQUEST_INFO_RESPONSE' },
]);
});
it('dispatches the REQUEST_MANIFEST and then RECEIVE_MANIFEST', () => {
store.dispatch(actions.fetchInfoResponse('https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/info.json'))
.then(() => {
const expectedActions = store.getActions();
expect(expectedActions).toEqual([
{ infoId: 'https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/info.json', type: 'REQUEST_INFO_RESPONSE' },
{ infoId: 'https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/info.json', infoJson: { data: '12345' }, type: 'RECEIVE_INFO_RESPONSE' },
]);
});
});
});
describe('error response', () => {
it('dispatches the REQUEST_INFO_RESPONSE and then RECEIVE_INFO_RESPONSE', () => {
store.dispatch(actions.fetchInfoResponse('https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/info.json'))
.then(() => {
const expectedActions = store.getActions();
expect(expectedActions).toEqual([
{ infoId: 'https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/info.json', type: 'REQUEST_INFO_RESPONSE' },
{ infoId: 'https://stacks.stanford.edu/image/iiif/sn904cj3429%2F12027000/info.json', error: new Error('invalid json response body at undefined reason: Unexpected end of JSON input'), type: 'RECEIVE_INFO_RESPONSE_FAILURE' },
]);
});
});
});
});
describe('removeInfoResponse', () => {
it('removes an existing infoResponse', () => {
const expectedAction = {
type: ActionTypes.REMOVE_INFO_RESPONSE,
infoId: 'foo',
};
expect(actions.removeInfoResponse('foo')).toEqual(expectedAction);
});
});
});
import React from 'react';
import { shallow } from 'enzyme';
import Display from '../../../src/components/Display';
import ManifestMetadata from '../../../src/components/ManifestMetadata';
import fixture from '../../fixtures/2.json';
describe('Display', () => {
it('renders without an error', () => {
const wrapper = shallow(<Display manifest={{}} />);
expect(wrapper.contains(<div className="Display"><div id="exampleManifest" className=""><ManifestMetadata manifest={{}} /></div></div>)).toBe(true);
});
it('sets class based on manifest state', () => {
let wrapper = shallow(<Display manifest={{ isFetching: true }} />);
expect(wrapper.find('.mirador-fetching').length).toBe(1);
wrapper = shallow(<Display manifest={{ error: true }} />);
expect(wrapper.find('.mirador-error').length).toBe(1);
});
it('displays content', () => {
let wrapper = shallow(<Display manifest={{ isFetching: true }} />);
expect(wrapper.text()).toBe('');
wrapper = shallow(<Display manifest={{ error: { message: 'bad things' } }} />);
expect(wrapper.text()).toBe('bad things');
wrapper = shallow(<Display manifest={{ json: fixture }} />);
expect(wrapper.find(ManifestMetadata).length).toBe(1);
});
});
import React from 'react';
import { shallow } from 'enzyme';
import { store } from '../../../src/store';
import ManifestListItem from '../../../src/components/ManifestListItem';
describe('ManifestListItem', () => {
it('renders without an error', () => {
const wrapper = shallow(<ManifestListItem manifest="http://example.com" store={store} />).dive();
expect(wrapper.find('li.mirador-manifest-list-item').length).toBe(1);
expect(wrapper.find('button').length).toBe(1);
expect(wrapper.find('button').text()).toEqual('http://example.com');
});
it('updates and adds window when button clicked', () => {
const wrapper = shallow(<ManifestListItem manifest="http://example.com" store={store} />).dive();
expect(store.getState().windows.length).toEqual(0);
wrapper.find('button').simulate('click');
expect(store.getState().windows.length).toEqual(1);
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment