chore(gulp): update e2e and snapshot (#8846)

* chore(e2e): add livereload server and clean task for e2e

* chore(e2e): refactor to use gulp connect and open

* chore(e2e): WIP remove open, add formatting, add reload task

* wip(e2e): use SystemJS for faster dev e2e rebuilds

* chore(e2e): wip removing old gulp file, old e2e task

update template for e2e and port number

* chore(e2e): wip add SystemJS for dev build of e2e, use those tasks instead

* chore(e2e): uncomment out range components

* chore(e2e): wip fix paths for the e2e tempate

* chore(scripts): update README back to the old way

* chore(e2e): code cleanup

* chore(e2e): split tasks into dev and prod, put common tasks in e2e

* chore(e2e): rename e2e templates and add to readme

* chore(e2e): fix dev build so it will work with snapshot

* chore(snapshot): get snapshot working with dev and prod builds
This commit is contained in:
Brandy Carney
2016-10-21 17:59:20 -04:00
committed by Manu Mtz.-Almeida
parent 981e95d034
commit d93070a7d5
13 changed files with 666 additions and 295 deletions

View File

@ -32,10 +32,10 @@
"@angular/forms": "^2.0.0", "@angular/forms": "^2.0.0",
"@angular/http": "^2.0.0", "@angular/http": "^2.0.0",
"@angular/platform-browser": "^2.0.0", "@angular/platform-browser": "^2.0.0",
"@angular/platform-browser-dynamic": "^2.0.0", "@angular/platform-browser-dynamic": "^2.1.0",
"@angular/platform-server": "^2.0.0", "@angular/platform-server": "^2.0.0",
"ionicons": "^3.0.0",
"ionic-native": "^2.0.3", "ionic-native": "^2.0.3",
"ionicons": "^3.0.0",
"rxjs": "^5.0.0-beta.12", "rxjs": "^5.0.0-beta.12",
"zone.js": "^0.6.21" "zone.js": "^0.6.21"
}, },
@ -55,6 +55,8 @@
"@types/semver": "5.3.30", "@types/semver": "5.3.30",
"@types/serve-static": "1.7.31", "@types/serve-static": "1.7.31",
"@types/through2": "2.0.29", "@types/through2": "2.0.29",
"babel-plugin-transform-es2015-modules-systemjs": "^6.14.0",
"babel-preset-es2015": "^6.16.0",
"canonical-path": "0.0.2", "canonical-path": "0.0.2",
"connect": "3.5.0", "connect": "3.5.0",
"conventional-changelog": "1.1.0", "conventional-changelog": "1.1.0",
@ -69,6 +71,8 @@
"glob": "7.0.6", "glob": "7.0.6",
"gulp": "3.9.1", "gulp": "3.9.1",
"gulp-autoprefixer": "3.1.1", "gulp-autoprefixer": "3.1.1",
"gulp-babel": "^6.1.2",
"gulp-cached": "^1.1.0",
"gulp-clean-css": "2.0.12", "gulp-clean-css": "2.0.12",
"gulp-concat": "2.6.0", "gulp-concat": "2.6.0",
"gulp-connect": "^5.0.0", "gulp-connect": "^5.0.0",

View File

