Skip to content

topheman/npm-registry-browser

Repository files navigation

npm-registry-browser

Build Status Cypress Demo

There are lots of great resources on React out there. What might be missing is some projects mixing real world constraints like:

  • API calls
  • using external libraries (UI kits, http clients ...)
  • some routing and global state management
  • code quality good practices (linting, testing, git hooks, cis ...)
  • automation / dev pipeline
  • and more ...

The hard part is often to be able to put all those together. This is the goal of this project: provide a well-documented example of a front-end app with real-world features and constraints.

📔 Read the blog post where I explain why I made the project.

📺 Watch the video of the talk 🇫🇷

🚀 Get started right away

"I'm a developer, I don't read docs, just give me the gist!"

git clone https://github.com/topheman/npm-registry-browser.git
cd npm-registry-browser
npm install
npm start

It just works ...!

However, you should take a look a the rest of the docs (you will find explanations and advanced uses).

What this project is not

  1. A simple boilerplate. It aims to be more than that: expose quality sample code that you could learn from, at a project level.
  2. The ultimate answer. There are things you would have done differently and it's ok. Other things will evolve with time.

Infos

This project was bootstrapped with create-react-app.

The whole README from create-react-app is available here.

Here's a list of some of the technologies I'm using:

🗒 Read the notes I took along the way.

Prerequisites

  • Nodejs v8
  • npm v5

Install

git clone https://github.com/topheman/npm-registry-browser.git
cd npm-registry-browser
npm install

Run

npm start

Checkout API proxy for development to understand how the API servers are served in development mode.

Build

npm run build

Will build the a production version of the website in the build folder.

Serve

Once you've built you're app, you can test the build on a local server with:

npm run serve

Test

The following command will run both unit and e2e (cypress) tests.

npm test

Check out Error npm test on MacOs Sierra if you're experimenting some troubles.

You can choose to run them separately.

Unit

You'll find unit tests spread in the src folder inside __tests__ folders in files named like *.spec.js.

I'm using Jest as a test runner and enzyme / react-testing-library as testing utilies.

  • npm run test:unit : single run of the unit tests
  • npm run test:unit:watch : run the unit tests in watch mode

Check out Error npm test on MacOs Sierra if you're experimenting some troubles.

End to end

End-to-end testing is a technique used to test whether the flow of an application right from start to finish is behaving as expected. The purpose of performing end-to-end testing is to identify system dependencies and to ensure that the data integrity is maintained between various system components and systems.

The entire application is tested for critical functionalities such as communicating with the other systems, interfaces, database, network, and other applications.

I'm using cypress.io for the e2e tests. You will find them in cypress/integration.

  • npm run test:cypress : single run the e2e tests. It will:
    • build the project and serve it on http://localhost:5000 (that way, your tests reflect exactly what the end user would see in production)
    • run the tests in cypress/integration folder
    • tear down once tests are passed (or failed)
  • npm run test:cypress:dev : use this one when you're coding your tests. It will:
    • spin up a dev server on http://localhost:3000 (so, you don't have to npm start)
    • open the cypress client that will let you choose which tests you want to run
  • npm run test:cypress:debug-build : use this if your e2e tests only fail on a production bundle, to debug the tests with the production version of your app. It will:
    • build the project and serve it on http://localhost:5000
    • open the cypress client that will let you choose which tests you want to run

When you add a spec file, reference it in cypress/integration/index.spec.js (specific to my cypress.json config, explained bellow).

Since [email protected], each spec file runs in isolation. The tests took much longer to run.

