├── .npmignore ├── .gitignore ├── examples ├── tictactoe │ ├── src │ │ ├── components │ │ │ ├── Input.js │ │ │ ├── newWindow.html │ │ │ ├── Square.js │ │ │ ├── Row.js │ │ │ ├── GameList.js │ │ │ └── App.js │ │ └── index.js │ ├── scss │ │ ├── application.scss │ │ ├── _variables.scss │ │ └── _game.scss │ ├── views │ │ └── index.ejs │ ├── README.md │ ├── server │ │ ├── util.js │ │ └── db │ │ │ ├── games.test.json │ │ │ ├── games.dev.json │ │ │ └── games.js │ ├── test │ │ ├── index.js │ │ └── js │ │ │ ├── enzymeTest.js │ │ │ ├── enzyme.js │ │ │ ├── supertest.js │ │ │ ├── zombie.js │ │ │ └── unit.js │ ├── test.js │ ├── webpack.config.js │ ├── index.js │ └── package.json └── instareact-app │ ├── public │ ├── favicon.ico │ ├── profile.jpg │ ├── instalogo.png │ ├── index.html │ └── feed.css │ ├── .gitignore │ ├── package.json │ └── src │ ├── index.css │ ├── index.js │ └── jquery-3.2.1.min.js ├── src ├── demo.gif ├── BlockScreenshot.png ├── nodeStore.js ├── inject.js ├── dom-parse.js └── assert.js ├── package.json ├── LICENSE.MD ├── README.md └── test └── assert.test.js /.npmignore: -------------------------------------------------------------------------------- 1 | /examples 2 | /cli -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bundle.js -------------------------------------------------------------------------------- /examples/tictactoe/src/components/Input.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactVT/react-vt/HEAD/src/demo.gif -------------------------------------------------------------------------------- /examples/tictactoe/scss/application.scss: -------------------------------------------------------------------------------- 1 | @import '_variables'; 2 | @import '_game'; 3 | -------------------------------------------------------------------------------- /src/BlockScreenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactVT/react-vt/HEAD/src/BlockScreenshot.png -------------------------------------------------------------------------------- /examples/instareact-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactVT/react-vt/HEAD/examples/instareact-app/public/favicon.ico -------------------------------------------------------------------------------- /examples/instareact-app/public/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactVT/react-vt/HEAD/examples/instareact-app/public/profile.jpg -------------------------------------------------------------------------------- /examples/instareact-app/public/instalogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactVT/react-vt/HEAD/examples/instareact-app/public/instalogo.png -------------------------------------------------------------------------------- /examples/tictactoe/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | $lightOrange: #FFCA08; 2 | $darkOrange: #F57921; 3 | $lightBlue: #ACCEEC; 4 | $darkBlue: #398BD4; 5 | -------------------------------------------------------------------------------- /examples/tictactoe/src/components/newWindow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Toe 6 | 7 | 8 | 9 | something 10 | 11 | -------------------------------------------------------------------------------- /examples/tictactoe/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tic Tac Toe 5 | 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/tictactoe/README.md: -------------------------------------------------------------------------------- 1 | # Tic Tac Toe example 2 | 3 | 1. run 'npm install' in tictactoe directory 4 | 2. run 'npm run build' to run webpack 5 | 3. run 'npm start' to run the server 6 | 4. Go to http://localhost:3000 to view the tictactoe app 7 | -------------------------------------------------------------------------------- /examples/tictactoe/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './components/App'; 4 | import styles from './../scss/application.scss' 5 | 6 | 7 | render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /examples/instareact-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /examples/tictactoe/server/util.js: -------------------------------------------------------------------------------- 1 | var util = {}; 2 | 3 | /** 4 | * Insert score into scores array at correct index and return its index 5 | * 6 | * @param score - Object 7 | * @param highScores - Array 8 | */ 9 | util.insertScore = function(newScore, highScores) { 10 | // TODO: Fix me so that my tests pass 11 | return highScores.push(newScore); 12 | }; 13 | 14 | module.exports = util; 15 | -------------------------------------------------------------------------------- /examples/tictactoe/test/index.js: -------------------------------------------------------------------------------- 1 | // This allows us to compile es2015 and react JSX code on the fly. 2 | // Anything that is required in after this point will be automatically compiled! 3 | require('babel-register')({ 4 | presets: ['es2015', 'react'], 5 | }); 6 | 7 | // require('./js/unit'); 8 | // require('./js/supertest'); 9 | // require('./js/zombie'); 10 | require('./js/enzymeTest'); 11 | -------------------------------------------------------------------------------- /examples/tictactoe/test.js: -------------------------------------------------------------------------------- 1 | const phantom = require('phantomjs'); 2 | phantom.casperPath = '/usr/local/bin/casperjs'; 3 | // phantom.injectJs('/usr/local/bin/casperjs/bin/bootstrap.js'); 4 | const casper = require('casperjs').create(); 5 | // phantomjs.exec('zombie.js'); 6 | casper.start('http://google.com'); 7 | casper.then(()=> this.echo('First page ' + this.getTitle())); 8 | 9 | 10 | 11 | console.log('Hello, world!'); 12 | phantom.exit(); -------------------------------------------------------------------------------- /examples/instareact-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.5.4", 7 | "react-dom": "^15.5.4" 8 | }, 9 | "devDependencies": { 10 | "react-scripts": "0.9.5" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } -------------------------------------------------------------------------------- /src/nodeStore.js: -------------------------------------------------------------------------------- 1 | // This stores props and state for each node. Uses the address of the node as the key. 2 | 3 | // Planning to add other storage objects here and have them all be keys on the export. 4 | let storage = { 5 | address: {}, 6 | id: {}, 7 | class: {}, 8 | node: {}, 9 | tag: {} 10 | }; 11 | 12 | const empty = function() { 13 | storage = { 14 | address: {}, 15 | id: {}, 16 | class: {}, 17 | node: {}, 18 | tag: {} 19 | } 20 | }; 21 | 22 | module.exports = { 23 | storage, 24 | empty 25 | }; -------------------------------------------------------------------------------- /examples/tictactoe/src/components/Square.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | const Square = (props) => { 4 | const { handleClick, letter, row, square } = props; 5 | 6 | return ( 7 |
{handleClick(row, square)}}>{letter}
8 | ); 9 | }; 10 | 11 | Square.propTypes = { 12 | handleClick: PropTypes.func.isRequired, 13 | letter: PropTypes.string, 14 | row: PropTypes.number.isRequired, 15 | square: PropTypes.number.isRequired 16 | }; 17 | 18 | export default Square; 19 | -------------------------------------------------------------------------------- /examples/tictactoe/test/js/enzymeTest.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | import React from 'react'; 3 | import { mount } from 'enzyme'; 4 | import 'jsdom-global/register'; 5 | import App from '../../src/components/App'; 6 | 7 | describe('React VT Tests', () => { 8 | let wrapper; 9 | beforeEach(() => { 10 | wrapper = mount(); 11 | }); 12 | 13 | it('should equal X', () => { 14 | wrapper.find('#board').childAt(0).childAt(0).simulate('click'); 15 | expect(wrapper.find('Square').at(0).props().letter).to.equal('X'); 16 | }); 17 | 18 | }); -------------------------------------------------------------------------------- /examples/tictactoe/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | path: path.join(__dirname, '/client'), 7 | filename: 'bundle.js' 8 | }, 9 | module: { 10 | loaders: [ 11 | { 12 | test: /jsx?/, 13 | exclude: /node_modules/, 14 | loader: 'babel-loader', 15 | query: { 16 | presets: ['es2015', 'react'] 17 | } 18 | }, 19 | { 20 | test: /scss$/, 21 | exclude: /node_modules/, 22 | loaders: ['style', 'css', 'sass'] 23 | } 24 | ] 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /examples/tictactoe/src/components/Row.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import Square from './Square'; 3 | 4 | const Row = (props) => { 5 | const { letters, handleClick, row } = props; 6 | const squareElements = letters.map((letter, i) => ( 7 | 8 | )); 9 | return ( 10 |
11 | {squareElements} 12 |
13 | ); 14 | }; 15 | 16 | Row.propTypes = { 17 | letters: PropTypes.arrayOf(PropTypes.string).isRequired, 18 | handleClick: PropTypes.func.isRequired, 19 | row: PropTypes.number.isRequired, 20 | }; 21 | 22 | export default Row; 23 | -------------------------------------------------------------------------------- /examples/tictactoe/server/db/games.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "winner": "X", 4 | "id": 0, 5 | "createdAt": "2017-07-11T21:45:31.240Z" 6 | }, 7 | { 8 | "winner": "O", 9 | "id": 1, 10 | "createdAt": "2017-07-11T21:45:31.241Z" 11 | }, 12 | { 13 | "winner": "O", 14 | "id": 2, 15 | "createdAt": "2017-07-11T21:45:31.241Z" 16 | }, 17 | { 18 | "winner": "X", 19 | "id": 3, 20 | "createdAt": "2017-07-11T21:45:31.259Z" 21 | }, 22 | { 23 | "winner": "O", 24 | "id": 4, 25 | "createdAt": "2017-07-11T21:45:31.263Z" 26 | }, 27 | { 28 | "winner": "X", 29 | "id": 5, 30 | "createdAt": "2017-07-11T21:45:31.276Z" 31 | } 32 | ] -------------------------------------------------------------------------------- /examples/tictactoe/scss/_game.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: $lightBlue; 3 | } 4 | 5 | #board { 6 | width: 390px; 7 | height: 390px; 8 | // border: 5px solid $lightOrange; 9 | // background-color: $darkBlue; 10 | margin: 20px auto; 11 | padding: 0; 12 | } 13 | 14 | .square { 15 | box-sizing: border-box; 16 | float: left; 17 | // display: inline-block; 18 | font-size: 0; 19 | line-height: 0; 20 | width: 120px; 21 | height: 120px; 22 | border-radius: 4px; 23 | background-color: white; 24 | border: 1px solid gainsboro; 25 | margin: 5px; 26 | font-size: 2em; 27 | font-weight: 550; 28 | line-height: 100px; 29 | text-align: center; 30 | cursor: pointer; 31 | 32 | &:hover { 33 | background-color: darken(white, 15); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /examples/tictactoe/server/db/games.dev.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "winner": "X", 4 | "id": 0, 5 | "createdAt": "2017-06-30T18:19:38.581Z" 6 | }, 7 | { 8 | "winner": "X", 9 | "id": 1, 10 | "createdAt": "2017-06-30T18:19:38.634Z" 11 | }, 12 | { 13 | "winner": "O", 14 | "id": 2, 15 | "createdAt": "2017-06-30T18:20:06.597Z" 16 | }, 17 | { 18 | "winner": "O", 19 | "id": 3, 20 | "createdAt": "2017-06-30T18:20:06.719Z" 21 | }, 22 | { 23 | "winner": "O", 24 | "id": 4, 25 | "createdAt": "2017-06-30T18:20:07.773Z" 26 | }, 27 | { 28 | "winner": "O", 29 | "id": 5, 30 | "createdAt": "2017-06-30T18:20:37.108Z" 31 | }, 32 | { 33 | "winner": "O", 34 | "id": 6, 35 | "createdAt": "2017-07-26T03:20:33.932Z" 36 | } 37 | ] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-vt", 3 | "version": "0.1.2", 4 | "description": "Data-driven visual testing library for React developers", 5 | "main": "./src/inject.js", 6 | "scripts": { 7 | "test": "jest -- assert.test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/ReactVT/react-vt.git" 12 | }, 13 | "author": "Ian Ramos, Ian Rustandi, Brian Schmalz", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/ReactVT/react-vt/issues" 17 | }, 18 | "homepage": "https://github.com/ReactVT/react-vt#readme", 19 | "dependencies": { 20 | "sinon": "^2.4.1" 21 | }, 22 | "devDependencies": { 23 | "babel-core": "^6.10.4", 24 | "babel-loader": "^6.2.4", 25 | "babel-preset-es2015": "^6.9.0", 26 | "babel-preset-react": "^6.11.0", 27 | "babel-register": "^6.7.2", 28 | "jest": "^20.0.4", 29 | "react": "^15.0.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/tictactoe/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyparser = require('body-parser'); 3 | const db = require('./server/db/games'); 4 | 5 | const app = express(); 6 | app.set('view engine', 'ejs'); 7 | app.set('views', './views'); 8 | 9 | app.use(express.static(`${__dirname}/client`)); 10 | 11 | app.use(bodyparser.json()); 12 | 13 | app.get('/', (req, res) => { 14 | res.render('index'); 15 | }); 16 | 17 | app.post('/games', (req, res) => { 18 | //object keys in req body, save as var 19 | let keys = Object.keys(req.body); 20 | // if length != 1 throw error 21 | if (!req.body.winner) res.status(400).json({ error: 'Must send winner.' }); 22 | if (keys.length != 1) res.status(400).json({ error: 'Invalid request' }); 23 | const game = db.create({ 24 | winner: req.body.winner, 25 | }); 26 | res.json(game); 27 | }); 28 | 29 | app.get('/games', (req, res) => { 30 | res.json(db.find()); 31 | }); 32 | 33 | const port = process.env.PORT || 3000; 34 | 35 | app.listen(port); 36 | -------------------------------------------------------------------------------- /examples/tictactoe/src/components/GameList.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | function getInitialState() { 3 | return { 4 | text: 'first button', 5 | }; 6 | } 7 | 8 | class GameList extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = getInitialState(); 12 | } 13 | 14 | componentDidMount() { 15 | // let self = this; 16 | // setTimeout(function() { 17 | // self.setState({ 18 | // text: 'second button'}); 19 | // }, 6000); 20 | } 21 | 22 | render() { 23 | const gameList = this.props.gameList; 24 | const listElements = gameList.map(game => ( 25 |
  • {game.winner} won at {game.createdAt}
  • 26 | )); 27 | return ( 28 |
    29 | 30 |

    Previous matches

    31 |
      32 | {listElements} 33 |
    34 |
    35 | ); 36 | } 37 | } 38 | 39 | GameList.propTypes = { 40 | gameList: PropTypes.array.isRequired, 41 | }; 42 | 43 | export default GameList; 44 | -------------------------------------------------------------------------------- /LICENSE.MD: -------------------------------------------------------------------------------- 1 | #MIT License 2 | 3 | ###Copyright (c) 2017 Ian Ramos, Ian Rustandi, Brian Schmalz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/tictactoe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tictactoe", 3 | "version": "1.0.0", 4 | "description": "Tic Tac Toe", 5 | "scripts": { 6 | "start": "node index.js", 7 | "build": "./node_modules/.bin/webpack -w", 8 | "test": "export NODE_ENV=test && mocha --timeout 8000 test/" 9 | }, 10 | "repository": { 11 | "type": "git" 12 | }, 13 | "author": "David Petri (davidpetri.com)", 14 | "license": "ISC", 15 | "dependencies": { 16 | "body-parser": "^1.15.0", 17 | "casperjs": "^1.1.4", 18 | "cross-env": "^1.0.7", 19 | "ejs": "^2.4.1", 20 | "express": "^4.13.4", 21 | "isomorphic-fetch": "^2.2.1", 22 | "phantomjs-prebuilt": "^2.1.14", 23 | "react": "^15.0.1", 24 | "react-dom": "^15.0.1", 25 | "sinon": "^2.3.6", 26 | "webpack": "^1.13.1" 27 | }, 28 | "devDependencies": { 29 | "babel-core": "^6.10.4", 30 | "babel-loader": "^6.2.4", 31 | "babel-preset-es2015": "^6.9.0", 32 | "babel-preset-react": "^6.11.0", 33 | "babel-register": "^6.7.2", 34 | "chai": "^3.5.0", 35 | "concurrently": "^2.0.0", 36 | "css-loader": "^0.23.1", 37 | "enzyme": "^2.4.1", 38 | "eslint": "^2.7.0", 39 | "eslint-config-airbnb": "^7.0.0", 40 | "eslint-plugin-jsx-a11y": "^0.6.2", 41 | "eslint-plugin-react": "^4.3.0", 42 | "expect": "^1.16.0", 43 | "jsdom": "11.0.0", 44 | "jsdom-global": "3.0.2", 45 | "mocha": "^2.4.5", 46 | "node-sass": "^3.8.0", 47 | "react-addons-test-utils": "^15.0.1", 48 | "sass-loader": "^3.2.2", 49 | "style-loader": "^0.13.1", 50 | "supertest": "^1.2.0", 51 | "zombie": "^4.2.1" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/tictactoe/server/db/games.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fs = require('fs'); 3 | let gamesList; 4 | let writeLocation; 5 | 6 | if (process.env.NODE_ENV === 'test') { 7 | writeLocation = `${__dirname}/games.test.json`; 8 | gamesList = require(writeLocation); 9 | } else { 10 | writeLocation = `${__dirname}/games.dev.json`; 11 | gamesList = require(writeLocation); 12 | } 13 | 14 | const db = {}; 15 | 16 | /** 17 | * #create - Adds a unique ID and a createdAt date in the form of an ISO string to 18 | * the game, and then appends it to the array of games in the games.env.json file. 19 | * 20 | * @param {Object} game - The new game to be added to the DB 21 | * @return {Object} the game that was created 22 | */ 23 | db.create = game => { 24 | // check game winner field 25 | // if it is X or O, create new game 26 | // otherwise throw error 27 | if (game.winner !== 'X' && game.winner !== 'O') throw new Error('No winner in game'); 28 | const newGame = Object.assign(game, { 29 | id: gamesList.length, 30 | createdAt: new Date().toISOString(), 31 | }); 32 | gamesList.push(newGame); 33 | fs.writeFileSync(writeLocation, JSON.stringify(gamesList, null, 2)); 34 | return gamesList.slice(-1)[0]; 35 | }; 36 | 37 | /** 38 | * #find - Returns the entire list of games from the appropriate games.env.json file. 39 | * 40 | * @return {Array} the list of games 41 | */ 42 | db.find = () => gamesList; 43 | 44 | /** 45 | * #drop - Deletes everything from the appropriate games.env.json file and writes 46 | * an empty array in its place. 47 | * 48 | * @return {boolean} whether or not the drop succeeded 49 | */ 50 | db.drop = () => { 51 | gamesList = []; 52 | fs.writeFileSync(writeLocation, JSON.stringify(gamesList, null, 2)); 53 | return true; 54 | }; 55 | 56 | db.reset = () => { 57 | gamesList = JSON.parse(fs.readFileSync(writeLocation)); 58 | }; 59 | 60 | module.exports = db; 61 | -------------------------------------------------------------------------------- /examples/instareact-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 17 | React App 18 | 19 | 20 | 21 | 22 | 34 |
    35 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/inject.js: -------------------------------------------------------------------------------- 1 | const domParse = require('./dom-parse.js'); 2 | const nodeStore = require('./nodeStore.js'); 3 | 4 | const assert = require('./assert.js'); 5 | let topNode; 6 | let firstPass = true; 7 | 8 | // importing React from example app 9 | function injector(React, parentNode) { 10 | topNode = parentNode; 11 | startTraverse(parentNode); 12 | const func = React.Component.prototype.setState; 13 | React.Component.prototype.setState = function(...args) { 14 | // set timeout to delay traverse so that it is appended to original setState 15 | startTraverse(this); 16 | return func.apply(this, args); 17 | } 18 | // listens for messages from backgroundjs -> content script -> webpage 19 | window.addEventListener('message', function(event) { 20 | // only accept messges to self 21 | if (event.source != window) return; 22 | // filter out other messages floating around in existing context 23 | 24 | if (event.data.type === 'assertion') { 25 | if (event.data.flag === 'onload') { 26 | event.data.message.forEach(item => { 27 | assert.addAssert(item); 28 | }); 29 | startTraverse(parentNode); 30 | } else if (event.data.flag === 'delete') { 31 | assert.deleteBlock(event.data.message); 32 | } else { 33 | assert.addAssert(event.data.message); 34 | } 35 | } 36 | }, false); 37 | } 38 | 39 | function startTraverse(self, reactDom) { 40 | const nodePackage = {}; 41 | setTimeout(()=> { 42 | let travPromise = throttle(domParse.parser, 25); 43 | travPromise.then((result) => { 44 | // Conditional to display feedback for react router incompatibility 45 | if (result === 'react-router') { 46 | window.postMessage({ type: 'virtualdom', data: 'react-router' }, "*"); 47 | } else { 48 | nodePackage.virtualDom = result; 49 | nodePackage.nodeStore = nodeStore.storage; 50 | let title = document.title; 51 | // specify message type to target specific message 52 | window.postMessage({ type: 'virtualdom', data: nodePackage, topNode: topNode.constructor.name, title: title, first: firstPass}, "*"); 53 | firstPass = false; 54 | } 55 | }); 56 | }, 0); 57 | } 58 | 59 | function throttle(func, wait) { 60 | let waiting = false; 61 | return new Promise((resolve, reject) => { 62 | if (waiting) reject(); 63 | waiting = true; 64 | setTimeout(() => { 65 | waiting = false; 66 | let result = func(topNode); 67 | resolve(result); 68 | }, wait); 69 | return func(topNode); 70 | }); 71 | } 72 | 73 | module.exports = injector; 74 | -------------------------------------------------------------------------------- /examples/instareact-app/public/feed.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica; 3 | } 4 | 5 | #header { 6 | text-align: center; 7 | height: 50px; 8 | vertical-align: middle; 9 | display: flex; 10 | } 11 | 12 | #leftHeader { 13 | float: left; 14 | list-style: none; 15 | } 16 | 17 | #rightBorder { 18 | border-right: 1px solid #000000; 19 | } 20 | 21 | #leftHeader li { 22 | display: inline; 23 | padding: 0px; 24 | padding-bottom: 10px; 25 | padding-right: 20px; 26 | vertical-align: middle; 27 | margin: auto; 28 | } 29 | 30 | #leftHeader i { 31 | vertical-align: middle; 32 | } 33 | 34 | #leftHeader img { 35 | width: 120px; 36 | vertical-align: middle; 37 | } 38 | 39 | #leftHeader { 40 | margin-left: 100px; 41 | padding-bottom: 20px; 42 | height: 100%; 43 | } 44 | 45 | #rightHeader { 46 | float: right; 47 | list-style: none; 48 | margin-right: 200px; 49 | color: rgb(100, 100, 100); 50 | } 51 | 52 | #rightHeader li { 53 | display: inline; 54 | padding: 10px; 55 | } 56 | 57 | #search { 58 | margin: auto; 59 | margin-top: 20px; 60 | text-align: center; 61 | vertical-align: middle; 62 | font-size: 14px; 63 | width: 200px; 64 | padding: 5px 10px; 65 | } 66 | 67 | #search:focus { 68 | text-align: left; 69 | } 70 | 71 | .post { 72 | width: 600px; 73 | border: 1px solid rgb(230, 230, 230); 74 | margin: 50px auto; 75 | border-radius: 3px; 76 | 77 | } 78 | 79 | .postHeader { 80 | height: 70px; 81 | padding: 15px; 82 | padding-bottom: 0; 83 | } 84 | 85 | #feed { 86 | text-align: center; 87 | width: 100% 88 | margin: auto; 89 | } 90 | 91 | .logoPic { 92 | height: 75%; 93 | display: inline-block; 94 | float: left; 95 | border-radius: 50%; 96 | } 97 | 98 | .postPic { 99 | width: 100%; 100 | } 101 | 102 | .headerText { 103 | display: inline-block; 104 | float: left; 105 | padding-left: 20px; 106 | text-align: left; 107 | } 108 | 109 | .headerText p{ 110 | margin: 5px; 111 | padding: 0px; 112 | font-size: 15px; 113 | } 114 | 115 | .name { 116 | font-weight: 600; 117 | font-size: 16px; 118 | } 119 | 120 | .location { 121 | font-weight: 300; 122 | } 123 | 124 | .date { 125 | display: inline-block; 126 | float: right; 127 | } 128 | 129 | .likes { 130 | text-align: left; 131 | margin-left: 20px; 132 | font-weight: 700; 133 | } 134 | 135 | .comments { 136 | text-align: left; 137 | margin-left: 20px; 138 | } 139 | 140 | .comment { 141 | margin: 10px 0px; 142 | } 143 | .userLink { 144 | font-weight: 700; 145 | } 146 | 147 | .social { 148 | text-align: left; 149 | border-top: 1px solid rgb(230, 230, 230); 150 | margin: 20px; 151 | padding-top: 20px; 152 | margin-bottom: 15px; 153 | 154 | } 155 | 156 | .addComment { 157 | font-size: 16px; 158 | padding: 5px; 159 | width: 500px; 160 | vertical-align: middle; 161 | margin-top: -15px; 162 | margin-left: 10px; 163 | border: none; 164 | } 165 | 166 | .addComment:focus { 167 | outline: none; 168 | } 169 | 170 | .liked { 171 | color: #ED4956; 172 | } -------------------------------------------------------------------------------- /examples/tictactoe/test/js/enzyme.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect'); 2 | const React = require('react'); 3 | import 'jsdom-global/register'; 4 | import App from '../../src/components/App'; 5 | import Square from '../../src/components/Square'; 6 | import Row from '../../src/components/Row'; 7 | import GameList from '../../src/components/GameList'; 8 | import { shallow } from 'enzyme'; 9 | import { mount } from 'enzyme'; 10 | import sinon from 'sinon'; 11 | 12 | // Enzyme is a wrapper around React test utilities which makes it easier to 13 | // shallow render and traverse the shallow rendered tree. 14 | 15 | describe('React unit tests', () => { 16 | describe('', () => { 17 | let wrapper; 18 | let spy 19 | 20 | before(() => { 21 | spy = sinon.spy(); 22 | wrapper = shallow(); 23 | }); 24 | 25 | it('Renders a
    with class "square"', () => { 26 | expect(wrapper.text()).toEqual('X'); 27 | expect(wrapper.type()).toEqual('div'); 28 | expect(wrapper.props().className).toEqual('square'); 29 | }); 30 | 31 | it('Clicking on the square passes row and square props to handleClick', () => { 32 | //shallow-render parent 33 | wrapper.simulate('click'); 34 | expect(spy.calledOnce).toBe(true); 35 | expect(spy.args[0]).toEqual([0, 1]); 36 | }); 37 | }); 38 | 39 | describe('', () => { 40 | let wrapper; 41 | // TODO: Write a test to make sure a Row renders 3 Squares 42 | it('Renders 3 squares', () => { 43 | wrapper = shallow( {} }/>); 44 | expect(wrapper.children().length).toEqual(3); 45 | expect(wrapper.childAt(1).name()).toBe('Square'); 46 | }); 47 | }); 48 | 49 | describe('GameList', () => { 50 | // TODO: Write a test to make sure a GameList renders a
      with an
    • 51 | // for every item in its gameList array prop 52 | let wrapper; 53 | it('Renders a ul with an li', () => { 54 | wrapper = shallow(); 86 | expect(wrapper.find('ul').length).toEqual(1); 87 | expect(wrapper.find('li').length).toEqual(6); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /examples/instareact-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica; 3 | } 4 | 5 | #header { 6 | text-align: center; 7 | height: 50px; 8 | vertical-align: middle; 9 | display: flex; 10 | } 11 | 12 | #leftHeader { 13 | float: left; 14 | list-style: none; 15 | } 16 | 17 | #rightBorder { 18 | border-right: 1px solid #000000; 19 | } 20 | 21 | #leftHeader li { 22 | display: inline; 23 | padding: 0px; 24 | padding-bottom: 10px; 25 | padding-right: 20px; 26 | vertical-align: middle; 27 | margin: auto; 28 | } 29 | 30 | #leftHeader i { 31 | vertical-align: middle; 32 | } 33 | 34 | #leftHeader img { 35 | width: 120px; 36 | vertical-align: middle; 37 | } 38 | 39 | #leftHeader { 40 | margin-left: 100px; 41 | padding-bottom: 20px; 42 | height: 100%; 43 | } 44 | 45 | #rightHeader { 46 | float: right; 47 | list-style: none; 48 | margin-right: 200px; 49 | color: rgb(100, 100, 100); 50 | } 51 | 52 | #rightHeader li { 53 | display: inline; 54 | padding: 10px; 55 | } 56 | 57 | #search { 58 | margin: auto; 59 | margin-top: 20px; 60 | text-align: center; 61 | vertical-align: middle; 62 | font-size: 14px; 63 | width: 200px; 64 | padding: 5px 10px; 65 | } 66 | 67 | #search:focus { 68 | text-align: left; 69 | } 70 | 71 | .post { 72 | width: 600px; 73 | border: 1px solid rgb(230, 230, 230); 74 | margin: 50px auto; 75 | border-radius: 3px; 76 | 77 | } 78 | 79 | .postHeader { 80 | height: 70px; 81 | padding: 15px; 82 | padding-bottom: 0; 83 | } 84 | 85 | #feed { 86 | text-align: center; 87 | width: 100%; 88 | margin: auto; 89 | } 90 | 91 | .logoPic { 92 | height: 75%; 93 | display: inline-block; 94 | float: left; 95 | border-radius: 50%; 96 | } 97 | 98 | .postPic { 99 | width: 100%; 100 | } 101 | 102 | .headerText { 103 | display: inline-block; 104 | float: left; 105 | padding-left: 20px; 106 | text-align: left; 107 | } 108 | 109 | .headerText p{ 110 | margin: 5px; 111 | padding: 0px; 112 | font-size: 15px; 113 | } 114 | 115 | .name { 116 | font-weight: 600; 117 | font-size: 16px; 118 | } 119 | 120 | .location { 121 | font-weight: 300; 122 | } 123 | 124 | .date { 125 | display: inline-block; 126 | float: right; 127 | } 128 | 129 | .likes { 130 | text-align: left; 131 | margin-left: 20px; 132 | font-weight: 700; 133 | } 134 | 135 | .comments { 136 | text-align: left; 137 | margin-left: 20px; 138 | } 139 | 140 | .comment { 141 | margin: 10px 0; 142 | text-align: left; 143 | } 144 | .userLink { 145 | font-weight: 700; 146 | margin-right: 5px; 147 | } 148 | 149 | .social { 150 | text-align: left; 151 | border-top: 1px solid rgb(230, 230, 230); 152 | margin: 20px; 153 | padding-top: 20px; 154 | margin-bottom: 15px; 155 | 156 | } 157 | 158 | .addComment { 159 | font-size: 16px; 160 | padding: 5px; 161 | width: 500px; 162 | vertical-align: middle; 163 | margin-top: -15px; 164 | margin-left: 10px; 165 | border: none; 166 | } 167 | 168 | .addComment:focus { 169 | outline: none; 170 | } 171 | 172 | .liked { 173 | color: #ED4956; 174 | } -------------------------------------------------------------------------------- /examples/tictactoe/src/components/App.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | import React, { Component } from 'react'; 3 | import Row from './Row'; 4 | import GameList from './GameList'; 5 | import injector from './../../../../src/inject.js'; 6 | 7 | let gameStore = []; 8 | 9 | function getInitialState() { 10 | return { 11 | rows: [ 12 | ['', '', ''], 13 | ['', '', ''], 14 | ['', '', ''], 15 | ], 16 | turn: 'X', 17 | winner: undefined, 18 | gameList: gameStore, 19 | }; 20 | } 21 | 22 | function fetchGames() { 23 | return fetch('/games') 24 | .then(response => response.json()); 25 | } 26 | 27 | function checkWin(rows) { 28 | const combos = [ 29 | [0, 1, 2], 30 | [3, 4, 5], 31 | [6, 7, 8], 32 | [0, 4, 8], 33 | [2, 4, 6], 34 | [0, 3, 6], 35 | [1, 4, 7], 36 | [2, 5, 8], 37 | ]; 38 | 39 | const flattened = rows.reduce((acc, row) => acc.concat(row), []); 40 | 41 | return combos.find(combo => ( 42 | flattened[combo[0]] !== '' && 43 | flattened[combo[0]] === flattened[combo[1]] && 44 | flattened[combo[1]] === flattened[combo[2]] 45 | )); 46 | } 47 | 48 | class App extends Component { 49 | constructor(props) { 50 | super(props); 51 | this.handleClick = this.handleClick.bind(this); 52 | this.submitGame = this.submitGame.bind(this); 53 | this.state = getInitialState(); 54 | } 55 | 56 | componentDidMount() { 57 | // let self = this; 58 | // setTimeout(function() { 59 | // self.setState({ 60 | // rows: [['', '', ''], 61 | // ['', '', ''], 62 | // ['', '', ''], 63 | // ['', '', '']] 64 | // }); 65 | // }, 6000); 66 | 67 | fetchGames() 68 | .then(gameList => { 69 | gameStore = gameList; 70 | this.setState(Object.assign( 71 | this.state, 72 | { gameList: gameStore } 73 | )); 74 | }); 75 | } 76 | 77 | componentDidUpdate() { 78 | console.log('did update'); 79 | } 80 | 81 | componentWillMount() { 82 | injector(React, this); 83 | } 84 | 85 | handleClick(row, square) { 86 | let { turn, winner } = this.state; 87 | const { rows } = this.state; 88 | const squareInQuestion = rows[row][square]; 89 | 90 | if (this.state.winner) return; 91 | if (squareInQuestion) return; 92 | 93 | rows[row][square] = turn; 94 | turn = turn === 'X' ? 'O' : 'X'; 95 | winner = checkWin(rows); 96 | 97 | this.setState({ 98 | rows, 99 | turn, 100 | winner, 101 | }); 102 | } 103 | 104 | submitGame(winner) { 105 | fetch('/games', { 106 | method: 'POST', 107 | headers: { 108 | Accept: 'application/json', 109 | 'Content-Type': 'application/json', 110 | }, 111 | body: JSON.stringify({ winner }), 112 | }).then(response => { 113 | if (response.status >= 200 && response.status < 300) { 114 | return response.json(); 115 | } 116 | return Promise.reject(response.statusText); 117 | }).then((json) => { 118 | gameStore = gameStore.concat([json]); 119 | this.setState(Object.assign( 120 | this.state, 121 | { gameList: gameStore } 122 | )); 123 | }).catch(err => { 124 | console.log('ERROR!', err); 125 | }); 126 | } 127 | 128 | render() { 129 | const { rows, turn, winner, gameList } = this.state; 130 | const handleClick = this.handleClick; 131 | const submitGame = this.submitGame; 132 | 133 | const rowElements = rows.map((letters, i) => ( 134 | 135 | )); 136 | 137 | let infoDiv; 138 | if (winner) { 139 | let winTurn = turn === 'X' ? 'O' : 'X'; 140 | infoDiv = ( 141 |
      142 |
      Player {winTurn} wins with squares {winner.join(', ')}!
      143 | 144 |
      145 | ); 146 | } else { 147 | infoDiv =
      Turn: {turn}
      ; 148 | } 149 | // TODO: the #clear button doesn't work yet. 150 | return ( 151 |
      152 | {infoDiv} 153 |
      154 | {rowElements} 155 |
      156 | 157 | 158 | 159 |
      160 | ); 161 | } 162 | } 163 | 164 | export default App; 165 | -------------------------------------------------------------------------------- /examples/tictactoe/test/js/supertest.js: -------------------------------------------------------------------------------- 1 | const request = require('supertest'); 2 | // Start server 3 | const app = require('../../'); 4 | const fs = require('fs'); 5 | const expect = require('expect'); 6 | const PORT = process.env.PORT || 3000; 7 | const HOST = `http://localhost:${PORT}`; 8 | const db = require('../../server/db/games.js'); 9 | /** 10 | * include an assertion library here so that you can make assertions other than those 11 | * provided by supertest. Choose whichever assertion library suits you. 12 | */ 13 | // const expect = require('expect'); 14 | // const expect = require('chai').expect; 15 | // const assert = require('chai').assert; 16 | 17 | describe('Route integration', () => { 18 | describe('/', () => { 19 | describe('GET', () => { 20 | it('responds with 200 status and text/html content type', done => { 21 | request(HOST) 22 | .get('/') 23 | .expect('Content-Type', /text\/html/) 24 | .expect(200, done); 25 | }); 26 | }); 27 | }); 28 | 29 | describe('/games', () => { 30 | describe('GET', () => { 31 | it('response with 200 status and application/json content type', done => { 32 | // request '/games' 33 | // expect status 200 34 | // expect content to equal app json 35 | request (HOST) 36 | .get('/games') 37 | .expect('Content-Type', /application\/json/) 38 | .expect(200, done); 39 | }); 40 | 41 | it('games from appropriate json file in server/db/ are in body of response', done => { 42 | // You'll need to inspect the body of the response and ensure it contains the games list. 43 | // Might need to read the games json file in first to make sure the games in the response 44 | // match the games in the database. 45 | 46 | // Read gamelist from json 47 | // do a get request to games 48 | // Compare body of response to the our json 49 | db.create({winner: 'X'}); 50 | db.create({winner: 'O'}); 51 | db.create({winner: 'O'}); 52 | const gamesList = db.find().slice(); 53 | request (HOST) 54 | .get('/games') 55 | .then(response => { 56 | expect(response.body).toEqual(gamesList); 57 | done(); 58 | }); 59 | }); 60 | }); 61 | 62 | describe('POST', () => { 63 | it('responds to valid request with 200 status and application/json content type', done => { 64 | request (HOST) 65 | .post('/games') 66 | .send({ winner: 'X' }) 67 | .expect('Content-Type', /application\/json/) 68 | .expect(200, done); 69 | }); 70 | 71 | it('responds to a valid request with the item that was created in the DB', done => { 72 | // Hint: inspect the response body and make sure it contains the winner, createdAt, and 73 | // id fields. 74 | 75 | // post req 76 | request(HOST) 77 | .post('/games') 78 | .send({ winner: 'O' }) 79 | .then( response => { 80 | // check body of response - verify that winner, created_at , and id fields are the same 81 | expect(response.body.winner).toEqual('O'); 82 | expect(response.body.id).toEqual(4); 83 | expect(response.body.createdAt).toExist(); 84 | done(); 85 | }); 86 | }); 87 | 88 | it('responds to invalid request with 400 status and error message in body', done => { 89 | // This feature does not exist yet. Follow test-driven-development here! See description 90 | // in readme. 91 | // Hint: An invalid request is a POST request in which the POST body does not contain 92 | // a JSON object with a "winner" key, or if the body contains fields other than "winner" 93 | 94 | // 3 requests 95 | // 1. no winner field/empty req 96 | request(HOST) 97 | .post('/games') 98 | .send({}) 99 | .then( res => { 100 | expect(res.status).toEqual(400); 101 | expect(res.body.error).toEqual('Must send winner.'); 102 | // 2. with a winner field and extra key 103 | request(HOST) 104 | .post('/games') 105 | .send({ winner: 'X', extraKey: 'key' }) 106 | .then( res => { 107 | console.log('status for winner and extra key ', res.status) 108 | expect(res.status).toEqual(400); 109 | expect(res.body.error).toEqual('Invalid request'); 110 | // 3. with one field that is not winner 111 | request(HOST) 112 | .post('/games') 113 | .send({ notWinner: 'wiener' }) 114 | .then( res => { 115 | expect(res.status).toEqual(400); 116 | expect(res.body.error).toEqual('Must send winner.'); 117 | done(); 118 | }); 119 | }); 120 | }); 121 | }); 122 | }); 123 | }); 124 | }); 125 | -------------------------------------------------------------------------------- /examples/tictactoe/test/js/zombie.js: -------------------------------------------------------------------------------- 1 | const Browser = require('zombie'); 2 | const expect = require('expect'); 3 | const db = require('../../server/db/games.js'); 4 | // const phantom = require('phantomjs-prebuilt'); 5 | // phantom.casperPath = '/usr/local/bin/casperjs'; 6 | // phantom.injectJs('/usr/local/bin/casperjs/bin/bootstrap.js'); 7 | // const casper = require('casper').create(); 8 | // // phantomjs.exec('zombie.js'); 9 | // casper.start('http://google.com'); 10 | // casper.then(()=> this.echo('First page ' + this.getTitle())); 11 | 12 | // Include one of the following assertion libraries if you need to make assertions 13 | // that Zombie does not provide out of the box. 14 | // const expect = require('expect'); 15 | // const expect = require('chai').expect; 16 | // const assert = require('chai').assert; 17 | 18 | const PORT = process.env.PORT || 3000; 19 | 20 | // Start the server 21 | require('../../'); 22 | 23 | // Regex which matches strings that are a single capital letter A-Z or Qu 24 | describe('Front-end Integration/Features', () => { 25 | const browser = new Browser(); 26 | browser.silent = true; 27 | 28 | before(done => { 29 | browser.visit(`http://localhost:${PORT}/`, done); 30 | }); 31 | 32 | describe('Initial display', () => { 33 | it('loads successfully', () => { 34 | browser.assert.success(); 35 | }); 36 | 37 | it('displays a board', () => { 38 | browser.assert.element('#board'); 39 | }); 40 | 41 | // TODO: Finish tests 42 | 43 | it('displays 3 rows', () => { 44 | // browser assert elements - rows - 3 45 | browser.assert.elements('.row', 3); 46 | }); 47 | 48 | it('displays 9 squares', () => { 49 | browser.assert.elements('.square', 9); 50 | }); 51 | }); 52 | 53 | describe('Game logic', () => { 54 | it('clicking on square places an X in square', () => { 55 | // verify that clicked square is now X 56 | browser.assert.text('.square', ''); 57 | browser.fire('.square', 'click'); 58 | browser.assert.text('.square', 'X'); 59 | }); 60 | 61 | it('if X gets 3 in a row, victory message is displayed', () => { 62 | // target more than one square 63 | let squareArr = browser.querySelectorAll('.square'); 64 | let innerHTML; 65 | let result; 66 | // simulate a click on square 67 | browser.fire(squareArr[0], 'click'); 68 | browser.fire(squareArr[5], 'click'); 69 | browser.fire(squareArr[1], 'click'); 70 | browser.fire(squareArr[6], 'click'); 71 | browser.fire(squareArr[2], 'click'); 72 | innerHTML = browser.querySelector('#root').firstChild.firstChild.firstChild.innerHTML; 73 | result = innerHTML.includes('wins'); 74 | expect(innerHTML).toExist(); 75 | expect(innerHTML.includes('X')).toBe(true); 76 | expect(result).toBe(true); 77 | }); 78 | 79 | it('if O gets 3 in a row, victory message is displayed', () => { 80 | browser.fire('#reset', 'click'); 81 | browser.assert.text('.square', ''); 82 | let squareArr = browser.querySelectorAll('.square'); 83 | let innerHTML; 84 | let result; 85 | // simulate a click on square 86 | browser.fire(squareArr[5], 'click'); 87 | browser.fire(squareArr[0], 'click'); 88 | browser.fire(squareArr[8], 'click'); 89 | browser.fire(squareArr[1], 'click'); 90 | browser.fire(squareArr[7], 'click'); 91 | browser.fire(squareArr[2], 'click'); 92 | innerHTML = browser.querySelector('#root').firstChild.firstChild.firstChild.innerHTML; 93 | result = innerHTML.includes('wins'); 94 | expect(innerHTML).toExist(); 95 | expect(innerHTML.includes('O')).toBe(true); 96 | expect(result).toBe(true); 97 | }); 98 | }); 99 | 100 | describe('Lists games from database', () => { 101 | it('all games from database are listed in #gameList div', () => { 102 | // TODO: You'll need to require in and query the test DB in order to ensure 103 | // that the right items show up. 104 | 105 | // query db for all records 106 | // get LI text from displayed records 107 | // parse LI 108 | // compare two values 109 | let newTab = browser.open('http://localhost:3000'); 110 | // console.log(db.find()); 111 | // save db find in variable 112 | const dbRecords = db.find(); 113 | let record = newTab.querySelectorAll('li'); 114 | // extract created At from db array 115 | dbRecords.forEach((el, i) => { 116 | console.log('headless results ', record[i].innerHTML); 117 | console.log('db results ', el.createdAt); 118 | expect(record[i].innerHTML.includes(el.createdAt)).toBe(true); 119 | }); 120 | // compare to innerHtml aray 121 | // expect for each element in array 122 | 123 | }); 124 | }); 125 | 126 | describe('Buttons', () => { 127 | xit('clicking the #reset button clears the game board and sets turn back to X', () => { 128 | }); 129 | 130 | xit('clicking the #clear button removes all listed games', () => { 131 | // TODO: You'll need to fix the button in src/components/App.js first! 132 | }); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /examples/instareact-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import injector from './../../../src/inject.js'; 5 | 6 | class List extends React.Component { 7 | constructor() { 8 | super(); 9 | this.state = { 10 | posts: [], 11 | }; 12 | this.picSearch('https://codesmith-precourse.firebaseio.com/instagram/-JqL35o8u6t3dTQaFXSV.json'); 13 | } 14 | 15 | componentWillMount() { 16 | injector(React, this); 17 | } 18 | 19 | picSearch(term) { 20 | fetch(term).then((response) => { 21 | response.json().then((data) => { 22 | this.picValidate(data); 23 | }); 24 | }); 25 | } 26 | 27 | addPic(pic) { 28 | const newPosts = this.state.posts.slice(); 29 | if (!newPosts.includes(pic)) { 30 | newPosts.push(pic); 31 | } 32 | this.setState({ posts: newPosts }); 33 | } 34 | 35 | picValidate(data) { 36 | const self = this; 37 | let count = 0; 38 | data.forEach((pic) => { 39 | fetch(pic) 40 | .then((response) => { 41 | if (response.status !== 200) { 42 | // Error text could be added here if desired 43 | return; 44 | } 45 | count++; 46 | if (count < 5) self.addPic(pic); 47 | } 48 | ) 49 | .catch((err) => { 50 | }); 51 | // Error text could be added here if desired 52 | }); 53 | } 54 | 55 | render() { 56 | const posts = this.state.posts.map((url) => { 57 | return ( 58 | 59 | ); 60 | }); 61 | return ( 62 |
      63 | {posts} 64 |
      65 | ); 66 | } 67 | } 68 | 69 | class PostItem extends React.Component { 70 | constructor(props) { 71 | super(props); 72 | this.state = { 73 | url: props.url, 74 | likes: 7, 75 | liked: false 76 | }; 77 | } 78 | 79 | liked() { 80 | if(this.state.liked) { 81 | const likey = this.state.likes - 1; 82 | this.setState({ likes: likey, liked: false}); 83 | return; 84 | } 85 | const likey = this.state.likes + 1; 86 | this.setState({ likes: likey, liked: true}); 87 | } 88 | 89 | render() { 90 | const self = this; 91 | return ( 92 |
      93 |
      94 | huh 95 |
      96 |

      brischmalz

      97 |

      Altadena, CA

      98 |
      99 |

      3d

      100 |
      101 | profile this.liked()}/> 102 | this.liked()}/> 104 |
      105 | ); 106 | } 107 | } 108 | 109 | class Social extends React.Component { 110 | constructor(props) { 111 | super(props); 112 | this.likeClick = props.likeClick; 113 | this.state = { 114 | comments: [{ user: 'username', comment: 'Sup Yo', key: 0 }] 115 | }; 116 | } 117 | 118 | 119 | render() { 120 | const comments = this.state.comments.map(comment => 121 | 122 | ); 123 | 124 | return ( 125 |
      126 |

      {this.props.likes} likes

      127 |
      128 | {comments} 129 |
      130 |
      131 | this.likeClick()} liked={this.props.liked} /> 132 | { 134 | const add = { user: 'bschmalz', comment: input, key: getKey.getKey() }; 135 | const newComments = this.state.comments.slice(); 136 | newComments.push(add); 137 | this.setState({ comments: newComments }); 138 | }} 139 | /> 140 |
      ); 141 | } 142 | } 143 | 144 | const getKey = { 145 | key: 0, 146 | getKey: () => { 147 | getKey.key += 1; 148 | return getKey.key; 149 | } 150 | }; 151 | 152 | class InputComment extends React.Component { 153 | constructor(props) { 154 | super(props); 155 | this.onEnter = props.onEnter; 156 | this.state = { 157 | value: '' 158 | }; 159 | this.handleChange = this.handleChange.bind(this); 160 | } 161 | 162 | handleChange(event) { 163 | if (event.key === 'Enter') { 164 | this.onEnter(this.state.value); 165 | this.setState({ value: '' }); 166 | return; 167 | } 168 | const val = this.state.value + event.key; 169 | this.setState({ value: val }); 170 | } 171 | 172 | render() { 173 | return ( 174 | 181 | ); 182 | } 183 | } 184 | 185 | const Comment = props => 186 |

      {props.user}{props.comment}

      ; 187 | 188 | const LikeButton = (props) => { 189 | const liked = props.liked; 190 | const str = liked ? 'fa fa-heart fa-2x likeButton liked' : 'fa fa-heart-o fa-2x likeButton'; 191 | return ( 192 |