2.3.2 • Published 2 years ago

betslip v2.3.2

Weekly downloads
-
License
UNLICENSED
Repository
gitlab
Last release
2 years ago

Betslip

The application-toolstack is the base for future web projects. It allows to use newer JavaScript versions (via Babel), provides libraries such as React and Redux, and allows asset bundling using webpack. More features and details about the toolstack can be found below.

Installation

~~No installation needed. Just clone the repository and start working with it.

How to clone:

git clone --recurse-submodules GIT-REPO-URL

We check in the node_modules folder because it allows us to be independent from npm (e.g. in case a npm package gets unpublished).~~

At this stage we are not yet committing the nod_modules folder because it will dramatically increase the size of the repo and it will get worse with each dependency update, so we try to delay this until it will be necessary.

In order to get started, just run npm update.

Note: the installation is tested with node v9.4.0 and npm v6.1.0, or with node 8.5.0 and npm v5.3.0. If something goes wrong during the installation, try to check those. You might want to use a tool such as nvm to install and manage various node/npm versions.

Submodules

Betslip uses a submodule for assets that live in an external repository and you will have to initialise it.

If the betslip assets submodule is not initialised you might get the following error: Module not found: Error: Can't resolve '../betslip-assets/styles/index.scss'

Solve this by running the commands bellow.

git submodule init
git submodule update

Usage

The repo includes a sample index.html file that can be used to run the betslip standalone with a bunch of basic buttons to enable isolated testing.

Running

npm run devServer:dev

This command starts a webpack-dev-server and sets the environment to dev (meaning the config build-tools/webpack.dev.js will be used). It also passes the initEvent env variable to start the react application. The dev server is served at http://localhost:1236, but the application is accessible via http://localhost:1236/betslip/.

npm run devServer:prod

This command starts a webpack-dev-server and sets the environment to prod (meaning the config build-tools/webpack.prod.js will be used). The production server is served at http://localhost:8080.

Bundling

After running the build commands, the code will be bundled into dist/ folder that will contain a main.js file. Include the file at the bottom of an HTML page and fire the initBetslip event to load Bet Slip component.

npm run build:dev -- --env.initEvent=initBetslip

npm run build:dev starts the building/bundling process and sets the environment to dev (meaning the config build-tools/webpack.dev.js will be used).

npm run build:prod -- --env.initEvent=initBetslip

npm run build:prod starts the building/bundling process and sets the environment to prod (meaning the config build-tools/webpack.prod.js will be used).

npm run build:watch:dev

npm run build:watch:dev starts the building/bundling process and sets the environment to dev (meaning the config build-tools/webpack.dev.js will be used). Additionally, the watch mode will be started, meaning the building/bundling process starts automatically when a file changes/is saved.

npm run build:watch:prod

npm run build:watch:prod starts the building/bundling process and sets the environment to prod (meaning the config build-tools/webpack.prod.js will be used). Additionally, the watch mode will be started, meaning the building/bundling process starts automatically when a file changes/is saved.

npm run build:dev:bundleanalyzer

npm run build:dev:bundleanalyzer starts the building/bundling process and sets the environment to dev (meaning the config build-tools/webpack.dev.js will be used). Additionally, the addon bundleanalyzer (in build-utils/addons/webpack.bundleanalyzer.js) will get started. More information on bundleanalyzer can be found here.

npm run build:prod:bundleanalyzer

npm run build:prod:bundleanalyzer starts the building/bundling process and sets the environment to prod (meaning the config build-tools/webpack.prod.js will be used). Additionally, the addon bundleanalyzer (in build-utils/addons/webpack.bundleanalyzer.js) will get started. More information on bundleanalyzer can be found here.

Code structure

The following folders and files can be found inside the /src folder. Files inside this folder will end up (in some form or another) in the final bundle generated by webpack.

/assets
/betslip
/lineManager
/utilities


/actions
/api
/components
/containers
/middleware
/reducers
/sagas
/store
index.html
index.tsx
typings.d.ts

The two main components are lineManager and betslip. The former is a api that listens to calls from the Market Offer, validates the payload and forwards it to the betslip component. The betslip displays the lines, manages the betting types, the stakes and all that, and places the tickets with the backend.

They are both redux applications, each with its own store. This added complexity is justified by the possibility of having them as possible standalone features in the future. The betslip has a UI written in React.

The utilities folder includes a set of functionality that are shared between the other two components, like error handling and sessionStorage functionality.

Line Manager

/actions

In /actions we can define the actions as well as the action creators.

