Features : Unit Testing, Environment variables, Automatic documentation, Production App Server, Automatic deployment, Dependencies version check
Many code chunks and tutorials are available on the web to configure a Ionic workflow that includes features such as Unit Testing, Environment Variables and others, but most of it are obsolete or depends on specific dependencies, version or configuration that is hard to put together or maintain through time and Ionic core evolution.
There is also tons of starters git repositories but nobody explain how to build their solution from scratch, so that people can understand and implement it in an existing project.
That's why I make this repository. You will find here a step by step guide, where nearly all implementations are versions specific and based on Ionic official repositories, so that this guide will still be consistent 3 months later and can easily be updated with Ionic updates.
====================================================================================
You'll find next a step by step guide, but if you want to make this quick, you can simply clone this repository. It contains the exact code that will be created by following the guide.
git clone https://github.com/RomainFallet/ionic-workflow-guide/
====================================================================================
I'm specifying Git, Node, Npm, Cordova and Ionic-CLI version in case a new version of these packages would break something or add an error/warning in the future. This will ensure that you'll follow this guide in the exact same conditions, but it may work with different versions of these.
https://nodejs.org/download/release/v7.10.0/
sudo npm install -g [email protected] [email protected] [email protected]
git clone https://github.com/driftyco/ionic2-app-base ./my-app
cd ./my-app && git reset --hard 49e70da
import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
IonicModule.forRoot(AppComponent)
],
bootstrap: [IonicApp],
entryComponents: [
AppComponent
],
providers: [
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
import { Component } from '@angular/core';
@Component({
templateUrl: 'app.component.html'
})
export class AppComponent {
rootPage: any;
constructor() {
}
}
<ion-nav [root]="rootPage"></ion-nav>
"@ionic/cli-plugin-cordova": "1.0.0",
"@ionic/cli-plugin-ionic-angular": "1.0.0"
"@types/jasmine": "2.5.41",
"@types/node": "7.0.8",
"angular2-template-loader": "0.6.2",
"html-loader": "0.4.5",
"jasmine": "2.5.3",
"jasmine-spec-reporter": "4.1.0",
"karma": "1.5.0",
"karma-chrome-launcher": "2.0.0",
"karma-jasmine": "1.1.0",
"karma-jasmine-html-reporter": "0.2.2",
"karma-sourcemap-loader": "0.3.7",
"karma-webpack": "2.0.3",
"null-loader": "0.1.1",
"protractor": "5.1.1",
"ts-loader": "2.0.3",
"ts-node": "3.0.2"
2.2 Add Unit Testing conf files (this will add two directories "e2e" and "test-config" in your root folder)
git clone https://github.com/driftyco/ionic-unit-testing-example.git ./tmp
cd ./tmp && git reset --hard 7b1f2a6 && cd ../
cp -r ./tmp/test-config ./
mkdir e2e && cp -r ./tmp/e2e/tsconfig.json ./e2e && rm -rf ./tmp
"test": "karma start ./test-config/karma.conf.js",
"test-ci": "karma start ./test-config/karma.conf.js --single-run",
"e2e": "webdriver-manager update --standalone false --gecko false; protractor ./test-config/protractor.conf.js"
npm install
2.5 Let's add your first test for your AppComponent. Create a new file "src/app.component.spec.ts" and add the following
import { async, TestBed } from '@angular/core/testing';
import { IonicModule } from 'ionic-angular';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
let fixture;
let component;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AppComponent ],
imports: [
IonicModule.forRoot(AppComponent)
],
providers: [
]
})
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
});
it ('should create a valid instance of AppComponent', () => {
expect(component instanceof AppComponent).toBe(true);
});
});
You can run Unit Tests with :
npm run test
If you need a single run execution, use :
npm run test-ci
To run end to end test (e2e), use :
ionic serve
then :
npm run e2e
import { async, TestBed } from '@angular/core/testing';
import { IonicPageModule, Config } from 'ionic-angular';
import { ConfigMock } from '../../../test-config/mocks-ionic.ts';
import { HomePage } from './home';
describe('HomePage', () => {
let fixture;
let component;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HomePage ],
imports: [
IonicPageModule.forChild(HomePage)
],
providers: [
{ provide: Config, useClass: ConfigMock }
]
})
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomePage);
component = fixture.componentInstance;
});
it ('should create a valid instance of HomePage', () => {
expect(component instanceof HomePage).toBe(true);
});
});
import { async, TestBed } from '@angular/core/testing';
import { IonicModule, Config } from 'ionic-angular';
import { ConfigMock } from '../../../test-config/mocks-ionic';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let fixture;
let component;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeaderComponent ],
imports: [
IonicModule
],
providers: [
{ provide: Config, useClass: ConfigMock }
]
})
}));
beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
});
it ('should create a valid instance of HeaderComponent', () => {
expect(component instanceof HeaderComponent).toBe(true);
});
});
git clone https://github.com/RomainFallet/ionic-workflow-guide ./tmp
cp -r ./tmp/{env,webpack.envars.js} ./ && rm -rf ./tmp
3.2 Add this inside your "package.json". This will add your environment variables when you'll use Ionic commands to build or serve your app.
"config": {
"ionic_webpack": "./webpack.envars.js"
}
3.3 Add this inside your "test-config/karma.conf" to make your environment variables available during your tests.
var envarsConfig = require('../webpack.envars.js');
declare const ENV: any;
3.5 For example, you can use it that way in your "src/app.component.ts" file (you can add more variables inside your "env/prod.json" and "env/dev.json" files)
import { Component } from '@angular/core';
declare const ENV: any;
@Component({
templateUrl: 'app.component.html'
})
export class AppComponent {
rootPage: any;
constructor() {
console.log('isProduction : ' + ENV.PRODUCTION);
}
}
If your serve your app now :
ionic serve
You'll see in your browser console :
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { enableProdMode } from '@angular/core';
declare const ENV;
if (ENV.PRODUCTION) { enableProdMode(); }
platformBrowserDynamic().bootstrapModule(AppModule);
"build": "ionic-app-scripts build --prod"
npm run build
This will create static assets of your app inside your "www" directory. Open your "www/index.html" in your browser and you'll see :
We have a cordova error but that's OK because we are running our app in browser. Now we see that our app is using our production environment variables and that the angular "enableProdMode" warning is gone.
Our app is fully in production mode !
"@compodoc/compodoc": "1.0.0-beta.9"
npm install
"compodoc": "./node_modules/.bin/compodoc -d ./docs/ -p ./tsconfig.json"
npm run compodoc
Then, open "docs/index.html" in your browser to see your docs.
If you're using Github for your app source control. You can simply publish your docs to Github pages. Go to your project settings and set your page source to your "docs" folder.
I made it for this repo, you can view it here : https://romainfallet.github.io/ionic-workflow-guide/
What you don't want with your docs is that the published version on Github pages does not match the last changes you made. To automatically update your docs when you push somes changes, append this to "scripts" inside your "package.json" :
"git-push": "npm run compodoc && git add ./docs && git commit -m 'Update docs' && git push -u origin master"
Then, use npm run git-push
instead of git push
to publish your commits. That way, you are sure that your last commits are fully and automatically documented.
We saw in the Environment Variables set up that we can build our app in production mode with :
npm run build
That packages our production ready app inside the "www" folder. Now we want to serve it.
"express": "4.15.2",
"compression": "1.6.2"
npm install
const express = require('express');
const compression = require('compression');
const path = require('path');
const app = express();
const port = process.env.PORT || 8080;
const ENV = require(path.join(__dirname + '/env/' + (process.env.NODE_ENV ? process.env.NODE_ENV : 'dev') + '.json'));
const forceSSL = function() {
return function (req, res, next) {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect(['https://', req.get('Host'), req.url].join(''));
}
next();
}
}
// We force HTTPS and GZIP compression on production
if (ENV.PRODUCTION) {
app.use(forceSSL());
app.use(compression());
}
app.use(express.static(__dirname + '/www'));
// We redirect all GET requests to our 'index.html' to let Ionic handle the rooting
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname + '/www/index.html'));
});
app.listen(port, function() {
console.log('Listening on port ' + port + '. isProduction : ' + ENV.PRODUCTION);
});
"start": "NODE_ENV=prod node ./server.js"
npm run start
Now that we have a production ready server. We need to deploy it somewhere in the web !
6.5 Move "typescript", "@ionic/app-scripts" and "@ionic/cli-build-ionic-angular" from "devDependencies" to "dependencies" inside your "package.json"
"dependencies": {
"@ionic/app-scripts": "1.3.7",
"typescript": "~2.2.1",
"@types/jasmine": "2.5.41",
This will make Heroku able to build our app.
"heroku-postbuild": "npm run build"
With this, Heroku will build our app in production mode as soon as our dependencies will be installed. We use "ionic-app-scripts" (which is now packaged in our dependencies) instead of "ionic" because the Ionic CLI is only installed on our computer locally.
This will make Heroku use the same environment in which we develop our app.
"engines": {
"node": "7.10.0",
"npm": "4.6.1"
}
To deploy, you just have to commit some changes. The automatic deploy will do the rest.
git add . && git commit -m 'Add Heroku deployment' && npm run git-push
I made it for this repo, you can view it here :
https://ionic-workflow-guide.herokuapp.com/
"npm-check": "5.4.4"
npm install
"check": "node ./node_modules/npm-check/bin/cli.js -u"
npm run check