This project includes configuration and tooling that conforms to Crema's baseline best-practices for a Mobile Application.
🧰 Tools Used
- Expo for simple configuration 😅
- ESLint for code linting
- Jest for unit tests
- Loki for visual testing
- Prettier for code formatting (via ESLint plugin)
- Storybook for component playground (and used by Loki)
- TypeScript for Static Typing in JavaScript (Learn)
- Install Node/NPM
- Install NVM (Node Version Manager)
nvm install 'lts/*' && nvm use
npm i -g expo-cli
npm i
(install project dependencies)- Install the ESLint plugin for
your editorVS Code - Enable "Auto-Fix on Save" in
settings.json
:
// There will likely be other settings within this JSON object...
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
]
}
- Ensure that
XCode
and the related command line tools are setup using this guide - Ensure that
Android Studio
and a virtual device (Emulator) are setup using this guide - Ensure that
adb
is setup correctly, if you have followed step9
it should be.- If
adb --version
outputs a version then you should be good to go.
- If
You can start the expo process with a number of different commands that each do something slightly different.
npm start
- Starts the expo development server but does not open the app on any devices.npm run start:ios
,npm run start:android
,npm run start:web
- Starts the expo development server and attempts to open the app on a device that matches the platform in the script (i.e.ios
,android
, orweb
).npm run start:visual
- Starts the expo development server using the storybook entry point (i.e.index.storybook.tsx
) as well as starts the storybook server.
There are a number of different ways to run your app, some virtual and some on physical hardware.
As long as XCode is setup correctly you should be able to start the Simulator directly from the Expo dev tools by pressing i
in the terminal, clicking Run on iOS simulator
, or by starting the app with npm run start:ios
.
If Android Studio is installed correctly you should be able to open an emulator by following these steps:
- Open the Android Studio app
- Click the button
Configure
in the bottom right of the window - Click
AVD Manager
from the list of options - Click
Launch this AVD in the Emulator
(It's the green play icon )- If there are no devices in the list then create one by clicking
Create Virtual Device
- If there are no devices in the list then create one by clicking
- You should be able to launch the app on the Emulator from the Expo dev tools by pressing
a
in the terminal, clickingRun on Android device/emulator
, or by starting the app withnpm run start:android
.
More to come in the future, for now here are some convenient links to consider.
Linting is done with ESlint
and Prettier
and can be ran with npm run test:lint
or npm run test:lint:fix
which will attempt to auto-fix any issues that it can.
The relevant configuration files are:
.eslintrc.js
.prettierrc.js
Type checking is done with tsc
and can be ran with npm run test:types
or started in watch mode with npm run start:types
.
The typescript config is at tsconfig.json
.
Unit testing is done with Jest and can be ran with npm run test:unit
or started in watch mode with npm run start:unit
.
Visual testing is done with a combination of Storybook and Loki and has a little more setup that the other test scripts.
- Expo needs to be started with the Storybook root, this is done by running
npm run start:visual
. - Load the App onto any device you are testing.
At this point clicking on one of the stories in the Web UI should display that story on any device that loaded the App onto.
Story files are first placed in the same directory as the component and then imported into src/stories/index.tsx
.
Notes:
- If you are testing on Android make sure the Android Emulator is already running before you run
npm run start:expo
. You need to stop any other expo processes before you run this. - If you see
adb: error: no devices/emulators found
then it didn't connect to the Android emulator for some reason. - You may need to reload the app on the devices to connect to Storybook, you can do this with CMD+R on iOS or pulling down the notification shade and clicking reload on Android.
To start Loki you run npm run test:visual
, npm run test:visual:ios
, or npm run test:visual:android
depending on which target you want to test.
More to come in the future, for now here are some convenient links to consider.
More to come in the future, for now here are some convenient links to consider.
Run the following scripts with npm run <SCRIPT_HERE>
:
start
- start the expo bundlerstart:android
- start the expo bundler and an android emulatorstart:ios
- start the expo bundler and an iOS simulatorstart:web
- start the expo bundler and the web appstart:visual
- start component playground with storybookstart:types
- start tsc in watch modestart:unit
- start the unit tests in watch modetest:ci
- run the same tests that the CI runstest:types
- run tsctest:lint
- run lintertest:lint:fix
- run linter and fix if possibletest:unit
- run unit teststest:visual
- run visual tests with loki (this assumes that storybook is running)test:visual:approve
- approve visual changestest:visual:update
- update or create visual referencesnew:component
- generate a new componentnew:util
- generate a new utilnew:type
- generate a new type
These scripts are located in
package.json
and do not represent the entirety of available scripts, but are the most commonly used.
The src
directory is where the meat of your app is located. Below is a printout of its file-tree with a description for each part.
src
├── assets // Fonts, Images, Etc.
│ └── logo.svg
├── components // Create a new one with `npm run new:component`
│ └── App
│ ├── __snapshots__ // Generated by Jest from `test.tsx`
│ │ └── test.tsx.snap
│ ├── README.md // Documentation
│ ├── index.tsx // Main Module Code
│ ├── stories.tsx // Playground stories (npm run test:playground)
│ └── test.tsx // Unit Tests (Jest)
├── types // Centralize Type Definitions
│ ├── Entity.ts // Base Type
│ ├── EntityThing.ts // A Sub-Type
│ ├── Id.ts // A Type Alias of `string`
│ ├── MapStateToProps.ts // Includes our `State`
│ ├── State.ts // Redux state interface
│ └── Thing.ts // Silly example used by `EntityThing`
├── utils // Create a new one with `npm run new:util`
│ ├── mySpecialUtil
│ │ ├── __snapshots__ // Generated by Jest from `test.ts`
│ │ │ └── test.tsx.snap
│ │ ├── README.md // Documentation
│ │ ├── index.tsx // Main Module Code
│ │ └── test.tsx // Unit Tests (Jest)
│ ├── decoratorCentered // Used in stories.tsx
│ └── shallowRender // Used to render components in test.tsx
├── stories // Directory used to load storybook stories in one place
│ └── index.tsx
├── index.storybook.tsx // Root Storybook UI Module
└── index.tsx // Root Module