/api

In /api we can define the listeners and senders to manage communications to and from the Market Offer and the Betslip. API calls are done using postRobot to allow for cross-window communication in the case we will have to integrate with other systems that require rendering within separate iframes.

/core

In /core we define the core functionality. The API calls get processed here before being dispatched to the betslip and saved to the store.

/invalidator

The invalidator handles line expiration. When a line is added, we set a timer that will expire the line in the betslip. The timers ids are saved in the store so we can easily manage them and avoid the weird behaviours witnessed in the old RGS.

/reducers

In /reducers we can define various reducers that manage various parts of the application state.

/store

In /store we can configure the store. Here, we inject our sagas as well as our middleware.

/validator

The validator makes sure that the payload received via the API is correctly shaped and that the lines are formally valid and not expired. For the lines validation we use ajv and json-schemas.

Betslip

As said, the betslip is a redux application as well, so I will avoid repeting the redux-specific components and focus on the differences.

/betslip-assets

In /betslip-assets we keep fonts, images and styles. It is fetched from an external git repository.

/components

In /components we can define the React components. We decided to keep in here both presentational components have access to state and actions via props and container components. The former don't have any logic (except maybe some UI logic), while the latter are connected to the Redux store and pass the state and actions down (via props).

/sagas

In /sagas we can define sagas. Sagas are like separate threads that help us to manage asynchronous calls.

/services

In /services we include functionality that will rely on external services like the configuration service.

/validator

The betslip validator does not validate the lines (they are validated in the lineManager). Instead it validates the data that we are passing around internally. It also validates the ticket before sending it out to the server.

Utilities

/errorHandler

The errorHandler file contains error classes we can create which can include useful information about what was going on when the error occurred. Error messages are contained in the errorStrings.json file and are referenced using an errorCode property in the error object.

/formatNum

The formatNum file includes a bunch of helper functions used to format numbers in specific ways depending on where we are using them.

/lineGenerator

The lineGenerator is a helper function we use in the tests to generate compliant random lines.

/logger

The log functions is used to send frontend error logs to an external service so that we can keep track of what is going wrong in the frontend and can fix bugs that would otherwise be very difficult to reproduce or would simply never be reported.

/sessionStorage

The betslip state can optionally be saved in the sessionStorage. In this file we implement a bunch of checks to ensure a smooth and predictable user experience.

genertic.ts

In this file we have a few functions that abstract simple, generic problems.

index.html

The index.html file includes the #root div container in which the application will be renderd. This file also is the template file for the html-webpack-plugin plugin which automatically includes extracted stylesheets and bundled scripts into that template.

index.tsx

The index.tsx file "boots up" the whole application. Here, the store is created and injected into the Root component. An event listener is added to window that listens to __INIT_EVENT__ that can be set via CLI. For an event initBetslip it would look something like that:

npm run build:prod -- --env.initEvent=initBetslip

The event initBetslip is eventually sent by the host application (e.g SRLive). After the event was received, the callback will execute the renderApplication function which will call the react-dom render function.

typings.d.ts

The typings.d.ts file is a TypeScript type definition file. In this file we have to define the same constants (e.g. __ENVIRONMENT__ and __INIT_EVENT__) as we do in webpack.common.js with the DefinePlugin. Otherwise, the TypeScript compiler will spit out errors.

build-utils

Within the build-utils folder are a variety of files that are used to create the bundle. Below is a list of all the currently available files and an explanation to each.

common-paths.js

The common-paths.js file defines paths that may be used by different modules. Those paths usually are the path/location of the source code (entryPath) and the path/location of the final bundle (outputPath).

customer-config.js

The customer-config.js is used to create custom bundles. A config function is exported which in turn exports a config object. Currently, the function accepts the parameters customer and stlye. customer defines which clientsetup should be used (default value is 'client1'). style defines which style should be used (default value is 'style.scss'). The config object currently has a property CUSTOMER_STYLES_PATH which is simply the path to the customer style (e.g. client1/style.scss).

vendors.js

In the vendors.js file we export an array of installed modules. These modules will be bundled into a separate file because there unlikely change and thus will be cached by the browser. There is an array vendors which inlcudes all the modules we want to bundle separately.

webpack.common.js

In the webpack.common.js file we define the webpack configurations that are common to the production build as well as to the development build (ie. dev and prod). Here we tell webpack to look for our source code, where to put the bundle, what code transformations (e.g. typescript, babel, ...) do we want to have, and we also define some global variables that we can use during development.

webpack.dev.js

