diff options
author | Maximilian Hils <git@maximilianhils.com> | 2017-06-12 10:59:27 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-12 10:59:27 +0200 |
commit | c89d076cf3aabd44bec9eb092e1dbab953c125b4 (patch) | |
tree | bf129b08a833c8bb72220b67d386a7cc0b75f970 | |
parent | 93d37e29c382240c688c574c2369d8a947a1e8f9 (diff) | |
parent | 49a04e37c36d576bb764c3cb2dac239b24d261be (diff) | |
download | mitmproxy-c89d076cf3aabd44bec9eb092e1dbab953c125b4.tar.gz mitmproxy-c89d076cf3aabd44bec9eb092e1dbab953c125b4.tar.bz2 mitmproxy-c89d076cf3aabd44bec9eb092e1dbab953c125b4.zip |
Merge pull request #2376 from MatthewShao/jest-dev
[WIP][web] Add tests for components/ContentView (con't)
22 files changed, 700 insertions, 82 deletions
diff --git a/web/package.json b/web/package.json index 94b0ee60..a4ef0f29 100644 --- a/web/package.json +++ b/web/package.json @@ -26,6 +26,7 @@ "bootstrap": "^3.3.7", "classnames": "^2.2.5", "lodash": "^4.17.4", + "mock-xmlhttprequest": "^1.1.0", "prop-types": "^15.5.0", "react": "^15.4.2", "react-codemirror": "^0.3.0", diff --git a/web/src/js/__tests__/components/ContentView/ContentLoaderSpec.js b/web/src/js/__tests__/components/ContentView/ContentLoaderSpec.js new file mode 100644 index 00000000..80b40c72 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/ContentLoaderSpec.js @@ -0,0 +1,74 @@ +import React from 'react' +import renderer from 'react-test-renderer' +import withContentLoader from '../../../components/ContentView/ContentLoader' +import { TFlow } from '../../ducks/tutils' +import TestUtils from 'react-dom/test-utils' +import mockXMLHttpRequest from 'mock-xmlhttprequest' + +global.XMLHttpRequest = mockXMLHttpRequest +class tComponent extends React.Component { + constructor(props, context){ + super(props, context) + } + render() { + return (<p>foo</p>) + } +} + +let tflow = new TFlow(), + ContentLoader = withContentLoader(tComponent) + +describe('ContentLoader Component', () => { + it('should render correctly', () => { + let contentLoader = renderer.create(<ContentLoader flow={tflow} message={tflow.response}/>), + tree = contentLoader.toJSON() + expect(tree).toMatchSnapshot() + }) + + let contentLoader = TestUtils.renderIntoDocument(<ContentLoader flow={tflow} message={tflow.response}/>) + + it('should handle updateContent', () => { + tflow.response.content = 'foo' + contentLoader.updateContent({flow: tflow, message: tflow.response}) + expect(contentLoader.state.request).toEqual(undefined) + expect(contentLoader.state.content).toEqual('foo') + // when content length is 0 or null + tflow.response.contentLength = 0 + tflow.response.content = undefined + contentLoader.updateContent({flow: tflow, message: tflow.response}) + expect(contentLoader.state.request).toEqual(undefined) + expect(contentLoader.state.content).toEqual('') + }) + + it('should handle componentWillReceiveProps', () => { + contentLoader.updateContent = jest.fn() + contentLoader.componentWillReceiveProps({flow: tflow, message: tflow.request}) + expect(contentLoader.updateContent).toBeCalled() + }) + + it('should handle requestComplete', () => { + expect(contentLoader.requestComplete(tflow.request, {})).toEqual(undefined) + // request == this.state.request + contentLoader.state.request = tflow.request + contentLoader.requestComplete(tflow.request, {}) + expect(contentLoader.state.content).toEqual(tflow.request.responseText) + expect(contentLoader.state.request).toEqual(undefined) + }) + + it('should handle requestFailed', () => { + console.error = jest.fn() + expect(contentLoader.requestFailed(tflow.request, {})).toEqual(undefined) + //request == this.state.request + contentLoader.state.request = tflow.request + contentLoader.requestFailed(tflow.request, 'foo error') + expect(contentLoader.state.content).toEqual('Error getting content.') + expect(contentLoader.state.request).toEqual(undefined) + expect(console.error).toBeCalledWith('foo error') + }) + + it('should handle componentWillUnmount', () => { + contentLoader.state.request = { abort : jest.fn() } + contentLoader.componentWillUnmount() + expect(contentLoader.state.request.abort).toBeCalled() + }) +}) diff --git a/web/src/js/__tests__/components/ContentView/ContentViewSpec.js b/web/src/js/__tests__/components/ContentView/ContentViewSpec.js new file mode 100644 index 00000000..f519185d --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/ContentViewSpec.js @@ -0,0 +1,85 @@ +import React from 'react' +import renderer from 'react-test-renderer' +import TestUtils from 'react-dom/test-utils' +import { Provider } from 'react-redux' +import { ViewServer, ViewImage, PureViewServer, Edit } from '../../../components/ContentView/ContentViews' +import { TFlow, TStore } from '../../ducks/tutils' +import mockXMLHttpRequest from 'mock-xmlhttprequest' + +global.XMLHttpRequest = mockXMLHttpRequest +let tflow = new TFlow() + +describe('ViewImage Component', () => { + let viewImage = renderer.create(<ViewImage flow={tflow} message={tflow.response}/>), + tree = viewImage.toJSON() + + it('should render correctly', () => { + expect(tree).toMatchSnapshot() + }) +}) + +describe('ViewServer Component', () => { + let store = TStore(), + setContentViewDescFn = jest.fn(), + setContentFn = jest.fn() + + it('should render correctly and connect to state', () => { + let provider = renderer.create( + <Provider store={store}> + <ViewServer + setContentViewDescription={setContentViewDescFn} + setContent={setContentFn} + flow={tflow} + message={tflow.response} + /> + </Provider>), + tree = provider.toJSON() + expect(tree).toMatchSnapshot() + + let viewServer = renderer.create( + <PureViewServer + showFullContent={true} + maxLines={10} + setContentViewDescription={setContentViewDescFn} + setContent={setContentViewDescFn} + flow={tflow} + message={tflow.response} + content={JSON.stringify({lines: [['k1', 'v1']]})} + /> + ) + tree = viewServer.toJSON() + expect(tree).toMatchSnapshot() + }) + + it('should handle componentWillReceiveProps', () => { + // case of fail to parse content + let viewSever = TestUtils.renderIntoDocument( + <PureViewServer + showFullContent={true} + maxLines={10} + setContentViewDescription={setContentViewDescFn} + setContent={setContentViewDescFn} + flow={tflow} + message={tflow.response} + content={JSON.stringify({lines: [['k1', 'v1']]})} + /> + ) + viewSever.componentWillReceiveProps({...viewSever.props, content: '{foo' }) + let e = '' + try {JSON.parse('{foo') } catch(err){ e = err.message} + expect(viewSever.data).toEqual({ description: e, lines: [] }) + }) +}) + +describe('Edit Component', () => { + it('should render correctly', () => { + let edit = renderer.create(<Edit + content="foo" + onChange={jest.fn} + flow={tflow} + message={tflow.response} + />), + tree = edit.toJSON() + expect(tree).toMatchSnapshot() + }) +}) diff --git a/web/src/js/__tests__/components/ContentView/DownloadContentButtonSpec.js b/web/src/js/__tests__/components/ContentView/DownloadContentButtonSpec.js new file mode 100644 index 00000000..fd00627d --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/DownloadContentButtonSpec.js @@ -0,0 +1,15 @@ +import React from 'react' +import renderer from 'react-test-renderer' +import DownloadContentButton from '../../../components/ContentView/DownloadContentButton' +import { TFlow } from '../../ducks/tutils' + +let tflow = new TFlow() +describe('DownloadContentButton Component', () => { + it('should render correctly', () => { + let downloadContentButton = renderer.create( + <DownloadContentButton flow={tflow} message={tflow.response}/> + ), + tree = downloadContentButton.toJSON() + expect(tree).toMatchSnapshot() + }) +}) diff --git a/web/src/js/__tests__/components/ContentView/MetaViewsSpec.js b/web/src/js/__tests__/components/ContentView/MetaViewsSpec.js new file mode 100644 index 00000000..26b2a27c --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/MetaViewsSpec.js @@ -0,0 +1,37 @@ +import React from 'react' +import renderer from 'react-test-renderer' +import { ContentEmpty, ContentMissing, ContentTooLarge } from '../../../components/ContentView/MetaViews' +import { TFlow } from '../../ducks/tutils' + +let tflow = new TFlow() + +describe('ContentEmpty Components', () => { + it('should render correctly', () => { + let contentEmpty = renderer.create(<ContentEmpty flow={tflow} message={tflow.response}/>), + tree = contentEmpty.toJSON() + expect(tree).toMatchSnapshot() + }) +}) + +describe('ContentMissing Components', () => { + it('should render correctly', () => { + let contentMissing = renderer.create(<ContentMissing flow={tflow} message={tflow.response}/>), + tree = contentMissing.toJSON() + expect(tree).toMatchSnapshot() + }) +}) + +describe('ContentTooLarge Components', () => { + it('should render correctly', () => { + let clickFn = jest.fn(), + uploadContentFn = jest.fn(), + contentTooLarge = renderer.create(<ContentTooLarge + flow={tflow} + message={tflow.response} + onClick={clickFn} + uploadContent={uploadContentFn} + />), + tree = contentTooLarge.toJSON() + expect(tree).toMatchSnapshot() + }) +}) diff --git a/web/src/js/__tests__/components/ContentView/ShowFullContentButtonSpec.js b/web/src/js/__tests__/components/ContentView/ShowFullContentButtonSpec.js new file mode 100644 index 00000000..14871f13 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/ShowFullContentButtonSpec.js @@ -0,0 +1,39 @@ +import React from 'react' +import renderer from 'react-test-renderer' +import { Provider } from 'react-redux' +import ConnectedComponent, { ShowFullContentButton } from '../../../components/ContentView/ShowFullContentButton' +import { TStore } from '../../ducks/tutils' + + +describe('ShowFullContentButton Component', () => { + let setShowFullContentFn = jest.fn(), + showFullContentButton = renderer.create( + <ShowFullContentButton + setShowFullContent={setShowFullContentFn} + showFullContent={false} + visibleLines={10} + contentLines={20} + /> + ), + tree = showFullContentButton.toJSON() + + it('should render correctly', () => { + expect(tree).toMatchSnapshot() + }) + + it('should handle click', () => { + tree.children[0].props.onClick() + expect(setShowFullContentFn).toBeCalled() + }) + + it('should connect to state', () => { + let store = TStore(), + provider = renderer.create( + <Provider store={store}> + <ConnectedComponent/> + </Provider> + ), + tree = provider.toJSON() + expect(tree).toMatchSnapshot() + }) +}) diff --git a/web/src/js/__tests__/components/ContentView/UploadContentButtonSpec.js b/web/src/js/__tests__/components/ContentView/UploadContentButtonSpec.js new file mode 100644 index 00000000..3695be72 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/UploadContentButtonSpec.js @@ -0,0 +1,12 @@ +import React from 'react' +import renderer from 'react-test-renderer' +import UploadContentButton from '../../../components/ContentView/UploadContentButton' + +describe('UpdateContentButton Component', () => { + it('should render correctly', () => { + let uploadContentFn = jest.fn(), + uploadContentButton = renderer.create(<UploadContentButton uploadContent={uploadContentFn}/>), + tree = uploadContentButton.toJSON() + expect(tree).toMatchSnapshot() + }) +}) diff --git a/web/src/js/__tests__/components/ContentView/ViewSelectorSpec.js b/web/src/js/__tests__/components/ContentView/ViewSelectorSpec.js new file mode 100644 index 00000000..9e87e2f0 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/ViewSelectorSpec.js @@ -0,0 +1,38 @@ +import React from 'react' +import renderer from 'react-test-renderer' +import ConnectedComponent, { ViewSelector } from '../../../components/ContentView/ViewSelector' +import { Provider } from 'react-redux' +import { TStore } from '../../ducks/tutils' + + +describe('ViewSelector Component', () => { + let contentViews = ['Auto', 'Raw', 'Text'], + activeView = 'Auto', + setContentViewFn = jest.fn(), + viewSelector = renderer.create( + <ViewSelector contentViews={contentViews} activeView={activeView} setContentView={setContentViewFn}/> + ), + tree = viewSelector.toJSON() + + it('should render correctly', () => { + expect(tree).toMatchSnapshot() + }) + + it('should handle click', () => { + let mockEvent = { preventDefault: jest.fn() }, + tab = tree.children[1].children[0].children[1] + tab.props.onClick(mockEvent) + expect(mockEvent.preventDefault).toBeCalled() + }) + + it('should connect to state', () => { + let store = TStore(), + provider = renderer.create( + <Provider store={store}> + <ConnectedComponent/> + </Provider> + ), + tree = provider.toJSON() + expect(tree).toMatchSnapshot() + }) +}) diff --git a/web/src/js/__tests__/components/ContentView/__snapshots__/ContentLoaderSpec.js.snap b/web/src/js/__tests__/components/ContentView/__snapshots__/ContentLoaderSpec.js.snap new file mode 100644 index 00000000..88d4a380 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/__snapshots__/ContentLoaderSpec.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ContentLoader Component should render correctly 1`] = ` +<div + className="text-center" +> + <i + className="fa fa-spinner fa-spin" + /> +</div> +`; diff --git a/web/src/js/__tests__/components/ContentView/__snapshots__/ContentViewSpec.js.snap b/web/src/js/__tests__/components/ContentView/__snapshots__/ContentViewSpec.js.snap new file mode 100644 index 00000000..a4bc06c5 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/__snapshots__/ContentViewSpec.js.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Edit Component should render correctly 1`] = ` +<div + className="text-center" +> + <i + className="fa fa-spinner fa-spin" + /> +</div> +`; + +exports[`ViewImage Component should render correctly 1`] = ` +<div + className="flowview-image" +> + <img + alt="preview" + className="img-thumbnail" + src="/flows/d91165be-ca1f-4612-88a9-c0f8696f3e29/response/content" + /> +</div> +`; + +exports[`ViewServer Component should render correctly and connect to state 1`] = ` +<div + className="text-center" +> + <i + className="fa fa-spinner fa-spin" + /> +</div> +`; + +exports[`ViewServer Component should render correctly and connect to state 2`] = ` +<div> + <pre> + <div> + <span + className="k" + > + 1 + </span> + <span + className="v" + > + 1 + </span> + </div> + </pre> +</div> +`; diff --git a/web/src/js/__tests__/components/ContentView/__snapshots__/DownloadContentButtonSpec.js.snap b/web/src/js/__tests__/components/ContentView/__snapshots__/DownloadContentButtonSpec.js.snap new file mode 100644 index 00000000..66900ca4 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/__snapshots__/DownloadContentButtonSpec.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DownloadContentButton Component should render correctly 1`] = ` +<a + className="btn btn-default btn-xs" + href="/flows/d91165be-ca1f-4612-88a9-c0f8696f3e29/response/content" + title="Download the content of the flow." +> + <i + className="fa fa-download" + /> +</a> +`; diff --git a/web/src/js/__tests__/components/ContentView/__snapshots__/MetaViewsSpec.js.snap b/web/src/js/__tests__/components/ContentView/__snapshots__/MetaViewsSpec.js.snap new file mode 100644 index 00000000..18ec5bba --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/__snapshots__/MetaViewsSpec.js.snap @@ -0,0 +1,66 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ContentEmpty Components should render correctly 1`] = ` +<div + className="alert alert-info" +> + No + response + content. +</div> +`; + +exports[`ContentMissing Components should render correctly 1`] = ` +<div + className="alert alert-info" +> + Response + content missing. +</div> +`; + +exports[`ContentTooLarge Components should render correctly 1`] = ` +<div> + <div + className="alert alert-warning" + > + <button + className="btn btn-xs btn-warning pull-right" + onClick={[Function]} + > + Display anyway + </button> + 7b + content size. + </div> + <div + className="view-options text-center" + > + <a + className="btn btn-default btn-xs" + href="#" + onClick={[Function]} + title="Upload a file to replace the content." + > + <i + className="fa fa-fw fa-upload" + /> + <input + className="hidden" + onChange={[Function]} + type="file" + /> + </a> + + <a + className="btn btn-default btn-xs" + href="/flows/d91165be-ca1f-4612-88a9-c0f8696f3e29/response/content" + title="Download the content of the flow." + > + <i + className="fa fa-download" + /> + </a> + </div> +</div> +`; diff --git a/web/src/js/__tests__/components/ContentView/__snapshots__/ShowFullContentButtonSpec.js.snap b/web/src/js/__tests__/components/ContentView/__snapshots__/ShowFullContentButtonSpec.js.snap new file mode 100644 index 00000000..e0532154 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/__snapshots__/ShowFullContentButtonSpec.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ShowFullContentButton Component should connect to state 1`] = `null`; + +exports[`ShowFullContentButton Component should render correctly 1`] = ` +<div> + <div + className="view-all-content-btn btn-xs btn btn-default" + disabled={undefined} + onClick={[Function]} + title={undefined} + > + Show full content + </div> + <span + className="pull-right" + > + + 10 + / + 20 + are visible + </span> +</div> +`; diff --git a/web/src/js/__tests__/components/ContentView/__snapshots__/UploadContentButtonSpec.js.snap b/web/src/js/__tests__/components/ContentView/__snapshots__/UploadContentButtonSpec.js.snap new file mode 100644 index 00000000..f642d731 --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/__snapshots__/UploadContentButtonSpec.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateContentButton Component should render correctly 1`] = ` +<a + className="btn btn-default btn-xs" + href="#" + onClick={[Function]} + title="Upload a file to replace the content." +> + <i + className="fa fa-fw fa-upload" + /> + <input + className="hidden" + onChange={[Function]} + type="file" + /> +</a> +`; diff --git a/web/src/js/__tests__/components/ContentView/__snapshots__/ViewSelectorSpec.js.snap b/web/src/js/__tests__/components/ContentView/__snapshots__/ViewSelectorSpec.js.snap new file mode 100644 index 00000000..481bd56a --- /dev/null +++ b/web/src/js/__tests__/components/ContentView/__snapshots__/ViewSelectorSpec.js.snap @@ -0,0 +1,123 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ViewSelector Component should connect to state 1`] = ` +<div + className="dropup pull-left" +> + <a + className="btn btn-default btn-xs" + href="#" + onClick={[Function]} + > + <span> + + <b> + View: + </b> + + auto + + <span + className="caret" + /> + + </span> + </a> + <ul + className="dropdown-menu" + role="menu" + > + <li> + + <a + href="#" + onClick={[Function]} + > + auto + </a> + + </li> + <li> + + <a + href="#" + onClick={[Function]} + > + raw + </a> + + </li> + <li> + + <a + href="#" + onClick={[Function]} + > + text + </a> + + </li> + </ul> +</div> +`; + +exports[`ViewSelector Component should render correctly 1`] = ` +<div + className="dropup pull-left" +> + <a + className="btn btn-default btn-xs" + href="#" + onClick={[Function]} + > + <span> + + <b> + View: + </b> + + auto + + <span + className="caret" + /> + + </span> + </a> + <ul + className="dropdown-menu" + role="menu" + > + <li> + + <a + href="#" + onClick={[Function]} + > + auto + </a> + + </li> + <li> + + <a + href="#" + onClick={[Function]} + > + raw + </a> + + </li> + <li> + + <a + href="#" + onClick={[Function]} + > + text + </a> + + </li> + </ul> +</div> +`; diff --git a/web/src/js/components/ContentView/ContentLoader.jsx b/web/src/js/components/ContentView/ContentLoader.jsx index 4cafde28..69e4a988 100644 --- a/web/src/js/components/ContentView/ContentLoader.jsx +++ b/web/src/js/components/ContentView/ContentLoader.jsx @@ -2,98 +2,100 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { MessageUtils } from '../../flow/utils.js' -export default View => class extends React.Component { +export default function withContentLoader(View) { + + return class extends React.Component { + static displayName = View.displayName || View.name + static matches = View.matches - static displayName = View.displayName || View.name - static matches = View.matches - - static propTypes = { - ...View.propTypes, - content: PropTypes.string, // mark as non-required - flow: PropTypes.object.isRequired, - message: PropTypes.object.isRequired, - } - - constructor(props) { - super(props) - this.state = { - content: undefined, - request: undefined, + static propTypes = { + ...View.propTypes, + content: PropTypes.string, // mark as non-required + flow: PropTypes.object.isRequired, + message: PropTypes.object.isRequired, } - } - componentWillMount() { - this.updateContent(this.props) - } - - componentWillReceiveProps(nextProps) { - if ( - nextProps.message.content !== this.props.message.content || - nextProps.message.contentHash !== this.props.message.contentHash || - nextProps.contentView !== this.props.contentView - ) { - this.updateContent(nextProps) + constructor(props) { + super(props) + this.state = { + content: undefined, + request: undefined, + } } - } - componentWillUnmount() { - if (this.state.request) { - this.state.request.abort() + componentWillMount() { + this.updateContent(this.props) } - } - updateContent(props) { - if (this.state.request) { - this.state.request.abort() + componentWillReceiveProps(nextProps) { + if ( + nextProps.message.content !== this.props.message.content || + nextProps.message.contentHash !== this.props.message.contentHash || + nextProps.contentView !== this.props.contentView + ) { + this.updateContent(nextProps) + } } - // We have a few special cases where we do not need to make an HTTP request. - if(props.message.content !== undefined) { - return this.setState({request: undefined, content: props.message.content}) - } - if(props.message.contentLength === 0 || props.message.contentLength === null){ - return this.setState({request: undefined, content: ""}) + + componentWillUnmount() { + if (this.state.request) { + this.state.request.abort() + } } - let requestUrl = MessageUtils.getContentURL(props.flow, props.message, (View.name == 'ViewServer' ? props.contentView : undefined)) + updateContent(props) { + if (this.state.request) { + this.state.request.abort() + } + // We have a few special cases where we do not need to make an HTTP request. + if (props.message.content !== undefined) { + return this.setState({request: undefined, content: props.message.content}) + } + if (props.message.contentLength === 0 || props.message.contentLength === null) { + return this.setState({request: undefined, content: ""}) + } - // We use XMLHttpRequest instead of fetch() because fetch() is not (yet) abortable. - let request = new XMLHttpRequest(); - request.addEventListener("load", this.requestComplete.bind(this, request)); - request.addEventListener("error", this.requestFailed.bind(this, request)); - request.open("GET", requestUrl); - request.send(); - this.setState({ request, content: undefined }) - } + let requestUrl = MessageUtils.getContentURL(props.flow, props.message, (View.name == 'ViewServer' ? props.contentView : undefined)) - requestComplete(request, e) { - if (request !== this.state.request) { - return // Stale request + // We use XMLHttpRequest instead of fetch() because fetch() is not (yet) abortable. + let request = new XMLHttpRequest(); + request.addEventListener("load", this.requestComplete.bind(this, request)); + request.addEventListener("error", this.requestFailed.bind(this, request)); + request.open("GET", requestUrl); + request.send(); + this.setState({request, content: undefined}) } - this.setState({ - content: request.responseText, - request: undefined - }) - } - requestFailed(request, e) { - if (request !== this.state.request) { - return // Stale request + requestComplete(request, e) { + if (request !== this.state.request) { + return // Stale request + } + this.setState({ + content: request.responseText, + request: undefined + }) + } + + requestFailed(request, e) { + if (request !== this.state.request) { + return // Stale request + } + console.error(e) + // FIXME: Better error handling + this.setState({ + content: "Error getting content.", + request: undefined + }) } - console.error(e) - // FIXME: Better error handling - this.setState({ - content: "Error getting content.", - request: undefined - }) - } - render() { - return this.state.content !== undefined ? ( - <View content={this.state.content} {...this.props}/> - ) : ( - <div className="text-center"> - <i className="fa fa-spinner fa-spin"></i> - </div> - ) + render() { + return this.state.content !== undefined ? ( + <View content={this.state.content} {...this.props}/> + ) : ( + <div className="text-center"> + <i className="fa fa-spinner fa-spin"></i> + </div> + ) + } } }; diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx index 136188d4..dfae937e 100644 --- a/web/src/js/components/ContentView/ContentViews.jsx +++ b/web/src/js/components/ContentView/ContentViews.jsx @@ -30,7 +30,7 @@ function Edit({ content, onChange }) { } Edit = ContentLoader(Edit) -class ViewServer extends Component { +export class PureViewServer extends Component { static propTypes = { showFullContent: PropTypes.bool.isRequired, maxLines: PropTypes.number.isRequired, @@ -85,7 +85,7 @@ class ViewServer extends Component { } -ViewServer = connect( +const ViewServer = connect( state => ({ showFullContent: state.ui.flow.showFullContent, maxLines: state.ui.flow.maxContentLines @@ -94,6 +94,6 @@ ViewServer = connect( setContentViewDescription, setContent } -)(ContentLoader(ViewServer)) +)(ContentLoader(PureViewServer)) export { Edit, ViewServer, ViewImage } diff --git a/web/src/js/components/ContentView/DownloadContentButton.jsx b/web/src/js/components/ContentView/DownloadContentButton.jsx index 447db211..f32a19ca 100644 --- a/web/src/js/components/ContentView/DownloadContentButton.jsx +++ b/web/src/js/components/ContentView/DownloadContentButton.jsx @@ -1,3 +1,4 @@ +import React from 'react' import { MessageUtils } from "../../flow/utils" import PropTypes from 'prop-types' diff --git a/web/src/js/components/ContentView/ShowFullContentButton.jsx b/web/src/js/components/ContentView/ShowFullContentButton.jsx index fd627ad9..c6d8c2f2 100644 --- a/web/src/js/components/ContentView/ShowFullContentButton.jsx +++ b/web/src/js/components/ContentView/ShowFullContentButton.jsx @@ -12,7 +12,7 @@ ShowFullContentButton.propTypes = { showFullContent: PropTypes.bool.isRequired } -function ShowFullContentButton ( {setShowFullContent, showFullContent, visibleLines, contentLines} ){ +export function ShowFullContentButton ( {setShowFullContent, showFullContent, visibleLines, contentLines} ){ return ( !showFullContent && diff --git a/web/src/js/components/ContentView/UploadContentButton.jsx b/web/src/js/components/ContentView/UploadContentButton.jsx index 0021593f..847d4eb0 100644 --- a/web/src/js/components/ContentView/UploadContentButton.jsx +++ b/web/src/js/components/ContentView/UploadContentButton.jsx @@ -1,3 +1,4 @@ +import React from 'react' import PropTypes from 'prop-types' import FileChooser from '../common/FileChooser' diff --git a/web/src/js/components/ContentView/ViewSelector.jsx b/web/src/js/components/ContentView/ViewSelector.jsx index 4c99d5ed..812d87e5 100644 --- a/web/src/js/components/ContentView/ViewSelector.jsx +++ b/web/src/js/components/ContentView/ViewSelector.jsx @@ -11,7 +11,7 @@ ViewSelector.propTypes = { setContentView: PropTypes.func.isRequired } -function ViewSelector ({contentViews, activeView, setContentView}){ +export function ViewSelector ({contentViews, activeView, setContentView}){ let inner = <span> <b>View:</b> {activeView.toLowerCase()} <span className="caret"></span> </span> return ( diff --git a/web/yarn.lock b/web/yarn.lock index 602b4916..54baf85e 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -3648,6 +3648,10 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: dependencies:
minimist "0.0.8"
+mock-xmlhttprequest@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/mock-xmlhttprequest/-/mock-xmlhttprequest-1.1.0.tgz#310edeccb4390a2d630e1a0b4e747615215bf7af"
+
module-deps@^4.0.8:
version "4.1.1"
resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd"
|