├── .babelrc ├── .codeclimate.yml ├── .editorconfig ├── .env.example ├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── package.json ├── public ├── css │ ├── spinner.css │ └── style.css ├── fonts │ └── geomanist-regular-webfont.ttf ├── images │ ├── favicon.ico │ └── react-logo.png └── index.html ├── src ├── components │ ├── commons │ │ ├── GithubButtons.js │ │ ├── PageNotFound.js │ │ └── Spinner.js │ └── home │ │ ├── ContributerItem.js │ │ ├── ContributerList.js │ │ └── index.js ├── constants │ └── routeConstants.js ├── index.js ├── routes.js ├── services │ └── githubService.js └── utils │ └── httpUtil.js ├── test ├── components │ ├── commons │ │ ├── GithubButtons.spec.js │ │ └── Spinner.spec.js │ └── home │ │ ├── ContributerItem.spec.js │ │ ├── ContributerList.spec.js │ │ └── index.spec.js └── mocks │ └── fileMock.js ├── webpack.config.js ├── webpack.config.production.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }], 4 | "es2017", 5 | "react" 6 | ], 7 | "plugins": [ 8 | "transform-runtime", 9 | "react-hot-loader/babel" 10 | ], 11 | "env": { 12 | "test": { 13 | "plugins": [ 14 | "transform-es2015-modules-commonjs" 15 | ] 16 | }, 17 | "production": { 18 | "plugins": [ 19 | "transform-react-remove-prop-types" 20 | ] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | eslint: 3 | channel: eslint-3 4 | enabled: true 5 | csslint: 6 | enabled: true 7 | duplication: 8 | enabled: true 9 | config: 10 | languages: 11 | - javascript 12 | fixme: 13 | enabled: true 14 | ratings: 15 | paths: 16 | - src/** 17 | exclude_paths: 18 | - dist/* 19 | - test/**/* 20 | - public/**/* 21 | - coverage/**/* 22 | - node_modules/**/* 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | indent_style = space 9 | insert_final_newline = true 10 | 11 | # Indentation override for all JS 12 | indent_size = 2 13 | 14 | # Matches the exact files either package.json or .travis.yml 15 | [{package.json,.travis.yml}] 16 | indent_style = space 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # BASE HREF 2 | BASE_NAME=react-starter 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "jest": true, 6 | "browser": true, 7 | "commonjs": true 8 | }, 9 | "extends": ["eslint:recommended", "plugin:react/recommended"], 10 | "parserOptions": { 11 | "ecmaVersion": 2017, 12 | "ecmaFeatures": { 13 | "experimentalObjectRestSpread": true, 14 | "jsx": true 15 | }, 16 | "sourceType": "module" 17 | }, 18 | "plugins": [ 19 | "react" 20 | ], 21 | "rules": { 22 | "indent": ["error", 2], 23 | "semi": ["error", "always"], 24 | "quotes": ["error", "single"], 25 | "react/jsx-uses-vars": ["error"], 26 | "react/jsx-uses-react": ["error"], 27 | "linebreak-style": ["error", "unix"], 28 | "padded-blocks": ["error", {"classes": "always"}], 29 | "comma-spacing": ["error", {"before": false, "after": true}] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies # 2 | node_modules 3 | 4 | # Distribution folder # 5 | dist 6 | 7 | # Test Coverage folder # 8 | coverage 9 | 10 | # Log files # 11 | *.log 12 | 13 | # Environment variables # 14 | .env 15 | 16 | # IDE Configurations 17 | .idea/ 18 | 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "7" 5 | cache: yarn 6 | 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2016-2017 Pratish Shrestha 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Repo Archived 2 | Use this instead 3 | [React Boilerplate](https://github.com/pratishshr/react-boilerplate) 4 | 5 | # React Starter 6 | [![Build Status](https://travis-ci.org/pratishshr/react-starter.svg?branch=master)](https://travis-ci.org/pratishshr/react-starter) 7 | [![Code Climate](https://codeclimate.com/github/pratishshr/react-starter/badges/gpa.svg)](https://codeclimate.com/github/pratishshr/react-starter) 8 | 9 | Simple starter kit for React projects. 10 | 11 | Comes with: 12 | 13 | - [Webpack](https://webpack.js.org/) 14 | - [React Router](https://reacttraining.com/react-router/) 15 | - ES7 [Async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)/[Await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) 16 | - [Hot Module Replacement](https://webpack.js.org/guides/hmr-react/) 17 | - SASS/CSS Support 18 | - [Axios](https://github.com/mzabriskie/axios) HTTP Client 19 | - [Jest](https://facebook.github.io/jest/) for Testing 20 | - [Dotenv](https://www.npmjs.com/package/dotenv) for Environment Configurations 21 | 22 | ## Install 23 | 24 | Clone the repository and install dependencies using [yarn](http://yarnpkg.com/). 25 | 26 | ```bash 27 | $ git clone git@github.com:pratishshr/react-starter.git 28 | 29 | $ cd 30 | 31 | $ yarn 32 | ``` 33 | 34 | Start the application in development mode using `yarn start` 35 | 36 | ## Scripts 37 | 38 | The following [commands](package.json) are available: 39 | 40 | |Name |Description | 41 | |-----------------|--------------------------------------------------| 42 | |start | Start application using webpack-dev-server | 43 | |lint | Run linter | 44 | |lint:fix | Run linter and try to fix errors | 45 | |test | Run tests | 46 | |test:coverage | Run tests and show code coverage report | 47 | |build | Development build | 48 | |build:production | Production build | 49 | 50 | Run a command using `yarn ` 51 | 52 | ## Contribution 53 | 54 | For contributions and feature requests, please create an [issue](https://github.com/pratishshr/react-starter/issues). 55 | 56 | ## License 57 | 58 | [MIT](LICENSE.md) 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-starter", 3 | "version": "1.0.0", 4 | "description": "Starter kit for React projects", 5 | "main": "src/index.js", 6 | "repository": { 7 | "url": "git@github.com:pratishshr/react-starter.git", 8 | "type": "git" 9 | }, 10 | "private": true, 11 | "author": "pratishshr ", 12 | "license": "MIT", 13 | "scripts": { 14 | "test": "jest", 15 | "prestart": "yarn build", 16 | "predeploy": "yarn build", 17 | "lint": "eslint src test", 18 | "deploy": "gh-pages -d dist", 19 | "start": "webpack-dev-server", 20 | "test:coverage": "jest --coverage", 21 | "lint:fix": "eslint src test --fix", 22 | "build": "rimraf dist && webpack --progress --colors", 23 | "build:production": "rimraf dist && NODE_ENV=production webpack --config ./webpack.config.production.js --progress --colors -p" 24 | }, 25 | "engines": { 26 | "node": ">= 6.9.0", 27 | "npm": ">= 3.10.8" 28 | }, 29 | "dependencies": { 30 | "axios": "^0.15.3", 31 | "dotenv": "^4.0.0", 32 | "react": "^15.4.2", 33 | "react-dom": "^15.4.2", 34 | "react-router-dom": "^4.0.0" 35 | }, 36 | "devDependencies": { 37 | "babel-core": "^6.24.0", 38 | "babel-jest": "^19.0.0", 39 | "babel-loader": "^6.4.1", 40 | "babel-plugin-transform-es2015-modules-commonjs": "^6.24.0", 41 | "babel-plugin-transform-react-remove-prop-types": "^0.3.3", 42 | "babel-plugin-transform-runtime": "^6.23.0", 43 | "babel-preset-es2015": "^6.24.0", 44 | "babel-preset-es2017": "^6.22.0", 45 | "babel-preset-react": "^6.23.0", 46 | "css-loader": "^0.27.3", 47 | "enzyme": "^2.7.1", 48 | "eslint": "^3.16.1", 49 | "eslint-plugin-react": "^6.10.0", 50 | "extract-text-webpack-plugin": "^2.1.0", 51 | "file-loader": "^0.10.1", 52 | "html-webpack-plugin": "^2.28.0", 53 | "identity-obj-proxy": "^3.0.0", 54 | "jest": "^19.0.2", 55 | "node-sass": "^4.5.0", 56 | "postcss-loader": "^1.3.3", 57 | "react-addons-test-utils": "^15.4.2", 58 | "react-hot-loader": "3.0.0-beta.6", 59 | "rimraf": "^2.6.1", 60 | "sass-loader": "^6.0.2", 61 | "style-loader": "^0.16.1", 62 | "webpack": "^2.3.2", 63 | "webpack-dev-server": "^2.4.2" 64 | }, 65 | "jest": { 66 | "moduleNameMapper": { 67 | "\\.(jpg|jpeg|png|ico|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/mocks/fileMock.js", 68 | "\\.(css|less)$": "identity-obj-proxy" 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /public/css/spinner.css: -------------------------------------------------------------------------------- 1 | .loader, 2 | .loader:before, 3 | .loader:after { 4 | border-radius: 50%; 5 | width: 2.5em; 6 | height: 2.5em; 7 | -webkit-animation-fill-mode: both; 8 | animation-fill-mode: both; 9 | -webkit-animation: load7 1.8s infinite ease-in-out; 10 | animation: load7 1.8s infinite ease-in-out; 11 | } 12 | 13 | .loader { 14 | color: #ffffff; 15 | font-size: 10px; 16 | margin: 0 auto; 17 | position: relative; 18 | text-indent: -9999em; 19 | -webkit-transform: translateZ(0); 20 | -ms-transform: translateZ(0); 21 | transform: translateZ(0); 22 | -webkit-animation-delay: -0.16s; 23 | animation-delay: -0.16s; 24 | } 25 | 26 | .loader:before, 27 | .loader:after { 28 | content: ''; 29 | position: absolute; 30 | top: 0; 31 | } 32 | 33 | .loader:before { 34 | left: -3.5em; 35 | -webkit-animation-delay: -0.32s; 36 | animation-delay: -0.32s; 37 | } 38 | 39 | .loader:after { 40 | left: 3.5em; 41 | } 42 | 43 | @-webkit-keyframes load7 { 44 | 0%, 45 | 80%, 46 | 100% { 47 | box-shadow: 0 2.5em 0 -1.3em; 48 | } 49 | 40% { 50 | box-shadow: 0 2.5em 0 0; 51 | } 52 | } 53 | 54 | @keyframes load7 { 55 | 0%, 56 | 80%, 57 | 100% { 58 | box-shadow: 0 2.5em 0 -1.3em; 59 | } 60 | 40% { 61 | box-shadow: 0 2.5em 0 0; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'geomanist'; 3 | src: url(../fonts/geomanist-regular-webfont.ttf); 4 | } 5 | 6 | body { 7 | color: #ffffff; 8 | background: #0C7EAF; 9 | font-family: geomanist, "Helvetica Neue"; 10 | } 11 | 12 | .logo-container { 13 | text-align: center; 14 | } 15 | 16 | .logo-container h1 { 17 | margin: 10px auto; 18 | font-size: 80px; 19 | } 20 | 21 | .logo-container img { 22 | width: 200px; 23 | height: 200px; 24 | display: block; 25 | margin: 0 auto; 26 | } 27 | 28 | .github-buttons-wrapper { 29 | padding: 20px; 30 | text-align: center; 31 | } 32 | 33 | .contributer-list { 34 | display: flex; 35 | justify-content: center; 36 | padding: 0 200px; 37 | } 38 | 39 | .contributer-item img { 40 | width: 80px; 41 | border-radius: 50%; 42 | height: 80px; 43 | border: 2px solid white; 44 | margin: 4px; 45 | } 46 | 47 | .section h2 { 48 | text-align: center; 49 | } 50 | 51 | .not-found-container { 52 | text-align: center; 53 | } 54 | 55 | .not-found-container .not-found-status { 56 | font-size: 100px; 57 | } 58 | 59 | .not-found-container .not-found-message { 60 | font-size: 60px; 61 | } 62 | -------------------------------------------------------------------------------- /public/fonts/geomanist-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pratishshr/react-starter/3f8bc3d85f7fa51d4d5b2c03ab4bb5f80c844ffb/public/fonts/geomanist-regular-webfont.ttf -------------------------------------------------------------------------------- /public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pratishshr/react-starter/3f8bc3d85f7fa51d4d5b2c03ab4bb5f80c844ffb/public/images/favicon.ico -------------------------------------------------------------------------------- /public/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pratishshr/react-starter/3f8bc3d85f7fa51d4d5b2c03ab4bb5f80c844ffb/public/images/react-logo.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Starter 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/commons/GithubButtons.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | class GithubButtons extends Component { 4 | 5 | render() { 6 | let { user, repo } = this.props; 7 | let starUrl = `https://ghbtns.com/github-btn.html?user=${user}&repo=${repo}&type=star&count=true&size=large`; 8 | let forkUrl = `https://ghbtns.com/github-btn.html?user=${user}&repo=${repo}&type=fork&count=true&size=large`; 9 | 10 | return ( 11 |
12 | 20 | 28 |
29 | ); 30 | } 31 | 32 | } 33 | 34 | GithubButtons.propTypes = { 35 | user: PropTypes.string.isRequired, 36 | repo: PropTypes.string.isRequired 37 | }; 38 | 39 | export default GithubButtons; 40 | -------------------------------------------------------------------------------- /src/components/commons/PageNotFound.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import reactLogo from '../../../public/images/react-logo.png'; 4 | 5 | class PageNotFound extends Component { 6 | 7 | render() { 8 | return ( 9 |
10 |
11 | react-logo 12 |
13 |
404
14 |
Page Not Found
15 |
16 | ); 17 | } 18 | 19 | } 20 | 21 | export default PageNotFound; 22 | -------------------------------------------------------------------------------- /src/components/commons/Spinner.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import '../../../public/css/spinner.css'; 3 | 4 | const Spinner = (() => { 5 | 6 | return ( 7 |
Loading...
8 | ); 9 | 10 | }); 11 | 12 | export default Spinner; 13 | -------------------------------------------------------------------------------- /src/components/home/ContributerItem.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | const ContributerItem = ({ profileUrl, imageUrl }) => { 4 | 5 | return ( 6 |
7 | 8 | contributer avatar 9 | 10 |
11 | ); 12 | 13 | }; 14 | 15 | ContributerItem.propTypes = { 16 | imageUrl: PropTypes.string.isRequired, 17 | profileUrl: PropTypes.string.isRequired 18 | }; 19 | 20 | export default ContributerItem; 21 | -------------------------------------------------------------------------------- /src/components/home/ContributerList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import * as githubService from '../../services/githubService'; 4 | 5 | import Spinner from '../commons/Spinner'; 6 | import ContributerItem from './ContributerItem'; 7 | 8 | class ContributerList extends Component { 9 | 10 | constructor() { 11 | super(); 12 | 13 | this.state = { 14 | isLoading: false, 15 | contributers: [] 16 | }; 17 | } 18 | 19 | async componentDidMount() { 20 | this.setState({ isLoading: true }); 21 | 22 | let response = await githubService.fetchContributers(); 23 | 24 | this.setState({ 25 | isLoading: false, 26 | contributers: response.data 27 | }); 28 | } 29 | 30 | render() { 31 | return ( 32 |
33 |

Contributers

34 |
35 | {!this.state.isLoading ? 36 | this.state.contributers.map(contributer => 37 | 42 | ) : 43 | } 44 |
45 |
46 | ); 47 | } 48 | 49 | } 50 | 51 | export default ContributerList; 52 | -------------------------------------------------------------------------------- /src/components/home/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import '../../../public/images/favicon.ico'; 4 | import reactLogo from '../../../public/images/react-logo.png'; 5 | 6 | import ContributerList from './ContributerList'; 7 | import GithubButtons from '../commons/GithubButtons'; 8 | 9 | const USER_NAME = 'pratishshr'; 10 | const REPO_NAME = 'react-starter'; 11 | 12 | class Home extends Component { 13 | 14 | render() { 15 | return ( 16 |
17 |
18 | react-logo 19 |

React Starter

20 | 21 |
22 | 23 | 24 |
25 | ); 26 | } 27 | 28 | } 29 | 30 | export default Home; 31 | -------------------------------------------------------------------------------- /src/constants/routeConstants.js: -------------------------------------------------------------------------------- 1 | const routeConstants = { 2 | HOME: '/', 3 | GUIDES: '/guides' 4 | }; 5 | 6 | export default routeConstants; 7 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import '../public/css/style.css'; 2 | 3 | import React from 'react'; 4 | import { render } from 'react-dom'; 5 | import { AppContainer } from 'react-hot-loader'; 6 | 7 | import Router from './routes'; 8 | 9 | const mountNode = document.getElementById('app-container'); 10 | 11 | const renderApp = () => { 12 | render( 13 | 14 | 15 | , 16 | mountNode 17 | ); 18 | }; 19 | 20 | renderApp(); 21 | 22 | // Hot Module Replacement API 23 | if (module.hot) { 24 | module.hot.accept('./routes', () => renderApp()); 25 | } 26 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter, Route, Switch } from 'react-router-dom'; 3 | 4 | import routeConstants from './constants/routeConstants'; 5 | 6 | import Home from './components/home'; 7 | import PageNotFound from './components/commons/PageNotFound'; 8 | 9 | const baseHref = process.env.BASE_HREF || '/'; 10 | 11 | const Router = () => ( 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 |
20 | ); 21 | 22 | export default Router; 23 | -------------------------------------------------------------------------------- /src/services/githubService.js: -------------------------------------------------------------------------------- 1 | import * as httpUtil from '../utils/httpUtil'; 2 | 3 | const USER_NAME = 'pratishshr'; 4 | const REPO_NAME = 'react-starter'; 5 | 6 | const API_BASE_URL = 'https://api.github.com'; 7 | const RESOURCE_CONTRIBUTERS = `${API_BASE_URL}/repos/${USER_NAME}/${REPO_NAME}/contributors`; 8 | 9 | export function fetchContributers() { 10 | return httpUtil.get(RESOURCE_CONTRIBUTERS); 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/utils/httpUtil.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export function get(url, params = {}) { 4 | return axios({ 5 | method: 'get', 6 | url: url, 7 | params: params 8 | }); 9 | } 10 | 11 | export function post(url, data) { 12 | return axios({ 13 | method: 'post', 14 | url: url, 15 | data: data 16 | }); 17 | } 18 | 19 | export function put(url, data) { 20 | return axios({ 21 | method: 'put', 22 | url: url, 23 | data: data 24 | }); 25 | } 26 | 27 | export function remove(url) { 28 | return axios({ 29 | method: 'delete', 30 | url: url 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /test/components/commons/GithubButtons.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shallow} from 'enzyme'; 3 | 4 | import GithubButtons from '../../../src/components/commons/GithubButtons'; 5 | 6 | describe('', ()=> { 7 | it('renders App component successfully', ()=> { 8 | const wrapper = shallow(); 9 | expect(wrapper.length).toEqual(1); 10 | }); 11 | 12 | it('renders two iframes', ()=> { 13 | const wrapper = shallow(); 14 | expect(wrapper.find('iframe').length).toEqual(2); 15 | }); 16 | 17 | it('first iframe url contains starUrl', ()=> { 18 | const user = 'dummyUser'; 19 | const repo = 'dummyRepo'; 20 | const wrapper = shallow(); 21 | expect(wrapper.find('iframe').get(0).props.src).toBe(`https://ghbtns.com/github-btn.html?user=${user}&repo=${repo}&type=star&count=true&size=large`); 22 | }); 23 | 24 | it('first iframe url contains starUrl', ()=> { 25 | const user = 'dummyUser'; 26 | const repo = 'dummyRepo'; 27 | const wrapper = shallow(); 28 | expect(wrapper.find('iframe').get(1).props.src).toBe(`https://ghbtns.com/github-btn.html?user=${user}&repo=${repo}&type=fork&count=true&size=large`); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/components/commons/Spinner.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shallow} from 'enzyme'; 3 | 4 | import Spinner from '../../../src/components/commons/Spinner'; 5 | 6 | describe('', ()=> { 7 | it('renders App component successfully', ()=> { 8 | const wrapper = shallow(); 9 | expect(wrapper.length).toEqual(1); 10 | }); 11 | 12 | it('has div with className loader', ()=> { 13 | const wrapper = shallow(); 14 | expect(wrapper.find('.loader').length).toEqual(1); 15 | }); 16 | 17 | it('has div with className loader with some value', ()=> { 18 | const wrapper = shallow(); 19 | expect(wrapper.find('.loader').text()).toEqual('Loading...'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/components/home/ContributerItem.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shallow} from 'enzyme'; 3 | 4 | import ContributerItem from '../../../src/components/home/ContributerItem'; 5 | 6 | describe('', ()=> { 7 | 8 | it('renders home component successfully', ()=> { 9 | const wrapper = shallow(); 10 | expect(wrapper.length).toEqual(1); 11 | }); 12 | 13 | it('should have tag', ()=> { 14 | const wrapper = shallow(); 15 | expect(wrapper.find('a').length).toEqual(1); 16 | }); 17 | 18 | it('should have href same as sent in the props', ()=> { 19 | const wrapper = shallow(); 20 | expect(wrapper.find('a').href).toEqual(wrapper.prop('profileUrl')); 21 | }); 22 | 23 | it('should have img tag', ()=> { 24 | const wrapper = shallow(); 25 | expect(wrapper.find('img').length).toEqual(1); 26 | }); 27 | 28 | it('should have img src same as sent in the props', ()=> { 29 | const wrapper = shallow(); 30 | expect(wrapper.find('img').src).toEqual(wrapper.prop('imageUrl')); 31 | }); 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /test/components/home/ContributerList.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shallow} from 'enzyme'; 3 | 4 | import Spinner from '../../../src/components/commons/Spinner'; 5 | import ContributerList from '../../../src/components/home/ContributerList'; 6 | import ContributerItem from '../../../src/components/home/ContributerItem'; 7 | 8 | const contributors = [ 9 | { 10 | id: 1, 11 | html_url: 'http://html-url-1.com', 12 | avatar_url: 'http://avatar_url-1.com' 13 | }, 14 | { 15 | id: 2, 16 | html_url: 'http://html-url-2.com', 17 | avatar_url: 'http://avatar_url-2.com' 18 | } 19 | ]; 20 | 21 | describe('', ()=> { 22 | 23 | it('renders home component successfully', ()=> { 24 | const wrapper = shallow(); 25 | expect(wrapper.length).toEqual(1); 26 | }); 27 | 28 | it('renders when value are provided to the contributers', ()=> { 29 | const wrapper = shallow(); 30 | wrapper.setState({isLoading: false, contributers: contributors}); 31 | setTimeout(()=>expect(wrapper.find().length).toEqual(2), 0); 32 | }); 33 | 34 | it('does not render when value are provided to the contributers', ()=> { 35 | const wrapper = shallow(); 36 | expect(wrapper.find().length).toEqual(0); 38 | }); 39 | 40 | it('renders when state isLoading is true', ()=> { 41 | const wrapper = shallow(); 42 | wrapper.setState({isLoading: true}); 43 | setTimeout(()=>expect(wrapper.find().length).toEqual(1), 0); 44 | }); 45 | 46 | it('does not render when state isLoading is true', ()=> { 47 | const wrapper = shallow(); 48 | wrapper.setState({isLoading: false}); 49 | setTimeout(()=>expect(wrapper.find().length).toEqual(0), 0); 50 | }); 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /test/components/home/index.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shallow} from 'enzyme'; 3 | 4 | import Home from '../../../src/components/home'; 5 | import GithubButtons from '../../../src/components/commons/GithubButtons'; 6 | import ContributerList from '../../../src/components/home/ContributerList'; 7 | 8 | describe('', ()=> { 9 | it('renders home component successfully', ()=> { 10 | const wrapper = shallow(); 11 | expect(wrapper.length).toEqual(1); 12 | }); 13 | 14 | it('should have img tag', ()=> { 15 | const wrapper = shallow(); 16 | expect(wrapper.find('img').length).toEqual(1); 17 | }); 18 | 19 | it('should have h1 tag with a text', ()=>{ 20 | const wrapper = shallow(); 21 | const expectedText = 'React Starter'; 22 | expect(wrapper.find('h1').text()).toEqual(expectedText); 23 | }); 24 | 25 | it('should contain component', ()=> { 26 | const wrapper = shallow(); 27 | expect(wrapper.find(GithubButtons).length).toEqual(1); 28 | }); 29 | 30 | it('should contain component', ()=>{ 31 | const wrapper = shallow(); 32 | expect(wrapper.find(ContributerList).length).toEqual(1); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const dotenv = require('dotenv'); 2 | const webpack = require('webpack'); 3 | const resolve = require('path').resolve; 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | 7 | // Initialize environment variables 8 | dotenv.config(); 9 | 10 | module.exports = { 11 | entry: [ 12 | 'react-hot-loader/patch', 13 | 'webpack-dev-server/client?http://localhost:8181', // WebpackDevServer host and port 14 | 'webpack/hot/only-dev-server', // 'only' prevents reload on syntax errors 15 | './index.js'// the entry point of our app 16 | ], 17 | output: { 18 | publicPath: '/', 19 | filename: 'js/bundle.js', // the output bundle 20 | chunkFilename: '[id].js', 21 | path: resolve(__dirname, 'dist') 22 | }, 23 | context: resolve(__dirname, 'src'), 24 | devtool: 'inline-source-map', 25 | devServer: { 26 | port: 8181, 27 | hot: true, // enable HMR on the server 28 | publicPath: '/', // match the output 'publicPath' 29 | historyApiFallback: true, // respond to 404s with index.html 30 | contentBase: resolve(__dirname, 'dist') // match the output path 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.js$/, 36 | exclude: /node_modules/, 37 | use: ['babel-loader'] 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) 42 | }, 43 | { 44 | test: /\.(jpe?g|png|gif|svg)$/i, 45 | use: 'file-loader?name=[name].[ext]&publicPath=/images/&outputPath=/images/' 46 | }, 47 | { 48 | test: /\.ico$/, 49 | use: 'file-loader?name=[name].[ext]&publicPath=/images/&outputPath=/' 50 | }, 51 | { 52 | test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, 53 | use: 'file-loader?name=[name].[ext]&publicPath=/fonts/&outputPath=/fonts/' 54 | }, 55 | { 56 | test: /\.scss$/, 57 | use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: ['css-loader', 'sass-loader'] }) 58 | } 59 | ] 60 | }, 61 | plugins: [ 62 | new ExtractTextPlugin('css/bundle.css'), 63 | new HtmlWebpackPlugin({ 64 | template: resolve(__dirname, 'public/index.html') 65 | }), 66 | new webpack.DefinePlugin({ 67 | 'process.env.NODE_ENV': JSON.stringify('production') 68 | }), 69 | new webpack.NamedModulesPlugin(), // prints more readable module names in the browser console on HMR updates 70 | new webpack.NoEmitOnErrorsPlugin(), // do not emit compiled assets that include errors 71 | new webpack.HotModuleReplacementPlugin() // enable HMR globally 72 | ] 73 | }; 74 | -------------------------------------------------------------------------------- /webpack.config.production.js: -------------------------------------------------------------------------------- 1 | const dotenv = require('dotenv'); 2 | const webpack = require('webpack'); 3 | const resolve = require('path').resolve; 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | 6 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 7 | 8 | // Initialize environment variables 9 | dotenv.config(); 10 | 11 | const baseHref = process.env.BASE_HREF; 12 | 13 | module.exports = { 14 | entry: './index.js', // the entry point of our app 15 | output: { 16 | publicPath: `/${process.env.BASE_HREF}`, 17 | chunkFilename: '[id].js', 18 | path: resolve(__dirname, 'dist'), 19 | filename: 'js/bundle.[chunkhash].js' // the output bundle 20 | }, 21 | context: resolve(__dirname, 'src'), 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.js$/, 26 | exclude: /node_modules/, 27 | use: ['babel-loader'] 28 | }, 29 | { 30 | test: /\.css$/, 31 | use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) 32 | }, 33 | { 34 | test: /\.(jpe?g|png|gif|svg)$/i, 35 | use: `file-loader?name=[name].[ext]&publicPath=/${baseHref}/images/&outputPath=/images/` 36 | }, 37 | { 38 | test: /\.ico$/, 39 | use: 'file-loader?name=[name].[ext]&publicPath=/images/&outputPath=/' 40 | }, 41 | { 42 | test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, 43 | use: `file-loader?name=[name].[ext]&publicPath=/${baseHref}/fonts/&outputPath=/fonts/` 44 | }, 45 | { 46 | test: /\.scss$/, 47 | use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: ['css-loader', 'sass-loader'] }) 48 | } 49 | ] 50 | }, 51 | plugins: [ 52 | new ExtractTextPlugin('css/bundle.css'), 53 | new HtmlWebpackPlugin({ 54 | template: resolve(__dirname, 'public/index.html') 55 | }), 56 | new webpack.DefinePlugin({ 57 | 'process.env.NODE_ENV': JSON.stringify('production'), 58 | 'process.env.BASE_HREF': JSON.stringify(process.env.BASE_HREF) 59 | }), 60 | new webpack.optimize.UglifyJsPlugin() 61 | ] 62 | }; 63 | --------------------------------------------------------------------------------