To speed them up, I decided to run all the files in the same spec (tests don't rely on state of previous state - they could be ran in isolation if needed).

A message is output to explain how to run them in a regular way:

Linter

I use eslint to check the coding style, with the following presets:

More on eslint configuration.

The following command will run the linter on your code base. This task is ran at pre-commit to ensure code quality.

npm run lint

Prettier

Prettier is a great tool to enforce a consistent style accross your code base (usefull when working in teams).

Here is how to integrate it with your editor.

Once it's done, when you'll save a file, it will reformat it.

The following command will let you format your code base. This task is ran at pre-commit.

npm run pretty

More on prettier.

Deploy

The demo is hosted on github-pages. A simple way to publish your app is to use the gh-pages package that will create a gh-pages orphan branch on which it will commit and push.

The following script will build then publish your app on your github pages:

npm run deploy

More infos

Continuous Integration (CI)

Each git push triggers a test suite on travis. The following will be ran:

  • linting
  • unit tests
  • end to end test with cypress

The end to end test sessions are recorded, you can check them here.

Continuous deployment (CD)

Staging

On each commit (or PR) pushed to master, if the tests are passing, a version of the website will be automatically deployed from travis to a staging server: https://staging-npm-registry-browser.surge.sh/ (that way, your QA team will be able to test your latest stable features before you release them)

Production

On each tag pushed to master:

🚚 More infos about Continuous Deployment

Commit guidelines

To have uniform commit messages, I follow the AngularJS git commit guidelines, please take a look at it.

It also makes it easier to generate changelogs.

To generate changelog:

npm run generate-changelog -- v1.1.0 v1.2.0

Ready to be pasted to the github releases part.

Advanced

Mock mode

You can test an online demo of the mocked version of the app (open the console to see the mocked requests).

Serve mocks

Thanks to src/services/apis, the api calls can be mocked at any time. The following command will let the api manager serve the mocks saved in src/services/apis/mocks.

npm run dev:mock

It can be useful when the front-end and back-end teams are developing the same feature at the same time, so as a front-end developer, you don't have to wait for the server to be completed.

It can also be applied once you have a backend to mock your api calls while coding or testing, to have deterministic responses from your http client.

Record mocks

I made a utility based on nock in bin/record-http-mocks.js to automate the recording of the mocks by declaring which urls you want to mock and automatically generate those files.

Specify your config in bin/record-http-mocks.js and

npm run record-http-mocks

Make a build with mocks

You can even make a mocked build version of the app.

Warning: Like in development, mocked requests will be intercepted (won't go to the server) and you will be shipping mocks (and the code that implements the mocking part) to your bundle.

This could be used for e2e testing purposes.

npm run build:mock

Online demo of the mocked version of the app

FAQ

Why use create-react-app ?

The goal of this project is to focus on how to put the pieces together to make a front-end app, to focus on architecture, not get stuck on webpack configurations 😉.

Toolkits are becoming more popular. Developers are tending to use them or make their own.

Since create-react-app is the most popular toolkit in the react community, I chose this one, with a challenge/constraint: NOT TO EJECT.

Why not eject ?

  • So that when you dive in the project, you don't have any more overhead from enforcing any weird configuration or tool
  • To challenge some use-cases where people tell you that you should eject when you can still remain

What couldn't I do with create-react-app ?

There are some points that I couldn't address with an unejected create-react-app:

  • Not being able to add babel-presets/babel-plugins: usually I use a few ones such as:
  • Not being able to alias modules via webpack config: when hacking/forking a module, it can come handy. This is why there is a src/libs folder (temporary).
  • Use HtmlWebpackPlugin and webpack.DefinePlugin to expose vars generated on the fly (such as retrieving the git hash, the date of the build ...) - I managed to do that with bin/expand-metadatas.js.

For those who want to play with webpack configurations, you can access a starter kit I open sourced: topheman/webpack-babel-starter.

Why not use redux ?

People often choose Redux before they need it. “What if our app doesn’t scale without it?” Later, developers frown at the indirection Redux introduced to their code. “Why do I have to touch three files to get a simple feature working?” Why indeed!

People blame Redux, React, functional programming, immutability, and many other things for their woes, and I understand them. It is natural to compare Redux to an approach that doesn’t require “boilerplate” code to update the state, and to conclude that Redux is just complicated. In a way it is, and by design so.

You Might Not Need Redux by Dan Abramov

I wanted to show that you don't necessarily need redux to make a web app like this one.

Also, not adding redux kept the codebase more agnostic.

Maybe in further versions, I will add redux while adding other features.

Why use Material UI ?

In every enterprise app, you're using some kind of UI Kit (whether it is homemade or based on libraries like material-ui, semantic-ui or bootstrap).

Working with such a library was part of the constraints I set.

I had never used material-ui, it was the opportunity of testing it (and also test the css-in-js paradigm that I knew of, but never coded with).

Why use Recompose ?

Recompose is a "React utility belt for function components and higher-order components" that you're likely to run into if your coding on React projects.

material-ui relies on it, so at least a part of the library is already shipping into the final bundle. That way, i get to show some code you might encounter on other projects.

Next

This project is a work in progress, here are some of the next features I'll be working on:

  • Test pluging an other css-in-js library to material-ui (default one is jss)
  • Form management use case (to show a more advanced way of state management - maybe using redux ?)
  • Add some i18n (since the content of the app is all in english, first find some adapted feature to apply it)
  • Add prettier git-hook on travis
  • Try out React Suspense (next versions of React)
  • Upgrade react-scripts / use webpack 4 ?