@ -2,7 +2,8 @@
## Getting Started ## Getting Started
All of these commands require you to run `npm install` first. Add the `--typecheck` flag to generate type definitions (`.d.ts`) and do type checking, but keep in mind builds and rebuilds when watching will be significantly slower (~1min and ~1s respectively, vs ~20s and ~200ms without typechecking). All of these commands require you to run `npm install` first. To see a full list of the gulp commands, run `gulp`.
### Installing Nightly Version ### Installing Nightly Version
@ -20,14 +21,24 @@ Run `gulp build` or `gulp watch` to watch for changes.
### Building & Running e2e Tests ### Building & Running e2e Tests
1. Run `gulp e2e` or `gulp e2e.watch` with a folder passed to watch for changes. #### Development
2. Navigate to `http://localhost:8080/dist/e2e`
1. Run `gulp e2e` or `gulp e2e.watch` to watch for changes.
2. Navigate to `http://localhost:8000/dist/e2e`
#### Validation
The following commands take longer to run because they use AoT compilation. They should really only be used to validate that our components work with AoT, and fix them if not.
1. Run `gulp e2e.prod` to bundle all e2e tests, or pass a folder for a specific test. For example, `gulp e2e.prod --f=alert/basic` will build the test in `src/components/alert/test/basic`.
2. Run `gulp e2e.watchProd` with a folder passed to watch a test. For example, `gulp e2e.watchProd --f=select/single-value` will watch the test in `src/components/select/test/single-value`.
3. Navigate to `http://localhost:8000/dist/e2e`
### Building & Running API Demos ### Building & Running API Demos
1. Run `gulp demos` or `gulp demos.watch` with a folder passed to watch for changes. 1. Run `gulp demos` or `gulp demos.watch` to watch for changes.
2. Navigate to `http://localhost:80808080/dist/demos` 2. Navigate to `http://localhost:8000/dist/demos`
### Building API Docs ### Building API Docs
@ -54,11 +65,22 @@ To remove the linked version of `ionic-angular` do `npm rm ionic-angular`, and t
### Running Snapshot ### Running Snapshot
#### Setup
1. Install [Protractor](https://angular.github.io/protractor/#/): `npm install -g protractor@2.5.1` 1. Install [Protractor](https://angular.github.io/protractor/#/): `npm install -g protractor@2.5.1`
2. Run `webdriver-manager update` 2. Run `webdriver-manager update`
3. Export `IONIC_SNAPSHOT_KEY` (get from someone) 3. Export `IONIC_SNAPSHOT_KEY` (get from someone)
4. Run `gulp snapshot`
#### Commands
- `gulp snapshot` will run the `gulp e2e.prod` task with AoT compilation.
- `gulp snapshot.skipBuild` will skip the `gulp e2e.prod` task with AoT compilation.
- `gulp snapshot.dev` will run a development build using the `gulp e2e` task.
- `gulp snapshot.quick` will skip the build and run snapshot without uploading to the server.
#### Flags
- `--f | -folder` will run the command with a test folder. For example, `gulp snapshot --f=action-sheet/basic` will run snapshot for the test at `src/components/action-sheet/test/basic`.
### Running Tests ### Running Tests

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<title>Ionic E2E</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="apple-mobile-web-app-capable" content="yes">
<link id="ionicLink" href="../../css/ionic.css" rel="stylesheet">
<link href="../../css/e2e.shared.css" rel="stylesheet">
<script>
var port;
if (location.href.indexOf('snapshot=true') > -1) {
port = 8876;
document.documentElement.classList.add('snapshot');
} else {
port = 8000;
document.documentElement.classList.remove('snapshot');
}
if (location.href.indexOf('cordova=true') > -1) {
window.cordova = {};
}
if (location.href.indexOf('rtl=true') > -1) {
document.dir = 'rtl';
} else {
document.dir = 'ltr';
}
if (location.href.indexOf('theme=dark') > -1) {
var link = document.getElementById('ionicLink');
link.setAttribute('href', link.getAttribute('href').replace('.css', '.dark.css'));
}
</script>
<script src="/node_modules/systemjs/dist/system.src.js"></script>
<script>
System.config({
map: {
'@angular/core': '/node_modules/@angular/core/bundles/core.umd.js',
'@angular/compiler': '/node_modules/@angular/compiler/bundles/compiler.umd.js',
'@angular/common': '/node_modules/@angular/common/bundles/common.umd.js',
'@angular/forms': '/node_modules/@angular/forms/bundles/forms.umd.js',
'@angular/http': '/node_modules/@angular/http/bundles/http.umd.js',
'@angular/platform-browser': '/node_modules/@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': '/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'ionic-angular': 'http://localhost:' + port,
'rxjs': '/node_modules/rxjs'
},
packages: {
'ionic-angular': {
main: 'index',
defaultExtension: false
},
'rxjs': {
defaultExtension: 'js'
}
}
});
</script>
<script src="../../polyfills/polyfills.ng.js"></script>
<script src="../../../bundles/ionic.system.js"></script>
</head>
<body>
<ion-app></ion-app>
<script>
System.import('@angular/platform-browser-dynamic').then(function(platformBrowserDynamic) {
System.import('app-module.js').then(function(appModule) {
platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(appModule.AppModule);
}, console.error.bind(console));
}, console.error.bind(console));
</script>
</body>
</html>

View File

@ -1,7 +1,7 @@
describe('<%= relativePath %>: <%= platform %>', function() { describe('<%= relativePath %>: <%= platform %>', function() {
it('should init', function() { it('should init', function() {
browser.get('http://localhost:<%= buildConfig.protractorPort %>/dist/e2e/components/<%= relativePath %>/index.html?ionicplatform=<%= platform %>&ionicOverlayCreatedDiff=0&ionicanimate=false&snapshot=true'); browser.get('http://localhost:<%= buildConfig.protractorPort %>/dist/e2e/<%= relativePath %>/index.html?ionicplatform=<%= platform %>&ionicOverlayCreatedDiff=0&ionicanimate=false&snapshot=true');
}); });
<%= contents %> <%= contents %>

View File

@ -45,4 +45,4 @@ export const NPM_VENDOR_FILES = [
// SERVER // SERVER
export const LOCAL_SERVER_PORT = 8080; export const LOCAL_SERVER_PORT = 8000;

View File

@ -2,18 +2,25 @@ declare module 'conventional-changelog';
declare module 'dgeni'; declare module 'dgeni';
declare module 'event-stream'; declare module 'event-stream';
declare module 'github'; declare module 'github';
declare module 'gulp-babel';
declare module 'gulp-cached';
declare module 'gulp-concat';
declare module 'gulp-connect'; declare module 'gulp-connect';
declare module 'gulp-if'; declare module 'gulp-if';
declare module 'gulp-open'; declare module 'gulp-remember';
declare module 'gulp-rename';
declare module 'gulp-scss-lint'; declare module 'gulp-scss-lint';
declare module 'gulp-server-livereload'; declare module 'gulp-server-livereload';
declare module 'gulp-tslint'; declare module 'gulp-tslint';
declare module 'gulp-typescript';
declare module 'html-entities'; declare module 'html-entities';
declare module 'path';
declare module 'rollup'; declare module 'rollup';
declare module 'rollup-plugin-commonjs'; declare module 'rollup-plugin-commonjs';
declare module 'rollup-plugin-multi-entry'; declare module 'rollup-plugin-multi-entry';
declare module 'rollup-plugin-node-resolve'; declare module 'rollup-plugin-node-resolve';
declare module 'rollup-plugin-uglify'; declare module 'rollup-plugin-uglify';
declare module 'through2';
declare module 'semver'; declare module 'semver';
declare module 'vinyl'; declare module 'vinyl';
declare module 'yargs'; declare module 'yargs';

View File

@ -4,6 +4,8 @@ import './tasks/default';
import './tasks/demos'; import './tasks/demos';
import './tasks/docs'; import './tasks/docs';
import './tasks/e2e'; import './tasks/e2e';
import './tasks/e2e.dev';
import './tasks/e2e.prod';
import './tasks/lint'; import './tasks/lint';
import './tasks/release'; import './tasks/release';
import './tasks/snapshot'; import './tasks/snapshot';

View File

@ -0,0 +1,226 @@
import { readFileSync } from 'fs';
import { dirname, join, sep } from 'path';
import { dest, src, start, task } from 'gulp';
import * as babel from 'gulp-babel';
import * as cache from 'gulp-cached';
import * as concat from 'gulp-concat';
import * as connect from 'gulp-connect';
import * as gulpif from 'gulp-if';
import * as remember from 'gulp-remember';
import * as rename from 'gulp-rename';
import * as tsc from 'gulp-typescript';
import * as watch from 'gulp-watch';
import { template } from 'lodash';
import * as merge from 'merge2';
import * as runSequence from 'run-sequence';
import { obj } from 'through2';
import * as VinylFile from 'vinyl';
import { DIST_E2E_ROOT, DIST_NAME, E2E_NAME, ES5, ES_2015, SCRIPTS_ROOT } from '../constants';
const buildConfig = require('../../build/config');
/**
* Builds Ionic e2e tests to dist/e2e and creates the necessary files for tests
* to run.
*/
task('e2e', e2eBuild);
function e2eBuild(done: (err: any) => void) {
runSequence(
'e2e.clean',
'e2e.build',
'e2e.polyfill',
'e2e.copyExternalDependencies',
'e2e.sass',
'e2e.fonts',
'e2e.bundle',
done);
}
/**
* Builds Ionic e2e tests to dist/e2e.
*/
task('e2e.build', function () {
var indexTemplate = template(
readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/e2e.template.dev.html`).toString()
)({
buildConfig: buildConfig
});
// Get each test folder with src
var tsResult = src([
'src/components/*/test/*/**/*.ts',
'!src/components/*/test/*/**/*.spec.ts'
])
.pipe(cache('e2e.ts'))
.pipe(tsc(getTscOptions(), undefined, tscReporter))
.on('error', function (error) {
console.log(error.message);
})
.pipe(gulpif(/app-module.js$/, createIndexHTML()))
.pipe(gulpif(/e2e.js$/, createPlatformTests()));
var testFiles = src([
'src/components/*/test/*/**/*',
'!src/components/*/test/*/**/*.ts'
])
.pipe(cache('e2e.files'));
return merge([
tsResult,
testFiles
])
.pipe(rename(function (file) {
file.dirname = file.dirname.replace(sep + 'test' + sep, sep);
}))
.pipe(dest(DIST_E2E_ROOT))
.pipe(connect.reload());
function createIndexHTML() {
return obj(function (file, enc, next) {
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTemplate),
path: join(dirname(file.path), 'index.html'),
}));
next(null, file);
});
}
function createPlatformTests() {
let platforms = [
'android',
'ios',
'windows'
];
let testTemplate = template(readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/e2e.template.js`).toString());
return obj(function (file, enc, next) {
let self = this;
let relativePath = dirname(file.path.replace(/^.*?src(\/|\\)components(\/|\\)/, ''));
relativePath = relativePath.replace('/test/', '/');
let contents = file.contents.toString();
platforms.forEach(function (platform) {
let platformContents = testTemplate({
contents: contents,
buildConfig: buildConfig,
relativePath: relativePath,
platform: platform
});
self.push(new VinylFile({
base: file.base,
contents: new Buffer(platformContents),
path: file.path.replace(/e2e.js$/, platform + '.e2e.js')
}));
});
next();
});
}
});
/**
* Creates SystemJS bundle from Ionic source files.
*/
task('e2e.bundle', function () {
var tsResult = tsCompile(getTscOptions('es6'), 'system')
.pipe(babel(babelOptions));
var swiper = src('src/components/slides/swiper-widget.system.js');
return merge([tsResult, swiper])
.pipe(remember('system'))
.pipe(concat('ionic.system.js'))
.pipe(dest(`${DIST_NAME}/bundles`))
.pipe(connect.reload());
});
function tsCompile(options, cacheName) {
return src([
'typings/main.d.ts',
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/components/*/test/**/*',
'!src/util/test/*',
'!src/config/test/*',
'!src/platform/test/*',
'!src/**/*.spec.ts'
])
.pipe(cache(cacheName, { optimizeMemory: true }))
.pipe(tsc(options, undefined, tscReporter));
}
function getTscOptions(name?: string) {
var opts = {
emitDecoratorMetadata: true,
experimentalDecorators: true,
target: ES5,
module: 'commonjs',
isolatedModules: true,
typescript: require('typescript'),
declaration: false
};
if (name === 'typecheck') {
opts.declaration = true;
delete opts.isolatedModules;
} else if (name === 'es6') {
opts.target = 'es6';
delete opts.module;
}
return opts;
}
var tscReporter = {
error: function (error) {
console.error(error.message);
}
};
// We use Babel to easily create named System.register modules
// See: https://github.com/Microsoft/TypeScript/issues/4801
// and https://github.com/ivogabe/gulp-typescript/issues/211
const babelOptions = {
moduleIds: true,
getModuleId: function (name) {
return 'ionic-angular/' + name;
},
plugins: ['transform-es2015-modules-systemjs'],
presets: [ES_2015]
};
/**
* Builds e2e tests to dist/e2e and watches for changes. Runs 'e2e.bundle' or
* 'sass' on Ionic source changes and 'e2e.build' for e2e test changes.
*/
task('e2e.watch', ['e2e'], function () {
watchTask('e2e.bundle');
watch('src/components/*/test/**/*', function (file) {
start('e2e.build');
});
});
function watchTask(task) {
watch([
'src/**/*.ts',
'!src/components/*/test/**/*',
'!src/util/test/*'
],
function (file) {
if (file.event !== 'unlink') {
start(task);
}
}
);
watch('src/**/*.scss', function () {
start('e2e.sass');
});
start('e2e.serve');
}

View File

@ -0,0 +1,279 @@
import { accessSync, F_OK, readFileSync, writeFileSync } from 'fs';
import { dirname, join } from 'path';
import * as glob from 'glob';
import { dest, src, start, task } from 'gulp';
import * as gulpif from 'gulp-if';
import * as watch from 'gulp-watch';
import { template } from 'lodash';
import * as rollup from 'rollup';
import * as nodeResolve from 'rollup-plugin-node-resolve';
import * as commonjs from 'rollup-plugin-commonjs';
import * as runSequence from 'run-sequence';
import { obj } from 'through2';
import * as VinylFile from 'vinyl';
import { argv } from 'yargs';
import { DIST_E2E_COMPONENTS_ROOT, DIST_E2E_ROOT, DIST_NAME, E2E_NAME, ES5, ES_2015, LOCAL_SERVER_PORT, PROJECT_ROOT, SCRIPTS_ROOT, SRC_COMPONENTS_ROOT, SRC_ROOT } from '../constants';
import { createTempTsConfig, deleteFiles, runNgc } from '../util';
task('e2e.prod', e2eBuild);
function e2eBuild(done: (err: any) => void) {
runSequence(
'e2e.clean',
'e2e.polyfill',
'e2e.copySource',
'e2e.compileTests',
'e2e.copyExternalDependencies',
'e2e.sass',
'e2e.fonts',
'e2e.bundleProd',
done);
}
task('e2e.copySource', (done: Function) => {
const buildConfig = require('../../build/config');
const stream = src([`${SRC_ROOT}/**/*`, `!${SRC_ROOT}/**/*.spec.ts`])
.pipe(gulpif(/app-module.ts$/, createIndexHTML()))
.pipe(gulpif(/e2e.ts$/, createPlatformTests()))
.pipe(dest(DIST_E2E_ROOT));
stream.on('end', done);
function createIndexHTML() {
const indexTemplate = readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/e2e.template.prod.html`);
const indexTs = readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/entry.ts`);
return obj(function (file, enc, next) {
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTemplate),
path: join(dirname(file.path), 'index.html'),
}));
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTs),
path: join(dirname(file.path), 'entry.ts'),
}));
next(null, file);
});
}
// TODO this is almost the same as dev, diff and combine
function createPlatformTests() {
let platforms = [
'android',
'ios',
'windows'
];
let testTemplate = template(readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/e2e.template.js`).toString());
return obj(function (file, enc, next) {
let self = this;
let relativePath = dirname(file.path.replace(/^.*?src(\/|\\)/, ''));
let contents = file.contents.toString();
platforms.forEach(function (platform) {
let platformContents = testTemplate({
contents: contents,
buildConfig: buildConfig,
relativePath: relativePath,
platform: platform
});
self.push(new VinylFile({
base: file.base,
contents: new Buffer(platformContents),
path: file.path.replace(/e2e.ts$/, platform + '.e2e.js')
}));
});
next();
});
}
});
task('e2e.compileTests', (done: Function) => {
let folderInfo = getFolderInfo();
buildE2ETests(folderInfo, done);
});
function buildE2ETests(folderInfo: any, done: Function) {
let includeGlob = ['./components/*/test/*/app-module.ts', './components/*/test/*/entry.ts'];
if (folderInfo.componentName && folderInfo.componentTest) {
includeGlob = [
`./components/${folderInfo.componentName}/test/${folderInfo.componentTest}/app-module.ts`,
`./components/${folderInfo.componentName}/test/${folderInfo.componentTest}/entry.ts`,
];
}
createTempTsConfig(includeGlob, ES5, ES_2015, `${DIST_E2E_ROOT}/tsconfig.json`);
runNgc(`${DIST_E2E_ROOT}/tsconfig.json`, (err) => {
if (err) {
done(err);
return;
}
// clean up any .ts files that remain
deleteFiles([`${DIST_E2E_ROOT}/**/*.ts`, `!${DIST_E2E_ROOT}/**/*.ngfactory.ts`, `!${DIST_E2E_ROOT}/**/*.d.ts`], done);
});
}
task('e2e.bundleProd', (done) => {
let includeGlob = `${DIST_E2E_ROOT}/components/*/test/*/entry.js`;
let folderInfo = getFolderInfo();
if (folderInfo.componentName && folderInfo.componentTest) {
includeGlob = `${DIST_E2E_ROOT}/components/${folderInfo.componentName}/test/${folderInfo.componentTest}/entry.js`;
}
glob(includeGlob, {}, function (er, files) {
var directories = files.map(function (file) {
return dirname(file);
});
let indexFileContents = directories.map(function (dir) {
let testName = dir.replace(`${DIST_E2E_ROOT}/components/`, '');
let fileName = dir.replace(`${PROJECT_ROOT}`, '');
return `<p><a href="${fileName}/index.html">${testName}</a></p>`;
}, []);
writeFileSync(`${DIST_E2E_ROOT}/index.html`,
'<!DOCTYPE html><html lang="en"><head></head><body style="width: 500px; margin: 100px auto">\n' +
indexFileContents.join('\n') +
'</center></body></html>'
);
createBundles(files).then(() => {
done();
}).catch(err => {
done(err);
});
});
});
function createBundles(files: string[]) {
let start;
if (!files) {
return Promise.reject(new Error('list of files is null'));
} else if (files.length === 0) {
return Promise.resolve();
} else {
const outputFileName = join(dirname(files[0]), 'app.bundle.js');
start = Date.now();
return bundle(files[0], outputFileName).then(() => {
const end = Date.now();
const seconds = (end - start) / 1000;
console.log(`Took ${seconds} seconds to process ${files[0]}`);
const remainingFiles = files.concat();
remainingFiles.shift();
return createBundles(remainingFiles);
}).catch(err => {
return Promise.reject(err);
});
}
}
function bundle(inputFile: string, outputFile: string): Promise<any> {
console.log(`Starting rollup on ${inputFile} ... writing to ${outputFile}`);
return rollup.rollup({
entry: inputFile,
plugins: [
commonjs(),
nodeResolve({
module: true,
jsnext: true,
main: true,
extensions: ['.js']
})
]
}).then(bundle => {
return bundle.write({
format: 'iife',
dest: outputFile,
});
});
}
task('e2e.watchProd', ['e2e.copyExternalDependencies', 'e2e.sass', 'e2e.fonts'], (done: Function) => {
const folderInfo = getFolderInfo();
let e2eTestPath = SRC_COMPONENTS_ROOT;
if (folderInfo.componentName && folderInfo.componentTest) {
e2eTestPath = join(SRC_COMPONENTS_ROOT, folderInfo.componentName, 'test', folderInfo.componentTest, 'app-module.ts');
}
try {
accessSync(e2eTestPath, F_OK);
} catch (e) {
done(new Error(`Could not find e2e test: ${e2eTestPath}`));
return;
}
if (e2eComponentsExists()) {
// already generated the e2e directory
e2eWatch(folderInfo.componentName, folderInfo.componentTest);
} else {
// generate the e2e directory
console.log('Generated e2e builds first...');
e2eBuild(() => {
e2eWatch(folderInfo.componentName, folderInfo.componentTest);
});
}
});
function e2eWatch(componentName: string, componentTest: string) {
// If any tests change within components then run e2e.resources.
watch([
'src/components/*/test/**/*'
],
function (file) {
console.log('start e2e.resources - ' + JSON.stringify(file.history, null, 2));
start('e2e.copyAndCompile');
});
// If any src files change except for tests then transpile only the source ionic files
watch([
'src/**/*.ts',
'!src/components/*/test/**/*',
'!src/util/test/*'
],
function (file) {
console.log('start e2e.ngcSource - ' + JSON.stringify(file.history, null, 2));
start('e2e.copyAndCompile');
});
// If any scss files change then recompile all sass
watch(['src/**/*.scss'], (file) => {
console.log('start sass - ' + JSON.stringify(file.history, null, 2));
start('e2e.sass');
});
console.log(`http://localhost:${LOCAL_SERVER_PORT}/${DIST_NAME}/${E2E_NAME}/components/${componentName}/test/${componentTest}/`);
start('e2e.serve');
}
function e2eComponentsExists(): boolean {
try {
accessSync(DIST_E2E_COMPONENTS_ROOT, F_OK);
} catch (e) {
return false;
}
return true;
}
function getFolderInfo() {
let componentName: string = null;
let componentTest: string = null;
const folder: string = argv.folder || argv.f;
if (folder && folder.length) {
const folderSplit = folder.split('/');
componentName = folderSplit[0];
componentTest = (folderSplit.length > 1 ? folderSplit[1] : 'basic');
}
return {
componentName: componentName,
componentTest: componentTest
};
}

View File

@ -1,32 +1,11 @@
import { accessSync, F_OK, readFileSync, writeFileSync } from 'fs'; import { dest, src, task } from 'gulp';
import { dirname, join } from 'path';
import * as glob from 'glob';
import {dest, src, start, task} from 'gulp';
import * as connect from 'gulp-connect'; import * as connect from 'gulp-connect';
import * as gulpif from 'gulp-if';
import * as open from 'gulp-open';
import * as watch from 'gulp-watch';
import { template } from 'lodash';
import * as rollup from 'rollup';
import * as nodeResolve from 'rollup-plugin-node-resolve';
import * as commonjs from 'rollup-plugin-commonjs';
import * as del from 'del'; import * as del from 'del';
import * as runSequence from 'run-sequence'; import * as runSequence from 'run-sequence';
import { obj } from 'through2';
import * as VinylFile from 'vinyl';
import { argv } from 'yargs';
import { DIST_E2E_COMPONENTS_ROOT, DIST_E2E_ROOT, DIST_NAME, E2E_NAME, ES5, ES_2015, LOCAL_SERVER_PORT, PROJECT_ROOT, SCRIPTS_ROOT, SRC_COMPONENTS_ROOT, SRC_ROOT } from '../constants'; import { DIST_E2E_ROOT, LOCAL_SERVER_PORT, SCRIPTS_ROOT } from '../constants';
import { compileSass, copyFonts, createTempTsConfig, createTimestamp, deleteFiles, runNgc, setSassIonicVersion, writePolyfills } from '../util'; import { compileSass, copyFonts, createTimestamp, setSassIonicVersion, writePolyfills } from '../util';
task('e2e', e2eBuild);
function e2eBuild(done: (err: any) => void) {
runSequence('e2e.polyfill', 'e2e.copySource', 'e2e.compileTests', 'e2e.copyExternalDependencies', 'e2e.sass', 'e2e.fonts', 'e2e.bundle', done);
}
// TODO this should run when building all of e2e, not when folder passed
task('e2e.clean', (done: Function) => { task('e2e.clean', (done: Function) => {
del(['dist/e2e/**']).then(() => { del(['dist/e2e/**']).then(() => {
done(); done();
@ -44,110 +23,13 @@ task('e2e.polyfill', (done: Function) => {
}); });
task('e2e.copyAndCompile', (done: (err: any) => void) => { task('e2e.copyAndCompile', (done: (err: any) => void) => {
runSequence('e2e.copySource', 'e2e.compileTests', 'e2e.bundle', done); runSequence(
'e2e.copySource',
'e2e.compileTests',
'e2e.bundle',
done);
}); });
task('e2e.copySource', (done: Function) => {
const buildConfig = require('../../build/config');
const stream = src([`${SRC_ROOT}/**/*`, `!${SRC_ROOT}/**/*.spec.ts`])
.pipe(gulpif(/app-module.ts$/, createIndexHTML()))
.pipe(gulpif(/e2e.ts$/, createPlatformTests()))
.pipe(dest(DIST_E2E_ROOT));
stream.on('end', done);
function createIndexHTML() {
const indexTemplate = readFileSync('scripts/e2e/index.html');
const indexTs = readFileSync('scripts/e2e/entry.ts');
return obj(function(file, enc, next) {
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTemplate),
path: join(dirname(file.path), 'index.html'),
}));
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTs),
path: join(dirname(file.path), 'entry.ts'),
}));
next(null, file);
});
}
function createPlatformTests() {
let platforms = [
'android',
'ios',
'windows'
];
let testTemplate = template(readFileSync('scripts/e2e/e2e.template.js').toString());
return obj(function(file, enc, next) {
let self = this;
let relativePath = dirname(file.path.replace(/^.*?src(\/|\\)components(\/|\\)/, ''));
let contents = file.contents.toString();
platforms.forEach(function(platform) {
let platformContents = testTemplate({
contents: contents,
buildConfig: buildConfig,
relativePath: relativePath,
platform: platform
});
self.push(new VinylFile({
base: file.base,
contents: new Buffer(platformContents),
path: file.path.replace(/e2e.ts$/, platform + '.e2e.js')
}));
});
next();
});
}
});
task('e2e.compileTests', (done: Function) => {
let folderInfo = getFolderInfo();
buildE2ETests(folderInfo, done);
});
function buildE2ETests(folderInfo: any, done: Function) {
let includeGlob = ['./components/*/test/*/app-module.ts', './components/*/test/*/entry.ts'];
if (folderInfo.componentName && folderInfo.componentTest) {
includeGlob = [
`./components/${folderInfo.componentName}/test/${folderInfo.componentTest}/app-module.ts`,
`./components/${folderInfo.componentName}/test/${folderInfo.componentTest}/entry.ts`,
];
}
createTempTsConfig(includeGlob, ES5, ES_2015, `${DIST_E2E_ROOT}/tsconfig.json`);
runNgc(`${DIST_E2E_ROOT}/tsconfig.json`, (err) => {
if (err) {
done(err);
return;
}
// clean up any .ts files that remain
deleteFiles([`${DIST_E2E_ROOT}/**/*.ts`, `!${DIST_E2E_ROOT}/**/*.ngfactory.ts`, `!${DIST_E2E_ROOT}/**/*.d.ts`], done);
});
}
function getFolderInfo() {
let componentName: string = null;
let componentTest: string = null;
const folder: string = argv.folder || argv.f;
if (folder && folder.length) {
const folderSplit = folder.split('/');
componentName = folderSplit[0];
componentTest = (folderSplit.length > 1 ? folderSplit[1] : 'basic');
}
return {
componentName: componentName,
componentTest: componentTest
};
}
task('e2e.copyExternalDependencies', () => { task('e2e.copyExternalDependencies', () => {
src([`${SCRIPTS_ROOT}/e2e/*.css`]).pipe(dest(`${DIST_E2E_ROOT}/css`)); src([`${SCRIPTS_ROOT}/e2e/*.css`]).pipe(dest(`${DIST_E2E_ROOT}/css`));
}); });
@ -162,154 +44,12 @@ task('e2e.fonts', () => {
return copyFonts(`${DIST_E2E_ROOT}/fonts`); return copyFonts(`${DIST_E2E_ROOT}/fonts`);
}); });
task('e2e.bundle', (done) => { task('e2e.serve', function() {
let includeGlob = `${DIST_E2E_ROOT}/components/*/test/*/entry.js`;
let folderInfo = getFolderInfo();
if (folderInfo.componentName && folderInfo.componentTest) {
includeGlob = `${DIST_E2E_ROOT}/components/${folderInfo.componentName}/test/${folderInfo.componentTest}/entry.js`;
}
glob(includeGlob, {}, function(er, files) {
var directories = files.map(function(file) {
return dirname(file);
});
let indexFileContents = directories.map(function(dir) {
let testName = dir.replace(`${DIST_E2E_ROOT}/components/`, '');
let fileName = dir.replace(`${PROJECT_ROOT}`, '');
return `<p><a href="${fileName}/index.html">${testName}</a></p>`;
}, []);
writeFileSync(`${DIST_E2E_ROOT}/index.html`,
'<!DOCTYPE html><html lang="en"><head></head><body style="width: 500px; margin: 100px auto">\n' +
indexFileContents.join('\n') +
'</center></body></html>'
);
createBundles(files).then(() => {
done();
}).catch(err => {
done(err);
});
});
});
function createBundles(files: string[]) {
let start;
if (!files) {
return Promise.reject(new Error('list of files is null'));
} else if ( files.length === 0) {
return Promise.resolve();
} else {
const outputFileName = join(dirname(files[0]), 'app.bundle.js');
start = Date.now();
return bundle(files[0], outputFileName).then(() => {
const end = Date.now();
const seconds = (end - start) / 1000;
console.log(`Took ${seconds} seconds to process ${files[0]}`);
const remainingFiles = files.concat();
remainingFiles.shift();
return createBundles(remainingFiles);
}).catch(err => {
return Promise.reject(err);
});
}
}
function bundle(inputFile: string, outputFile: string): Promise<any> {
console.log(`Starting rollup on ${inputFile} ... writing to ${outputFile}`);
return rollup.rollup({
entry: inputFile,
plugins: [
commonjs(),
nodeResolve({
module: true,
jsnext: true,
main: true,
extensions: ['.js']
})
]
}).then(bundle => {
return bundle.write({
format: 'iife',
dest: outputFile,
});
});
}
task('e2e.watch', ['e2e.copyExternalDependencies', 'e2e.sass', 'e2e.fonts'], (done: Function) => {
const folderInfo = getFolderInfo();
if (! folderInfo.componentName || ! folderInfo.componentTest) {
done(new Error('Passing in a folder to watch is required for this command. Use the --folder or -f option.'));
return;
}
const e2eTestPath = join(SRC_COMPONENTS_ROOT, folderInfo.componentName, 'test', folderInfo.componentTest, 'app-module.ts');
try {
accessSync(e2eTestPath, F_OK);
} catch (e) {
done(new Error(`Could not find e2e test: ${e2eTestPath}`));
return;
}
if (e2eComponentsExists()) {
// already generated the e2e directory
e2eWatch(folderInfo.componentName, folderInfo.componentTest);
} else {
// generate the e2e directory
console.log('Generated e2e builds first...');
e2eBuild(() => {
e2eWatch(folderInfo.componentName, folderInfo.componentTest);
});
}
});
function e2eWatch(componentName: string, componentTest: string) {
// If any tests change within components then run e2e.resources.
watch([
'src/components/*/test/**/*'
],
function(file) {
console.log('start e2e.resources - ' + JSON.stringify(file.history, null, 2));
start('e2e.copyAndCompile');
});
// If any src files change except for tests then transpile only the source ionic files
watch([
'src/**/*.ts',
'!src/components/*/test/**/*',
'!src/util/test/*'
],
function(file) {
console.log('start e2e.ngcSource - ' + JSON.stringify(file.history, null, 2));
start('e2e.copyAndCompile');
});
// If any scss files change then recompile all sass
watch(['src/**/*.scss'], (file) => {
console.log('start sass - ' + JSON.stringify(file.history, null, 2));
start('e2e.sass');
});
console.log(`http://localhost:${LOCAL_SERVER_PORT}/${DIST_NAME}/${E2E_NAME}/components/${componentName}/test/${componentTest}/`);
connect.server({ connect.server({
root: './', root: './',
port: LOCAL_SERVER_PORT, port: LOCAL_SERVER_PORT,
livereload: true livereload: {
port: 35700
}
}); });
});
src('dist').pipe(
open({uri: `http://localhost:${LOCAL_SERVER_PORT}/${DIST_NAME}/${E2E_NAME}`})
);
}
function e2eComponentsExists(): boolean {
try {
accessSync(DIST_E2E_COMPONENTS_ROOT, F_OK);
} catch (e) {
return false;
}
return true;
}

