Skip to content

Instantly share code, notes, and snippets.

@dschinkel
Last active May 3, 2017 19:17
Show Gist options
  • Save dschinkel/94db9bd80a556ff4d0ed7e1385cfe02a to your computer and use it in GitHub Desktop.
Save dschinkel/94db9bd80a556ff4d0ed7e1385cfe02a to your computer and use it in GitHub Desktop.
A few examples of React components from WeDoTDD.com
// a "dumb/presentational" React Component
import CompanyHeader from './CompanyHeader';
import CompanyProfile from './CompanyProfile';
import InterviewContentMain from './InterviewContentMain';
import Main from '../Main';
import MainLayout from '../MainLayout';
import React, { Component } from 'react';
export default class Interview extends Component{
render(){
const company = this.props.companies;
return (
<Main>
<MainLayout title={title}>
<div>
<div id='ft-interview'>
<div className="panel vertical-space">
<CompanyHeader company={company}/>
<CompanyProfile company={company}/>
<InterviewContentMain company={company}/>
</div>
</div>
</div>
</MainLayout>
</Main>
)
}
}
@dschinkel
Copy link
Author

dschinkel commented May 3, 2017

Example Integration Test. This test is not a unit test because it's testing multiple levels in the App tree.

'use strict';

import chai from 'chai';
import chaiEnzyme from 'chai-enzyme';
chai.use(chaiEnzyme());
var expect = require('chai').expect
require('../../jsdom');
import {shallow, mount} from 'enzyme';
import { ListGroup, LinkList } from "../../../client/Company/CompanyList";

describe('Homepage - Link Sets & Groups', () => {

    it("renders the correct number of Link Sets", () => {
        const country = {
                id: 1,
                name: "United States"
            },
            companies = [
                {id: 1, name: "Company A", active: true, locations: [{primary: true, country: country}]},
                {id: 2, name: "Company B", active: true, locations: [{primary: true, country: country}]},
                {id: 3, name: "Company C", active: true, locations: [{primary: true, country: country}]},
                {id: 4, name: "Company D", active: true, locations: [{primary: true, country: country}]}
            ]
        const group = mount(<ListGroup companies={companies} countries={[country]}/>),
            unitedStatesLinkSets = group.find(".ft-link-set-usa"),
            nonUnitedStatesLinkSets = group.find(".ft-link-set");

        expect(unitedStatesLinkSets.length).to.equal(2);
        expect(nonUnitedStatesLinkSets.length).to.equal(0);
    });

    it("lists headers for each country list", () => {
        const companies = [],
            countries = ["France",
                        "Germany",
                        "United Kingdom",
                        "Canada",
                        "Poland",
                        "Spain"].map((id, name) => ({id: id++, name: name}));


        for(let [index, country] of countries.entries()) {
            const company = {
                id: index + 1,
                name: "Test Company for - " + country.name,
                active: true,
                locations: [{
                    primary: true,
                    country: country
                }]
            };

            companies.push(company);
        }

        const header = shallow(<LinkList {...companies} {...countries}/>).find('.ft-company-header');

        expect(header.length).to.equal(6);

        for(let [index, header] of header.entries()){
            var headerText = header[index].find("span").text();

            expect(headerText).to.equal(countries[index].name);
        }
    });

});

@dschinkel
Copy link
Author

dschinkel commented May 3, 2017

example isolated React "dumb" component unit test

'use strict';

import React from 'react';
import chai from 'chai';
import chaiEnzyme from 'chai-enzyme';
chai.use(chaiEnzyme());
var expect = require('chai').expect
require('../../../jsdom');
import {mount} from 'enzyme';
import { LinkItem,  LinkSet } from "../../../../client/Company/CompanyList";

describe('Company - Link Set', () => {

    it('renders a link set wrapped with a company header for US companies', () => {
        const links = [],
            country = {
                id: 1,
                name: "United States"
            },
            companies = [{id: 1, name: "Company A", active: true, locations: [{primary: true, country: country}]},
                {id: 2, name: "Company B", active: true, locations: [{primary: true, country: country}]},
                {id: 3, name: "Company C", active: true, locations: [{primary: true, country: country}]},
                {id: 4, name: "Company D", active: true, locations: [{primary: true, country: country}]}];

            for(let company of companies){
                links.push(<LinkItem company={company} key={company.id}/>);
            };

            const   linkSet = mount(<LinkSet country={country} links={links} />).find('.ft-link-set-usa'),
                    header = linkSet.find('.ft-company-header-usa');

        expect(linkSet).to.exist;
        expect(header).to.not.exist;
    });
});

@dschinkel
Copy link
Author

dschinkel commented May 3, 2017

