diff --git a/README.md b/README.md index b915f90..da32a7f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # js-code + Useful code and practice of JavaScript ## TOC -* [Test React components using component tests](./testing-js/react-components/test-app/) +- [Test React components using component tests](./testing-js/react-components/test-app/) +- [Tic tac toe game](./tic-tac-toe/README.md) diff --git a/tic-tac-toe/.env b/tic-tac-toe/.env new file mode 100644 index 0000000..98a4f13 --- /dev/null +++ b/tic-tac-toe/.env @@ -0,0 +1 @@ +CHOKIDAR_USEPOLLING=true \ No newline at end of file diff --git a/tic-tac-toe/.gitignore b/tic-tac-toe/.gitignore new file mode 100644 index 0000000..4d29575 --- /dev/null +++ b/tic-tac-toe/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/tic-tac-toe/README.md b/tic-tac-toe/README.md new file mode 100644 index 0000000..50ad7cf --- /dev/null +++ b/tic-tac-toe/README.md @@ -0,0 +1,13 @@ +# Tic Tac Toe Game + +[Following ReactJs tutorial](https://reactjs.org/tutorial/tutorial.html) + +## Testing + +- visual tests with [wdio](https://webdriver.io/docs/gettingstarted/) + +```js +npm init wdio . +``` + +1. What are all the tests that we need to write to ensure bug free code? diff --git a/tic-tac-toe/package.json b/tic-tac-toe/package.json new file mode 100644 index 0000000..2616c50 --- /dev/null +++ b/tic-tac-toe/package.json @@ -0,0 +1,46 @@ +{ + "name": "tic-tac-toe", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.1", + "@testing-library/react": "^11.2.7", + "@testing-library/user-event": "^12.8.3", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-scripts": "4.0.3", + "web-vitals": "^1.1.2" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject", + "wdio": "wdio run wdio.conf.js" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@wdio/cli": "^7.16.11", + "@wdio/local-runner": "^7.16.11", + "@wdio/mocha-framework": "^7.16.11", + "@wdio/sauce-service": "^7.16.11", + "@wdio/spec-reporter": "^7.16.11" + } +} \ No newline at end of file diff --git a/tic-tac-toe/public/favicon.ico b/tic-tac-toe/public/favicon.ico new file mode 100644 index 0000000..a11777c Binary files /dev/null and b/tic-tac-toe/public/favicon.ico differ diff --git a/tic-tac-toe/public/index.html b/tic-tac-toe/public/index.html new file mode 100644 index 0000000..aa069f2 --- /dev/null +++ b/tic-tac-toe/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/tic-tac-toe/public/logo192.png b/tic-tac-toe/public/logo192.png new file mode 100644 index 0000000..fc44b0a Binary files /dev/null and b/tic-tac-toe/public/logo192.png differ diff --git a/tic-tac-toe/public/logo512.png b/tic-tac-toe/public/logo512.png new file mode 100644 index 0000000..a4e47a6 Binary files /dev/null and b/tic-tac-toe/public/logo512.png differ diff --git a/tic-tac-toe/public/manifest.json b/tic-tac-toe/public/manifest.json new file mode 100644 index 0000000..080d6c7 --- /dev/null +++ b/tic-tac-toe/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/tic-tac-toe/public/robots.txt b/tic-tac-toe/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/tic-tac-toe/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/tic-tac-toe/src/index.css b/tic-tac-toe/src/index.css new file mode 100644 index 0000000..ac7a816 --- /dev/null +++ b/tic-tac-toe/src/index.css @@ -0,0 +1,51 @@ +body { + font: 14px "Century Gothic", Futura, sans-serif; + margin: 20px; +} + +ol, +ul { + padding-left: 30px; +} + +.board-row:after { + clear: both; + content: ""; + display: table; +} + +.status { + margin-bottom: 10px; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.square:focus { + outline: none; +} + +.kbd-navigation .square:focus { + background: #ddd; +} + +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} diff --git a/tic-tac-toe/src/index.js b/tic-tac-toe/src/index.js new file mode 100644 index 0000000..2a8cec2 --- /dev/null +++ b/tic-tac-toe/src/index.js @@ -0,0 +1,134 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import "./index.css"; + +function Square(props) { + //no more render() + return ( + //no more this.props and no more ()=> onClick(), instead onClick + + ); +} + +/** + * the best approach is to store the game’s state in the parent Board + * component instead of in each Square. + * The Board component can tell each Square what to display + * by passing a prop + * To collect data from multiple children, + * or to have two child components communicate with each other, + * you need to declare the shared state in their parent component instead. + * The parent component can pass the state back down to the children + * by using props; + * this keeps the child components in sync with each other + * and with the parent component. + */ +class Board extends React.Component { + constructor(props) { + super(props); + this.state = { + squares: Array(9).fill(null), + xIsNext: true, + }; + } + handleClick(i) { + const squares = this.state.squares.slice(); + if (calculateWinner(squares) || squares[i]) { + return; + } + squares[i] = this.state.xIsNext ? "X" : "O"; + this.setState({ squares: squares, xIsNext: !this.state.xIsNext }); + } + renderSquare(i) { + return ( + // Now we’re passing down two props from Board to Square: + // value and onClick. + // The onClick prop is a function that Square can call when clicked. + this.handleClick(i)} + testid={i} + /> + ); + } + + //The render method returns a description of what you want to see on the screen + render() { + const winner = calculateWinner(this.state.squares); + let status; + if (winner) { + status = "Winner " + winner; + } else { + status = "Next player: " + (this.state.xIsNext ? "X" : "O"); + } + + return ( +
+
{status}
+
+ {this.renderSquare(0)} + {this.renderSquare(1)} + {this.renderSquare(2)} +
+
+ {this.renderSquare(3)} + {this.renderSquare(4)} + {this.renderSquare(5)} +
+
+ {this.renderSquare(6)} + {this.renderSquare(7)} + {this.renderSquare(8)} +
+
+ ); + } +} + +class Game extends React.Component { + render() { + return ( +
+
+ +

Built, tested, and deployed with ❤️ by Nikolay Advolodkin

+ ultimateqa +
+
+
{/* status */}
+
    {/* TODO */}
+
+
+ ); + } +} + +// ======================================== + +ReactDOM.render(, document.getElementById("root")); + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} diff --git a/tic-tac-toe/test/specs/visual.spec.js b/tic-tac-toe/test/specs/visual.spec.js new file mode 100644 index 0000000..87be53a --- /dev/null +++ b/tic-tac-toe/test/specs/visual.spec.js @@ -0,0 +1,17 @@ +describe("Tic tac toe game", () => { + it("should look correct", async () => { + await browser.url(``); + await browser.execute("/*@visual.init*/", "Tic Tac Toe"); + await browser.execute("/*@visual.snapshot*/", "Empty State"); + + await $('[data-testid="0"]').click(); + await $('[data-testid="3"]').click(); + await $('[data-testid="1"]').click(); + await $('[data-testid="4"]').click(); + await $('[data-testid="2"]').click(); + await browser.execute("/*@visual.snapshot*/", "X is Winner"); + + const result = await browser.execute("/*@visual.end*/"); + expect(result.message).toBeNull(); + }); +}); diff --git a/tic-tac-toe/wdio.conf.js b/tic-tac-toe/wdio.conf.js new file mode 100644 index 0000000..97445d3 --- /dev/null +++ b/tic-tac-toe/wdio.conf.js @@ -0,0 +1,74 @@ +const visualOptions = { + apiKey: process.env.SCREENER_API_KEY, + projectName: "tic-tac-toe", + failOnNewStates: false, +}; +const sauceOptions = { + username: process.env.SAUCE_USERNAME, + accesskey: process.env.SAUCE_ACCESS_KEY, +}; +exports.config = { + region: "us", + services: [ + [ + "sauce", + { + sauceConnect: true, + }, + ], + ], + specs: ["./test/specs/**/*.js"], + // Patterns to exclude. + exclude: [ + // 'path/to/excluded/files' + ], + maxInstances: 10, + hostname: "hub.screener.io", + port: 443, + protocol: "https", + path: "/wd/hub", + capabilities: [ + //Desktop A 28%: https://www.w3schools.com/browsers/browsers_display.asp + { + browserName: "chrome", + platformName: "windows 10", + browserVersion: "latest", + "sauce:options": { + ...sauceOptions, + }, + "sauce:visual": { + ...visualOptions, + viewportSize: "1366x768", + }, + }, + { + browserName: "safari", + platformName: "macOS 11.00", + browserVersion: "14", + "sauce:options": { + ...sauceOptions, + }, + "sauce:visual": { + ...visualOptions, + viewportSize: "1366x768", + }, + }, + ], + // + // =================== + // Test Configurations + // =================== + // Level of logging verbosity: trace | debug | info | warn | error | silent + logLevel: "info", + bail: 0, + baseUrl: "http://localhost:3000", + waitforTimeout: 10000, + connectionRetryTimeout: 120000, + connectionRetryCount: 3, + framework: "mocha", + reporters: ["spec"], + mochaOpts: { + ui: "bdd", + timeout: 60000, + }, +};