View File

@ -7,23 +7,27 @@ import { task } from 'gulp';
import * as serveStatic from 'serve-static'; import * as serveStatic from 'serve-static';
import { argv } from 'yargs'; import { argv } from 'yargs';
import { DIST_E2E_COMPONENTS_ROOT, PROJECT_ROOT, SCRIPTS_ROOT } from '../constants'; import { DIST_E2E_ROOT, DIST_E2E_COMPONENTS_ROOT, PROJECT_ROOT, SCRIPTS_ROOT } from '../constants';
import { mergeObjects } from '../util'; import { mergeObjects } from '../util';
task('snapshot', ['e2e'], (done: Function) => { task('snapshot', ['e2e.clean', 'e2e.prod'], (done: Function) => {
snapshot(false, done); snapshot(false, false, done);
}); });
task('snapshot.skipBuild', ['e2e.sass'], (done: Function) => { task('snapshot.skipBuild', ['e2e.sass'], (done: Function) => {
snapshot(false, done); snapshot(false, false, done);
});
task('snapshot.dev', ['e2e.clean', 'e2e'], (done: Function) => {
snapshot(false, true, done);
}); });
task('snapshot.quick', ['e2e.sass'], (done: Function) => { task('snapshot.quick', ['e2e.sass'], (done: Function) => {
snapshot(true, done); snapshot(true, true, done);
}); });
function snapshot(quickMode: boolean, callback: Function) { function snapshot(quickMode: boolean, devMode: boolean, callback: Function) {
const snapshotConfig = require('../../snapshot/snapshot.config').config; const snapshotConfig = require('../../snapshot/snapshot.config').config;
const protractorConfigFile = resolve(SCRIPTS_ROOT, 'snapshot/protractor.config.js'); const protractorConfigFile = resolve(SCRIPTS_ROOT, 'snapshot/protractor.config.js');
@ -45,7 +49,10 @@ function snapshot(quickMode: boolean, callback: Function) {
e2eSpecs = folderArgPaths[1]; e2eSpecs = folderArgPaths[1];
} }
} }
const specs = join(DIST_E2E_COMPONENTS_ROOT, component, 'test', e2eSpecs, '*e2e.js'); var specs = join(DIST_E2E_COMPONENTS_ROOT, component, 'test', e2eSpecs, '*e2e.js');
if (devMode) specs = join(DIST_E2E_ROOT, component, e2eSpecs, '*e2e.js');
console.log('[snapshot] Running with', devMode ? 'Development' : 'Production', 'build');
console.log(`[snapshot] Specs: ${specs}`); console.log(`[snapshot] Specs: ${specs}`);
const testId = generateTestId(); const testId = generateTestId();
@ -64,6 +71,7 @@ function snapshot(quickMode: boolean, callback: Function) {
'--params.height=' + snapshotValues.params.height, '--params.height=' + snapshotValues.params.height,
'--params.test_id=' + snapshotValues.params.test_id, '--params.test_id=' + snapshotValues.params.test_id,
'--params.upload=' + snapshotValues.params.upload, '--params.upload=' + snapshotValues.params.upload,
'--params.dev=' + devMode,
'--specs=' + specs '--specs=' + specs
]; ];

