-
-
Save dschinkel/94db9bd80a556ff4d0ed7e1385cfe02a to your computer and use it in GitHub Desktop.
// 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> | |
) | |
} | |
} |
this needs to be cleaned up more but...anyway:
import React, { Component } from 'react';
const ReactDOM = require('react-dom'),
Scroll = require('react-scroll'),
Link = Scroll.Link;
export default class TableOfContents extends Component {
constructor() {
super();
this.state = {rendered: false}
}
componentDidMount() {
this.setState({rendered: true})
setTimeout(() => {
const el = ReactDOM.findDOMNode(this);
if(el && document.getElementById("interviewHeading")) {
new Ink.UI.Sticky(el, {topElement: "#interviewHeading", bottomElement: "#footer", });
}
}, 200);
}
render(){
if (!this.state.rendered) {
return null;
}
const teamProfile = document.getElementById("ft-team-profile"),
challenges = document.getElementById("ft-tdd-challenges"),
culture = document.getElementById("ft-team-culture"),
methodology = document.getElementById("ft-dev-methodology"),
workflow = document.getElementById("ft-dev-workflow"),
qa = document.getElementById("ft-qa"),
testTypes = document.getElementById("ft-test-types"),
practice = document.getElementById("ft-tdd-practice"),
benefits = document.getElementById("ft-tdd-benefits"),
ci = document.getElementById("ft-continuous-integration"),
apprenticeships = document.getElementById("ft-apprenticeships"),
learning = document.getElementById("ft-learning"),
hiring = document.getElementById("ft-hiring"),
refactoring = document.getElementById("ft-tdd-refactoring"),
relatedLinks = document.getElementById("ft-relatedLinks");
return (
<div className="ft-table-of-contents margin-top-20">
<p className="margin-8">
<span className="blue medium">
<a href="/" id="1001">HOME</a>
</span>
</p>
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1002" to='ft-company-profile' spy={true} smooth={true} duration={500}>Company Profile</Link>
</span>
</p>
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1003" to='interviewee' spy={true} smooth={true} duration={500}>Interviewee</Link>
</span>
</p>
{ teamProfile &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1004" to='ft-team-profile' spy={true} smooth={true}
duration={500}>Team Profile</Link>
</span>
</p>
}
{ culture &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1005" to='ft-team-culture' spy={true} smooth={true}
duration={500}>Culture, Principles, Practices</Link>
</span>
</p>
}
{ methodology &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1006" to='ft-dev-methodology' spy={true} smooth={true}
duration={500}>Development Methodology</Link>
</span>
</p>
}
{ workflow &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1007" to='ft-dev-workflow' spy={true} smooth={true}
duration={500}>Development Workflow</Link>
</span>
</p>
}
{ qa &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1008" to='ft-qa' spy={true} smooth={true} duration={500}>QA</Link>
</span>
</p>
}
{ testTypes &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1009" to='ft-test-types' spy={true} smooth={true}
duration={500}>Types of Tests</Link>
</span>
</p>
}
{ practice &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1010" to='ft-tdd-practice' spy={true} smooth={true}
duration={500}>Practice & Style</Link>
</span>
</p>
}
{ refactoring &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1010" to='ft-tdd-refactoring' spy={true} smooth={true}
duration={500}>Refactoring</Link>
</span>
</p>
}
{ benefits &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1012" to='ft-tdd-benefits' spy={true} smooth={true}
duration={500}>Benefits</Link>
</span>
</p>
}
{ challenges &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" to='ft-tdd-challenges' spy={true} smooth={true} duration={500}>Challenges</Link>
</span>
</p>
}
{ ci &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1013" to='ft-continuous-integration' spy={true} smooth={true}
duration={500}>Continuous Integration</Link>
</span>
</p>
}
{ apprenticeships &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1014" to='ft-apprenticeships' spy={true} smooth={true} duration={500}>Apprenticeships / Onboarding</Link>
</span>
</p>
}
{ learning &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1015" to='ft-learning' spy={true} smooth={true} duration={500}>Learning</Link>
</span>
</p>
}
{ hiring &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1016" to='ft-hiring' spy={true} smooth={true} duration={500}>Hiring</Link>
</span>
</p>
}
{ relatedLinks &&
<p className="margin-8">
<span className="medium">
<Link activeClass="focused" id="1017" to='relatedLinks' spy={true} smooth={true} duration={500}>Related Links</Link>
</span>
</p>
}
<p className="margin-top-40 margin-left-10"><a href="https://www.patreon.com/WeDoTDD" id="9000" target="_blank"><img src="/lib/assets/social/patreon-small.jpg" alt="logo" /></a></p>
</div>
)
}
}
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);
}
});
});
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;
});
});
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).
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);
});
})
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);
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>
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>)
}
}
using NGINX
{
"root": "dist/client/",
"routes": {
"/**": "index.html"
},
"proxies": {
"/api/": {
"origin": "https://wedotdd-service.herokuapp.com"
}
}
}
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>
)
}
}
this is a very top level component that renders each major part of the TDD Company Interviews that you see on WeDoTDD.com.