├── .gitignore ├── src ├── __tests__ │ ├── mock.js │ └── shallow.js ├── utils.js └── list-github-repos.js ├── .babelrc └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /src/__tests__/mock.js: -------------------------------------------------------------------------------- 1 | test('works', () => {}) 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-react", "babel-preset-env"], 3 | "plugins": ["babel-plugin-transform-class-properties"] 4 | } 5 | -------------------------------------------------------------------------------- /src/__tests__/shallow.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Enzyme, {shallow} from 'enzyme' 3 | import Adapter from 'enzyme-adapter-react-16' 4 | import {ListGitHubRepos} from '../list-github-repos' 5 | 6 | Enzyme.configure({adapter: new Adapter()}) 7 | 8 | test('shallow', () => { 9 | const wrapper = shallow() 10 | console.log(wrapper.debug()) 11 | }) 12 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | function searchRepos(query) { 4 | return axios({ 5 | url: 'https://api.github.com/graphql', 6 | method: 'post', 7 | data: { 8 | query: `{ 9 | search(first: 10,type:REPOSITORY, query:"${query}") { 10 | nodes { 11 | ... on Repository { 12 | nameWithOwner 13 | homepageUrl 14 | url 15 | } 16 | } 17 | } 18 | }` 19 | }, 20 | headers: { 21 | Authorization: `bearer 102307c997ceb0fd9a4bc6c3457473c91f2b0b6f` 22 | } 23 | }).then(response => { 24 | return response.data.data.search.nodes 25 | }) 26 | } 27 | 28 | export {searchRepos} 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "never-use-shallow-rendering", 3 | "version": "1.0.0", 4 | "description": "Example of why you shouldn't use shallow rendering", 5 | "main": "index.js", 6 | "dependencies": { 7 | "axios": "^0.18.0", 8 | "react-dom": "^16.4.1", 9 | "react": "^16.4.1" 10 | }, 11 | "devDependencies": { 12 | "babel-core": "^6.26.3", 13 | "babel-plugin-transform-class-properties": "^6.24.1", 14 | "babel-preset-env": "^1.7.0", 15 | "babel-preset-react": "^6.24.1", 16 | "enzyme": "^3.3.0", 17 | "enzyme-adapter-react-16": "^1.1.1", 18 | "jest": "^23.2.0", 19 | "react-testing-library": "^4.0.2" 20 | }, 21 | "scripts": { 22 | "test": "jest" 23 | }, 24 | "keywords": [], 25 | "author": "Kent C. Dodds (http://kentcdodds.com/)", 26 | "license": "GPLv3" 27 | } 28 | -------------------------------------------------------------------------------- /src/list-github-repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {searchRepos} from './utils' 3 | 4 | const callAll = (...fns) => (...args) => fns.forEach(fn => fn && fn(...args)) 5 | 6 | class SearchInput extends React.Component { 7 | static idCounter = 1 8 | id = `search-input-${SearchInput.idCounter++}` 9 | input = React.createRef() 10 | state = {error: null} 11 | handleChange = event => { 12 | clearTimeout(this.timeout) 13 | const {value} = event.target 14 | this.setState({error: null}) 15 | // delay showing the error in case the user's still typing 16 | this.timeout = setTimeout(() => { 17 | this.setState({error: this.getError(value)}) 18 | }, 400) 19 | } 20 | getError(value) { 21 | return value && value.length < 3 ? 'Must be 3 characters or longer' : null 22 | } 23 | componentWillUnmount() { 24 | clearTimeout(this.timeout) 25 | } 26 | render() { 27 | const {error} = this.state 28 | const { 29 | label, 30 | inputProps: {onChange, id = this.id, ...inputProps} 31 | } = this.props 32 | return ( 33 |
34 | 35 | 36 |
{error}
37 |
38 | ) 39 | } 40 | } 41 | 42 | class List extends React.Component { 43 | render() { 44 | const {list} = this.props 45 | return ( 46 |
47 | 54 |
55 | ) 56 | } 57 | } 58 | 59 | class Title extends React.Component { 60 | render() { 61 | return

List of GitHub repos

62 | } 63 | } 64 | 65 | class SearchForm extends React.Component { 66 | handleSubmit = async event => { 67 | event.preventDefault() 68 | const query = event.target.elements.query.value 69 | const repos = await searchRepos(query) 70 | this.props.onSearchResult(repos) 71 | } 72 | render() { 73 | return ( 74 |
75 | 76 | 77 | 78 | ) 79 | } 80 | } 81 | 82 | class ListGitHubRepos extends React.Component { 83 | state = {list: []} 84 | handleSearchResult = repos => this.setState({list: repos}) 85 | render() { 86 | const {list} = this.state 87 | return ( 88 |
89 | 90 | <SearchForm onSearchResult={this.handleSearchResult} /> 91 | <List list={list} /> 92 | </div> 93 | ) 94 | } 95 | } 96 | 97 | export {ListGitHubRepos} 98 | --------------------------------------------------------------------------------