I'm in the middle of some refactoring (ripping out a few things locally at the moment) but here's a view of isolated unit tests being run. Since these tests are truly isolated (not coupled to implementation details, only the right tests break for a given bug...that's important, because brittle tests can cause much pain).

screen shot 2017-05-03 at 1 47 46 pm

@dschinkel
Copy link
Author

dschinkel commented May 3, 2017

example integration test making a real API call to my new backend Node REST API:

import React from 'react';
import chai from 'chai';
import chaiEnzyme from 'chai-enzyme';
chai.use(chaiEnzyme());
require('../../jsdom');
import { mount } from 'enzyme';
const expect = require('chai').expect;
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux';

import rootReducer from '../../../client/reducers/root';
import FeaturedCompaniesContainer from '../../../client/company/containers/FeaturedCompaniesContainer';

describe('Feature - FeaturedCompanies - Integration', () => {
    let Container;

    before(() => {
        Container = <Provider store={createStore(rootReducer, applyMiddleware(thunk))}>
                        <FeaturedCompaniesContainer />
                    </Provider>
    });

    it('returns featured companies', async () => {
        const container = await new Promise(resolve => resolve(mount(Container))),
        groups = container.find('.ft-featured-company-groups');

        expect(groups.length).to.be.greaterThan(1);
    });
})

@dschinkel
Copy link
Author

React-redux example Connected Container

/*flow*/
import React, { Component } from 'react';
import { connect } from 'react-redux';
import FeaturedCompanies from '../FeaturedCompanies';
import { fetchNewCompanies } from '../../actions/companies';


class FeaturedCompaniesContainer extends Component {
    componentDidMount(){
        if(!this.props.companies){
            fetchNewCompanies();
        }
    }
    render(){
        return(<FeaturedCompanies />)
    }
}

export const mapStateToProps = (state) => ({
    companies: state.featuredCompanies
});
        
export default connect(mapStateToProps)(FeaturedCompaniesContainer);

@dschinkel
Copy link
Author

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <link rel="alternate" type="application/rss+xml" title="We Do TDD - Companies & Teams Practicing Test Driven Development" href="http://feeds.feedburner.com/WeDoTdd-AListOfCompaniesPracticingTestDrivenDevelopment"/>
        <link rel="stylesheet" type="text/css" href="http://fastly.ink.sapo.pt/3.1.10/css/ink.min.css">
        <script type="text/javascript" src="http://fastly.ink.sapo.pt/3.1.10/js/ink-all.min.js"></script>
        <!--[if lt IE 9 ]>
        <link rel="stylesheet" href="ink-ie.min.css" type="text/css">
        <![endif]-->
        <link rel="stylesheet" type="text/css" href="/lib/css/master.css" />
    </head>
    <body>
        <div id="app"></div>
        <script type="text/javascript" src="/scripts/app.bundle.js"></script>
    </body>
</html>

@dschinkel
Copy link
Author

RelatedLinks Component (the Related Links section you see in an interview):

import React, { Component } from 'react';

export default class RelatedLinks extends Component {
    render(){
        const {company} = this.props
        return (
            <div className="all-100 padding-bottom-200">
                { company.interview.relatedLinks &&
                    <div id="ft-relatedLinks">
                        <p id="relatedLinks" className="section-heading bold padding-top-20 font-22">Related Links</p>
                        <div className="all-100 padding-left-30 align-left">
                            <div className="all-100">
                                <Links links={company.interview.relatedLinks}/>
                            </div>
                        </div>
                    </div>
                }
            </div>
        )
    }
}

export class Links extends Component {
    render(){
        const {links} = this.props;
        let formattedLink,
            formattedLinkGroup,
            formattedLinks = [],
            formattedLinkGroups = []

        for (let [i, link] of links.entries()) {
            formattedLink = <li key={i}><a href={link.href} target="_blank"><span className="black small">{link.text}</span></a></li>;
            formattedLinkGroup = <div key={i} className="all-40"><ul className="noStyle line-height-1 margin-0">{formattedLinks}</ul></div>;

            formattedLinks.push(formattedLink);

            if ((++i) % 12 === 0) {
                formattedLinkGroups.push(formattedLinkGroup);
                formattedLinks = [];
            }
        }

        if(formattedLinkGroups.length === 0){
            formattedLinkGroups.push(formattedLinkGroup);
        }

        return(<div className="ft-links column-group">{formattedLinkGroups}</div>)
    }
}

@dschinkel
Copy link
Author

using NGINX

{
  "root": "dist/client/",
  "routes": {
    "/**": "index.html"
  },
  "proxies": {
    "/api/": {
      "origin": "https://wedotdd-service.herokuapp.com"
    }
  }
}

@dschinkel
Copy link
Author

MainLayout component

const DocumentTitle = require('react-document-title');
import React, {Component} from 'react';
import Header from './Header';
import TopNav from './TopNav';


export default class MainLayout extends Component{
    render() {
        return (
            <div className="wrap">
                <div className="ink-grid">
                    <Header />
                    <DocumentTitle title={this.props.title} />
                    <div className='ft-homepage'>
                        <TopNav />
                        {this.props.children}
                    </div>
                </div>
            </div>
        )
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment