The ALLTHINGS app — based on react.js.
- Prerequisite
- Overview
- Redux
- Developer notes
- Tests
- Deploying
- Testing the legacy bundle in your evergreen browser
- Troubleshoot
- UI
As specified in the package.json file, the node engine must be >= 6.5.0 and the npm version must be >=3.0.0.
The best way to manage multiple active NodeJS versions is to use nvm. Please refer to its documentation.
"resolutions": {
"uglifyjs-webpack-plugin/uglify-es": "3.1.6"
}We needed to pin the package uglify-es to a specific version because of a bug that occured.
See: https://github.com/allthings/app/pull/1087
This needs to be revisited soon, to make sure we can get rid of this again.
yarnLinux user don't need fsevents and should run the following command:
yarn --ignore-optionalyarn buildThis will build 3 bundles:
- Server side build
- Client vendor build (
react,redux,glamor, etc. seewebpack/dll/dll.base.config.js) - Client side build (with webpack)
Server side code will land into dist folder and client side code into public/static/js.
yarn startThis will watch the file system for change inside the src folder and trigger an incremental build.
After it was rebuilt the node container will be restarted so that the server side can serve the correct bundle.
Important: Webpack DLL is not run in an incremental build. This has to be done either manually by npm run webpack:dll or a full build npm run build.
If everything worked you can go to https://app.dev.allthings.me and see the app running.
If you are targeting the legacy bundle - by default only the modern one is built - append the legacy keyword:
yarn start legacyIf you want to deep dive into the content of the bundles in order to optimize them, set the following environment variable before performing a yarn build:
export WEBPACK_REPORT=1The reports will be generated and opened automatically.
To disable it run:
unset WEBPACK_REPORTAPP_DOMAIN="<your allthings app domain>" \
OAUTH_CLIENT_ID="<your oauth client id here>" \
yarn start:standaloneThen navigate to https://app.localhost/
Note: Since the standalone environment uses a self-signed SSL certificate, the first time running the standalone development environment you'll need to confirm an exception to the SSL certificate in your browser. Do this by opening the following two URLs in your browser:
open https://app.localhost/
open https://app-cdn.localhost/Note 2: Your OAuth client must be valid in the Target API Environment and must be a "public" Client with Authorization Code grant allowed. If it's not a trusted Client, it also needs DPA and TOS fields filled for the User Authorization Screen.
Note 3: The App Domain must exist in the Target API Environment
Note 4: If the localhost subdomains don't resolve, you might try adding the following to your /etc/hosts file.
127.0.0.1 app.localhost
127.0.0.1 app-cdn.localhost
127.0.0.1 app.staging.localhost
127.0.0.1 app.prerelease-magenta.localhost
One can point the App at different environments by using different hostnames:
| API Environment | URL |
|---|---|
| Production | https://app.localhost/ |
| Staging | https://app.staging.localhost/ |
| Prerelease | https://app.prerelease-magenta.localhost/ |
componentsare not connected to the store and only serve presentational purposecontainersshould connect to the store and render components passing datamicroappsare accessible to logged in users and represent a self-contained unitwith a menu item and own color theme, e.g.Settingspagesare located directly under the root node and are independent of any other part of the app.they can however be wrapped by containers if they need to be connected to the storeserveris responsible for the server-side rendering of the appstorecomprises actions, reducers and the store configutilsact like internal libraries and offer functions that are used throughout different parts of the app
The basic workflow in this app is powered by redux, which basically consists of a store, actions and reducers.
Nothing too fancy, holds the state of the app.
To create one or multiple actions use createActions. Should be placed in src/actions
const actions = createActions({
simpleAction (foo, bar) {
return { payload: { foo, bar } }
}
// ...
})When called, this action will be dispatched automatically with the associated type (given by its name):
actions.simpleAction('Hello', 'World')
/*
This is what the dispatched object looks like:
{
type: 'simpleAction',
foo: 'Hello',
bar: 'World'
}
*/An async action is recognized by returning a callback instead of an object.
Params given into the callback
| Param | Type | Description |
|---|---|---|
dispatch |
function | Dispatches actions. |
api |
object | A function to call the API (provided by the js-sdk) |
state |
function | The current application state. |
const actions = createActions({
asyncLogin (user, pass) {
return async (dispatch, api, state) => {
dispatch({ status: 'pending' })
const response = await api({
path: 'api/some/endpoint',
params: {
foo: state.bar
}
})
if (response.status.code === 200) {
dispatch({ status: 'ok', payload: response.entity })
} else {
// Example for an error
dispatch({ status: 'error', payload: response.error })
}
}
}
})const reducers = createReducers({
simpleAction (state, action) {
return {
...state,
}
},
asyncLogin (state, action) {
// action.status one of 'pending', 'ok' or 'error'
}
})yarn lintWhenever you create or edit a React component or any other JavaScript file, you must update or create a corresponding test file in the same directory.
By convention the test file should be named after the file's name with an additional .test. part:
SomeDir
| - MyButton.jsx
| - MyButton.test.jsxSomeOtherDir
| - utils.js
| - utils.test.jsTo manually trigger the unit tests, run:
yarn test:unitYou can also use the corresponding watch task:
yarn watch:test:unitThe unit tests are performed using the Jest platform, please refer its documentation.
React component testing is based on the Enzyme testing utility, please refer to its documentation.
The use of snapshots generated by the toMatchSnapshot() method is strongly encouraged. Those snapshots must be always commited as they are a very useful tool whenever you want to make sure your UI does not change unexpectedly
This is the default and preferred way of writting / running e2e tests. Whenever you have to refactor or create new specs, please use the new setup!
The exhaustive documentation can be found in the e2e repository.
K.I.S.S. principle is the golden rule here 📜.
A spec:
- should not be any longer than 100 loc, if not split it!
- should be purely declarative, easy to read. Do not introduce complex utility functions for the sake of making it cleaver, it should be easy to understand!
- should be testing one piece of functionality, not a complete section (e.g. create / edit / delete).
- should not test the back-end. The back-end has its own set of unit tests.
- should not rely on data you have created or modify in another spec. If you expect something to be there, you probably need the db-data dump to be adjusted to your need.
The specs are living in ./test/wdio/test/app/specs/.
The specs are using webdriver i/o, please refer to its own documentation.
You can either use chrome or firefox directly without any additional setup (more browsers / devices are available, please check the e2e repository):
# Run all the specs against chrome and firefox.
yarn wdio# Run one spec against chrome.
yarn wdio:chrome --spec custom-filter-creation# Run one spec against firefox.
yarn wdio:firefox --spec custom-filter-creationGetting Cannot start service geckodriver / Cannot start service chromedriver?
cd ./test/wdio && docker-compose down -v
The e2e tests are written with Nightwatch.js.
New tests can be simply added by creating new spec files in the test/e2e/specs
directory.
In order to wait for an element before performing any kind of interaction (e.g. a click), you should use the following custom command:
browser
.waitForElementClickable(someElement)
.click(someElement)Please note that the tests are clustered in four different directories (test/specs/app/pipeline-1, test/specs/app/pipeline-2, test/specs/app/pipeline-3, test/specs/app/pipeline-4) in order to get them running concurrently in the CI! They are organized in a way that the average execution time of each folder is almost similar.
yarn e2eUnless npm run e2e has been executed already, run the following command once:
yarn release-buildNext you'll be able to debug the e2e tests with the following command:
yarn e2e-vncyarn e2e-devyarn e2e-localyarn e2e-browserstackBy default, this uses a random BrowserStack environment (usually Firefox on Windows). To select a specific environment, use the following command:
yarn e2e-browserstack --env iphone|android|edge|ie|safari|firefoxyarn e2e-browserstack-localRun only one or multiple tags:
yarn e2e --tag login
yarn e2e --tag password loginSkipping tag(s) can be performed as follow:
yarn e2e --skiptags login
yarn e2e --skiptags login,passwordDeploy to AWS with yarn deploy ENVIRONMENT
yarn deploy stagingdeploys to staging stageyarn deploy prerelease-ENV_NAMEdeploys to prerelease stage (replaceENV_NAMEwith the prerelease name: check wiki for available environments)yarn version(enter version when prompted) ornpm version [major|minor|patch]deploys to production
If you are having issues deploying, please check the Troubleshoot section.
Append force-legacy-bundle as a query parameter to the URL.
Your node_modules are out of date. Run yarn and try again.
This probably means one of our packages was updated to a newer version, and this package has sub-dependencies which were also updated to a newer version. The integrity check fails because the yarn.lock file still lists the old versions of the sub-dependencies.
Delete your yarn.lock, run yarn, and then push your newly created yarn.lock.
If you receive this after running the command above
Starting e2e_chromedriver_1 Waiting for chromedriver:4444 to become available ... timeout nc: bad address 'chromedriver'
Execute yarn e2e-dev --down and run the tests again
The UI is based on Allthings Elements.
You can find some useful ressources regarding Elements over here:
- Elements documentation
- Latest Elements Storybook (based on the latest
masterbranch) - Elements Examples (some very basic public Examples)
- Elements Playground (some internally used Examples)
We're using the Allthings Color Palette for every colorization within the App. You can find the repo over here