View File

@ -15,7 +15,8 @@ var IonicSnapshot = function(options) {
self.domain = options.domain || 'ionic-snapshot-go.appspot.com'; self.domain = options.domain || 'ionic-snapshot-go.appspot.com';
self.groupId = options.groupId || 'test_group'; self.groupId = options.groupId || 'test_group';
self.appId = options.appId || 'test_app'; self.appId = options.appId || 'test_app';
self.sleepBetweenSpecs = options.sleepBetweenSpecs || 500; self.build = (browser.params.dev === 'true') ? 'Development' : 'Production';
self.sleepBetweenSpecs = self.build === 'Development' ? 3000 : (options.sleepBetweenSpecs || 500);
self.testId = browser.params.test_id || 'test_id'; self.testId = browser.params.test_id || 'test_id';
self.shouldUpload = browser.params.upload !== 'false'; self.shouldUpload = browser.params.upload !== 'false';
self.platformId = browser.params.platform_id; self.platformId = browser.params.platform_id;
@ -83,7 +84,7 @@ var IonicSnapshot = function(options) {
} }
}); });
log(colors.green('Start Snapshot:'), log(colors.green('Start ' + self.build + ' Snapshot - ' + self.sleepBetweenSpecs + ' between Specs:'),
self.groupId, self.appId, self.platformId, self.testId, '(' + self.width + 'x' + self.height + ')'); self.groupId, self.appId, self.platformId, self.testId, '(' + self.width + 'x' + self.height + ')');
}; };
@ -114,10 +115,9 @@ var IonicSnapshot = function(options) {
var specIdString = '[' + (spec.id+1) + '/' + self.testData.total_specs + ']'; var specIdString = '[' + (spec.id+1) + '/' + self.testData.total_specs + ']';
self.testData.spec_index = spec.id; self.testData.spec_index = spec.id;
self.testData.description = spec.getFullName().replace('/test/', '/');
self.testData.highest_mismatch = self.highestMismatch; self.testData.highest_mismatch = self.highestMismatch;
self.testData.png_base64 = pngBase64; self.testData.png_base64 = pngBase64;
self.testData.description = spec.getFullName().replace('test/', ''); self.testData.description = spec.getFullName().replace('components/', '').replace('test/', '');
self.testData.url = currentUrl.replace('dist', '').replace('components/', '').replace('test/', '').replace('&ionicanimate=false', ''); self.testData.url = currentUrl.replace('dist', '').replace('components/', '').replace('test/', '').replace('&ionicanimate=false', '');
pngBase64 = null; pngBase64 = null;