The webpack.dev.js file will be used when we set the environment to dev. Here we tell webpack to look for SCSS/CSS and put it directly (inline) into our bundle. Since during development we want everything to work as fast as possible we also won't make any minifications or optimizations.

webpack.prod.js

The webpack.prod.js file will be used when we set the environment to prod. Here we tell webpack to extract SCSS/CSS into a separate file, execute some minifications/optimizations, and split out bundle into different files. We added a new entry for vendors. Vendors are modules that unlikely to change, and we want them in a separate package because the browser can cache them for us.

addons

In the addons folder we put webpack plugins that can help us optimize our build process. Currently, we have the following addons available:

  • webpack.bundleanalyzer.js: bundleanalyzer will analyze the bundle and show what modules are in the bundle as well as their size. A small web page is automatically created that visualizes the bundle.

root directory

Below is a list of files within the root directory of this project and an explanation to each file.

.babelrc

The .babelrc file is used to configure Babel. Babel is used to transpile newer (even stage-0) ECMAScript features down to ES5. Babel is also used to transpile JSX via babel-preset-react.

package-lock.json

The package-lock.json file is generated by npm. This file defines the exact dependencies for the dependencies listed in package.json. This is supposed to guarantee that the exact same versions of dependencies are installed whenever npm install is executed.

package.json

The package.json file defines, i.a., scripts, and dependencies needed for this particular project.

tsconfig.json

The tsconfig.json file configures the TypeScript compiler.

webpack.config.js

The webpack.config.js file composes the webpack configuration. When building, webpack will look for a file with this name.

Testing

For testing the framework Jest is used. Jest allows to simply create unit tests, code coverage, and snapshot testing. If you want to write a test, e.g. for a reducer or an action, you should put your test in the same folder as the thing you want to test. If you want to test a reducer reducers/myReducer.ts then you colocate the file reducers/myReducer.test.ts. A test suite could look like as follows:

import myReducer from './myReducer'
import * as actionTypes from '../actions/actionTypes'
import {increment, decrement} from '../actions/actions'

// Describe test suite for reducer 'myReducer'
describe('myReducer', () => {
    let state = {
        count: 1
    }
    it('should handle INCREMENT', () => {
        expect(myReducer(state, {type: actionTypes.INCREMENT})).toEqual({count: 2})
    })
    it('should handle DECREMENT', () => {
        expect(myReducer(state, {type: actionTypes.DECREMENT})).toEqual({count: 0})
    })
})

// Describe test suite for action 'increment'
describe('increment', () => {
    it('should return an object with the value \'INCREMENT\' for the key \'type\'', () => {
        expect(increment()).toEqual({type: INCREMENT})
    })
})

// Describe test suite for action 'decrement'
describe('decrement', () => {
    it('should return an object with the value \'DECREMENT\' for the key \'type\'', () => {
        expect(decrement()).toEqual({type: DECREMENT})
    })
})

To run all tests, you simply have to run npm run test. The script test will call jest with options specified in the jest.config.json file.

Only run specific tests

Jest provides the --testNamePattern=<regex> (alias is -t) option for only running tests that comply to the provided regex. It will check the string passed to describe or test.

If npm run test -- -t 'action' is executed then every suite that didn't match with action is skipped. In the following example only the second suite (action myAction) would be tested.

describe('reducer myReducer', () => {
    it('should do something', () => {
        // the test
    })
})

describe('action myAction', () => {
    it('should do something', () => {
        // the test
    })
})

Linting

For linting TSLint is used. TSLint is a customizable tool that checks TypeScript code for readability, maintainability, and functionality errors. This linter is setup to perform standard checks that can suits different coding styles, giving developers the possibility to add their own custom rules on top of the default ones.

Packages

This project uses tslint and tslint-react as development dependencies.

TSLint rules

Default TSLint rules are defined in the tslint.json file. You can modify Linter behaviour by adding your own rules there. TSLint core rules can be found here

Running linter in your code editor or IDE

Subime Text 3

  1. Be sure you have TSLint installed globally on your machine;
  2. Using Package Control install SublimeLinter, a Sublime Text framework supporting different types of linter;
  3. Install a specific linter supporting TSLint such as SublimeLinter-contrib-tslint; You can choose lint mode via the command palette of Sublime Text by typing Sublime Linter: Choose Lint Mode

IntelliJ Idea

Detailed configuration instructions can be found here https://www.jetbrains.com/help/idea/tslint.html;

Running linter via terminal

Linter can be also launched directly via terminal by typing the command npm run lint.