diff --git a/README.md b/README.md index 207169f..d6eca04 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # Overview : Generate CSV document according to given data. @@ -35,7 +36,7 @@ This data can be *array of arrays*, *array of literal objects* or *string*. **.i.e (array of arrays)** ```js //array of arrays : each item is rendered as csv line -data = =[ +data = [ ['firstname', 'lastname', 'email'] , ['Ahmed', 'Tomi' , 'ah@smthing.co.com'] , ['Raed', 'Labes' , 'rl@smthing.co.com'] , diff --git a/package.json b/package.json index 3522b24..5da01ba 100644 --- a/package.json +++ b/package.json @@ -44,12 +44,17 @@ "babel-preset-es2015": "^6.18.0", "babel-preset-react": "^6.16.0", "babel-preset-stage-0": "^6.16.0", + "console-info": "0.0.4", "coveralls": "^2.11.15", "enzyme": "^2.6.0", "expect": "^1.20.2", + "jsdom": "9.8.3", + "jsdom-global": "2.1.0", "mocha": "^3.2.0", "mocha-lcov-reporter": "^1.2.0", "react": "^15.4.1", + "react-addons-test-utils": "^15.4.1", + "react-dom": "^15.4.1", "sinon": "^1.17.6" }, "dependencies": { diff --git a/src/components/Download.js b/src/components/Download.js index 11e10db..18992a9 100644 --- a/src/components/Download.js +++ b/src/components/Download.js @@ -8,13 +8,19 @@ const defaultProps = { class CSVDownload extends React.Component { constructor(props) { super(props); + this.state={}; } + + buildURI() { + return buildURI(...arguments); + } + componentDidMount(){ - this.page = window.open(buildURI(this.props.data, this.props.headers), this.props.target, this.props.specs, this.props.replace); + this.state.page = window.open(this.buildURI(this.props.data, this.props.headers), this.props.target, this.props.specs, this.props.replace); } getWindow() { - return this.page; + return this.state.page; } render(){ diff --git a/src/components/Link.js b/src/components/Link.js index 0e3da47..17752e1 100644 --- a/src/components/Link.js +++ b/src/components/Link.js @@ -6,12 +6,17 @@ import XObject from 'x-object/safe'; class CSVLink extends React.Component { constructor(props) { super(props); + this.buildURI= this.buildURI.bind(this); + } + + buildURI() { + return buildURI(...arguments); } render(){ return ( !PropsNotForwarded.includes(k))} - href={buildURI(this.props.data, this.props.headers)}> + href={this.buildURI(this.props.data, this.props.headers)}> {this.props.children} ) diff --git a/test/ComponentsSpec.js b/test/ComponentsSpec.js new file mode 100644 index 0000000..bf5e57b --- /dev/null +++ b/test/ComponentsSpec.js @@ -0,0 +1,121 @@ +import 'jsdom-global/register'; +import React from 'react'; +import { + mount, + shallow +} from 'enzyme'; +import sinon from 'sinon'; +import expect from 'expect'; + +import { + CSVLink, + CSVDownload +} from '../src'; +import {buildURI} from '../src/core'; +import 'console-info'; + + +const getAttrs =((htmlElment) => Array.from( + htmlElment.attributes + ).reduce((a, b) => { + a[b.name ==='class' ? 'className':b.name ] = b.value ; + return a;} + , {}) +); + +describe('CSVLink', () => { + let minProps; + beforeEach(() => { + minProps = { + data: [ + ['X', 'Y'], + ['1', '2'], + ['3', '4'] + ] + }; + }); + it(`renders without error if required props are passed`, () => { + const wrapper = shallow( Click here ); + expect(wrapper.length).toEqual(1); + }); + + it(`renders anchor tag`, () => { + const wrapper = shallow( Click here ); + expect(wrapper.find('a').length).toEqual(1); + }); + + it(`calls "buildURI" method on mounting`, () => { + const dataURI = `data:text/csv;some,thing` + const buildURI = sinon.stub(CSVLink.prototype, 'buildURI').returns(dataURI); + const wrapper = mount( Click here ); + expect(buildURI.calledOnce).toBeTruthy(); + buildURI.restore(); + }); + it(`generates CSV download link and bind it to "href" of element`, () => { + const linkPrefix = `data:text/csv` + const wrapper = mount( Click here ); + const actualLink = wrapper.find(`a`).get(0).getAttribute('href'); + expect(actualLink.startsWith(linkPrefix)).toBeTruthy(); + }); + + it(`forwards props to anchor tag unless props is forbidden`, () => { + const extraProps = { + className:`btn`, + target:'_self' + }; + const wrapper = mount( Click here ); + const actualAnchorAttrs =getAttrs(wrapper.find(`a`).get(0)); + expect(actualAnchorAttrs).toInclude(extraProps); + + }) + + }); + + describe('CSVDownload', () => { + let minProps; + let manyProps; + beforeEach(() => { + minProps = { + data: [ + ['X', 'Y'], + ['1', '2'], + ['3', '4'] + ] + }; + manyProps= Object.assign(minProps, { + target: '_blank', + specs: 'fullscreen=yes&height=200&status=yes', + replace:'no' + }); + }); + + it(`does not render anything by default`, () => { + const wrapper = shallow( ); + expect(wrapper.props().children).toNotExist(); + }); + it(`calls "buildURI" on mounting`, () => { + const dataURI = `data:text/csv;some,thing` + const buildURI = sinon.stub(CSVDownload.prototype, 'buildURI').returns(dataURI); + const wrapper = mount( ); + expect(buildURI.calledOnce).toBeTruthy(); + buildURI.restore(); + }); + it(`redirects in different page on mounting`, () => { + const openCallback = sinon.stub(window,'open').returns({ + focus: ()=> {} + }); + const wrapper = mount( ); + expect(openCallback.calledWith(buildURI(manyProps.data), manyProps.target, manyProps.specs, manyProps.replace)).toBeTruthy(); + expect(openCallback.calledOnce).toBeTruthy(); + openCallback.restore(); + }); + + it(`persists new opened window`, () => { + const openCallback = sinon.stub(window,'open').returns('newPage'); + const wrapper = mount( ); + const actualNewWindow= wrapper.instance().getWindow(); + expect(actualNewWindow).toEqual('newPage'); + openCallback.restore(); + + }); + });