Coder Social home page Coder Social logo

tic-tac-toe's Introduction

Showcase of my development workflow

Since all my relevant development work is focused on private, commercial codebases, I am writing this small tic tac toe game to showcase my skillsets to the world. Hello, world!

This is how I develop an app to make it easy to test.

Status
CI service Build Status
Tests Coverage Status

Live demo

View the live demo on Heroku or the individual UI components of my Storybook on Github Pages.

Demo Animation

Best practices on this codebase

  • Super-high test coverage (see the reports for more details), including unit tests, integration tests and end to end tests.
  • Application of the DRY principle.
  • Usage of a linter and code formatting.
  • Atomic design and Component Driven Development for the UI. Each UI component does only one thing and one thing well and are tested in isolation then later in conjunction and build their documentations as I write the code using Storybook.
  • Test Driven Development for the business logic.
  • Good separation of concerns between the views (React components) and their state management (Redux).
  • Pixel-perfect CSS on the react components (see how I use styled components on Board.jsx and Square.jsx.
  • A Domain Specific Language (DSL), extremely easy to learn, suitable to play the game programmatically and evaluate results (used for testing). Useful to make it easy to write tests (for the QA team, for example) or to run the game programmatically in headless mode (in the real world this could be useful for maintenance tasks, for example). I got this idea from The Pragmatic Programmer book. TODO: Use babel to simplify the DSL even more (a la JSX). Example:
    Scenario('Game over! X wins');
    I.placeMove(TOP_LEFT);
    I.placeMove(BOTTOM_RIGHT);
    I.placeMove(TOP_CENTER);
    I.placeMove(BOTTOM_CENTER);
    I.placeMove(TOP_RIGHT);
    I.expect().gameOver();
    I.expect(X).toBeWinner();

Tech stack

  • Create-react-app, which includes: React, JSX, ES6, Webpack, Babel and other amazing projects.
  • Prettier Code Formatter + ESLint setup with Airbnb's style guide + VSCode integration
  • Jest + Enzyme for tests, including @storybook/addon-storyshots to snapshot test my Storybook and puppeteer for e2e tests.
  • Storybook of my UI components
  • Travis CI to build the production bundles and deploy it to Heroku, run the tests, creating and publishing the test coverage report and the UI documentation as a Storybook on Github Pages.

Instructions

Install and run locally

Install redux-devtools-extension in your browser (optional, use this browser extension during development).

then clone the repo via git:

git clone https://github.com/davps/tic-tac-toe.git

And then install dependencies.

cd tic-tac-toe && npm install

Run these two commands simultaneously in different console tabs.

npm start
npm run storybook

And open http://localhost:3000/ to run the web app, http://localhost:9009/ to open the storybook and Menu > Debug > Start Debugging on VSCode to run the test on each file change. If that didn't work for you, you can try Menu > Debug > Start without debugging or you can use this command on your terminal:

npm test

You can generate a report of the test coverage with this command

npm test -- --coverage

and then open the ./coverage/Icov-report/index.html file to explore the coverage details.

In your local environment, the puppeteer tests will pass only if your server is up and running (you need to do that manually) Why? Because reinitializing the server to run the test is too slow. In the CI server, the test suite will automatically start the server and tear down when it is done.

How to extend the DSL

We will always want to extend the actual syntax of our DSL to cover new cases of the business domain. This section document the steps you can follow to extend the DSL.

I'll explain it with an example: We want to add the new method isAvailable() to use it as

I.expect(TOP_LEFT).isAvailable()

Below are the steps:

1- Add isAvailable to DSL.js, as a descriptor on the createDescriptor method, in this case, as part of the expectation object. Note that we pass the arg value here:

      isAvailable: () => dispatch(expect.isAvailable(arg))

2- Add an expectation creator for isAvailable. It will look like this (from the architectural point of view, this is equivalent to the action creator of redux):

  isAvailable: position => ({
    type: EXPECT_IS_AVAILABLE,
    position
  })

3- Add an expectation type (also, equivalent to action types of Redux):

export const EXPECT_IS_AVAILABLE = 'EXPECT_IS_AVAILABLE';

4- Now we need to modify the App.DSLto****.adaptor.test.jsx files, to implement this new expectation type for each file. The implementation is optional for expectations but mandatory when the new interface is not an expectation. So, for App.DSLtoRedux.adaptor.test.jsx, for example, we add:

case EXPECT_IS_AVAILABLE: {
    expect(store.getState().moves[action.position]).toBe(ACTOR.PENDING);
    break;
}

5- In case you are writing the new tests on a new file, make sure to concat all the tests on the tests array of your adaptor that run implement file. At the time of writing this tutorial, we used these tests on the redux adaptor (retrieved the JSON description of the tests with import:

import testsWithDSL from './App.testsWithDSL';
import testFeaturePlaceMove from './App.test.feature.placeMove';

and then put all the test scenarios on a single array with:

const tests = [].concat(testsWithDSL, testFeaturePlaceMove);

and start running the tests from there

TODO

Setup build stages for my Heroku deployments.

tic-tac-toe's People

Contributors

davps avatar

Stargazers

Chau Giang avatar Philipp Muens avatar  avatar Iván Ariel Cáceres avatar

Watchers

 avatar James Cloos avatar vaibhav avatar

tic-tac-toe's Issues

Improve messages of failed expectations on DSL adaptors

DSL adaptors should reference which CASE fails when an expectation is not meet.

Jasmine did not provide a good way to do this (see link below)
jasmine/jasmine#641 (comment)
but it will worth expending some time to find a solution, basically it should be easy to figure out test failures.

Example:
I've added the 'Expected a winner but did not get one' message. In practice it didn't work because jasmine didn't provide the API for that, but I could figure out how to do something equivalent in another way.

        case EXPECT_A_WINNER: {
          expect(
            game
              .find(WinnerName)
              .at(0)
              .find('.has-winner').length
          ).toBeGreaterThan(0, 'Expected a winner but did not get one');
          break;
        }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.