diff --git a/gulpfile.js b/gulpfile.js index 3e401a9e02..2086dff30a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -20,6 +20,7 @@ var sass = require('gulp-sass'); var shell = require('gulp-shell'); var traceur = require('gulp-traceur'); var wrap = require('gulp-wrap'); +var argv = require('yargs').argv; gulp.task('default', ['js', 'html', 'sass', 'libs', 'playgroundJs', 'playgroundFiles']); @@ -47,10 +48,6 @@ gulp.task('karma-watch', function() { return karma.start({ configFile: __dirname + '/scripts/test/karma-watch.conf.js' }); }); -gulp.task('sass-watch', ['sass'], function () { - gulp.watch('src/**/*.scss', ['sass']); -}); - gulp.task('sass', function(done) { gulp.src('src/components/app/ionic.scss') .pipe(sass({ @@ -137,12 +134,18 @@ gulp.task('angular2', function () { }); gulp.task('examples', ['sass'], function() { - return gulp.src('src/components/**/examples/**/*') + var examplesSrc = path.join(__dirname, 'src/components/**/examples/**/*'); + var templateSrc = path.join(__dirname, 'scripts/examples/index.template.html'); + var examplesDest = path.join(__dirname, 'dist/examples/'); + + return gulp.src(examplesSrc) .pipe(gulpif(/index.html/, wrap({ - src: 'scripts/examples/index.template.html' + src: templateSrc }))) .pipe(rename(function(file) { file.dirname = file.dirname.replace('/examples/', '/'); })) - .pipe(gulp.dest('dist/examples/')); + .pipe(gulp.dest(examplesDest)); }); + +require('./scripts/snapshot/snapshot.task')(gulp, argv, buildConfig); diff --git a/package.json b/package.json index 4fa77c2961..74c28af288 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "ionic2", "version": "0.0.0", "devDependencies": { + "canonical-path": "0.0.2", "connect": "^3.3.4", "del": "~1.1.1", "gulp": "~3.8.10", @@ -16,11 +17,18 @@ "gulp-sass": "^1.3.3", "gulp-shell": "^0.4.0", "gulp-traceur": "0.16.*", + "gulp-util": "^3.0.4", "gulp-wrap": "^0.11.0", "karma": "^0.12.31", + "lodash": "^2.4.1", + "lodash.template": "^2.4.1", + "node-uuid": "^1.4.1", + "q": "^1.2.0", + "request": "^2.53.0", "serve-static": "~1.8.1", "systemjs": "^0.11.3", - "through2": "~0.6.3" + "through2": "~0.6.3", + "yargs": "^3.6.0" }, "dependencies": { "angular2": "2.0.0-alpha.13", diff --git a/scripts/build/config.js b/scripts/build/config.js index 639c2bc9a7..b2508fcb96 100644 --- a/scripts/build/config.js +++ b/scripts/build/config.js @@ -15,5 +15,6 @@ module.exports = { 'node_modules/systemjs/lib/extension-register.js', 'node_modules/angular2/node_modules/zone.js/zone.js', 'node_modules/hammerjs/hammer.js' - ] + ], + protractorPort: 8876 }; diff --git a/scripts/examples/index.template.html b/scripts/examples/index.template.html index cd4c6ef815..5337ec7079 100644 --- a/scripts/examples/index.template.html +++ b/scripts/examples/index.template.html @@ -4,7 +4,7 @@ - + diff --git a/scripts/snapshot/ionic.snapshot.js b/scripts/snapshot/ionic.snapshot.js new file mode 100644 index 0000000000..fb5ccb1147 --- /dev/null +++ b/scripts/snapshot/ionic.snapshot.js @@ -0,0 +1,140 @@ +var q = require('q'); + +var IonicSnapshot = function(options) { + + // modules + var _ = require('lodash'); + var request = require('request'); + var colors = require('gulp-util').colors; + var log = console.log.bind(console, '[' + colors.cyan('IonicReporter') + ']'); + + var IonicReporter = function(options) { + var self = this; + + // set options and defaults + self.domain = options.domain || 'ionic-snapshot-go.appspot.com'; + self.groupId = options.groupId || 'test_group'; + self.appId = options.appId || 'test_app'; + self.testId = browser.params.test_id || 'test_id'; + self.platformId = browser.params.platform_id; + self.platformIndex = browser.params.platform_index; + self.platformCount = browser.params.platform_count; + self.sleepBetweenSpecs = options.sleepBetweenSpecs || 400; + self.width = browser.params.width || -1; + self.height = browser.params.height || -1; + self.highestMismatch = 0; + self.screenshotRequestPromises = []; + + self.flow = protractor.promise.controlFlow(); + + if(self.width > 0 && self.height > 0) { + self.flow.execute(function(){ + return browser.driver.manage().window().setSize(self.width, self.height); + }); + } + self.flow.execute(function(){ + return browser.getCapabilities().then(function (capabilities) { + self.testData = { + group_id: self.groupId, + app_id: self.appId, + test_id: self.testId, + platform_id: self.platformId, + platform_index: self.platformIndex, + platform_count: self.platformCount, + width: self.width, + height: self.height, + browser: capabilities.get('browserName'), + platform: capabilities.get('platform'), + version: capabilities.get('version'), + access_key: options.accessKey + }; + }); + }); + process.on('exit', function() { + log(colors.green('Highest Mismatch:'), self.highestMismatch, '%'); + }); + + log('init:', _.pick(self, ['testId', 'appId', 'width', 'height', 'platformId'])); + }; + + IonicReporter.prototype.reportSpecResults = function(spec) { + var self = this; + + if(!self.testData.total_specs) { + self.testData.total_specs = 0; + var allSpecs = jasmine.getEnv().currentRunner().specs(); + for(var sId in allSpecs) { + self.testData.total_specs++; + } + } + + self.flow.execute(function(){ + var d = protractor.promise.defer(); + + browser.waitForAngular().then(function(){ + + browser.getCurrentUrl().then(function(currentUrl) { + + browser.sleep(self.sleepBetweenSpecs).then(function(){ + + browser.takeScreenshot().then(function(pngBase64){ + var specIdString = '[' + (spec.id+1) + '/' + self.testData.total_specs + ']'; + log(specIdString, spec.getFullName()); + + self.testData.spec_index = spec.id; + self.testData.description = spec.getFullName(); + self.testData.highest_mismatch = self.highestMismatch; + self.testData.png_base64 = pngBase64; + self.testData.url = currentUrl; + pngBase64 = null; + + var requestDeferred = q.defer(); + self.screenshotRequestPromises.push(requestDeferred.promise); + + request.post( + 'http://' + self.domain + '/screenshot', + { form: self.testData }, + function (error, response, body) { + log(specIdString, 'reportSpecResults:', body); + try { + var rspData = JSON.parse(body); + self.highestMismatch = Math.max(self.highestMismatch, rspData.Mismatch); + } catch(e) { + log(specIdString, colors.red('reportSpecResults', 'error posting screenshot:'), e); + } + requestDeferred.resolve(); + } + ); + d.fulfill(); + }); + }); + + }); + + }); + + return d.promise; + }); + }; + + IonicReporter.prototype.reportRunnerResults = function() { + var self = this; + + self.flow.execute(function() { + var d = protractor.promise.defer(); + log('Waiting for all screenshots to be posted...'); + // allSettled waits until all the promises are done, whether they are rejected or resolved + q.allSettled(self.screenshotRequestPromises).then(function(all) { + d.fulfill(); + log('Finished!'); + }); + }); + }; + + + + this.jasmine.getEnv().addReporter( new IonicReporter(options) ); + +}; + +module.exports = IonicSnapshot; diff --git a/scripts/snapshot/protractor.config.js b/scripts/snapshot/protractor.config.js new file mode 100644 index 0000000000..909cad15b6 --- /dev/null +++ b/scripts/snapshot/protractor.config.js @@ -0,0 +1,32 @@ + +var buildConfig = require('../build/config'); +var path = require('canonical-path'); +var projectRoot = path.resolve(__dirname, '../..'); + +exports.config = { + + // Spec patterns are relative to the location of the spec file. They may + // include glob patterns. + specs: [ + path.resolve(projectRoot, 'dist/examples/**/*.scenario.js'), + ], + + // Options to be passed to Jasmine-node. + jasmineNodeOpts: { + showColors: true, // Use colors in the command line report. + defaultTimeoutInterval: 120000, + isVerbose: true + }, + + baseUrl: 'http://localhost:' + buildConfig.protractorPort, + + onPrepare: function() { + var ionicSnapshot = require('./ionic.snapshot.js'); + ionicSnapshot({ + groupId: 'ionic2', + appId: 'snapshots', + accessKey: process.env.IONIC_SNAPSHOT_KEY + }); + } + +}; diff --git a/scripts/snapshot/snapshot.task.js b/scripts/snapshot/snapshot.task.js new file mode 100644 index 0000000000..dfee2be7e2 --- /dev/null +++ b/scripts/snapshot/snapshot.task.js @@ -0,0 +1,75 @@ +var _ = require('lodash'); +var http = require('http'); +var connect = require('connect'); +var serveStatic = require('serve-static'); +var cp = require('child_process'); +var path = require('canonical-path'); +var uuid = require('node-uuid'); + +var projectRoot = path.resolve(__dirname, '../..'); + + +module.exports = function(gulp, argv, buildConfig) { + + var protractorHttpServer; + gulp.task('protractor-server', function() { + var app = connect().use(serveStatic(projectRoot + '/' + buildConfig.dist)); // serve everything that is static + protractorHttpServer = http.createServer(app).listen(buildConfig.protractorPort); + console.log('Serving `dist` on http://localhost:' + buildConfig.protractorPort); + }); + + gulp.task('snapshot', ['examples', 'protractor-server'], function(done) { + var configFile = path.resolve(projectRoot, 'scripts/snapshot/protractor.config.js'); + snapshot(done, configFile); + }); + + var snapshotValues = _.merge({ + browser: 'chrome', + platform: 'linux', + params: { + platform_id: 'chrome_local_test', + platform_index: 0, + platform_count: 1, + width: 400, + height: 800, + test_id: uuid.v4() + } + }, argv); + + function snapshot(done, configFile) { + var protractorArgs = [ + '--browser <%= browser %>', + '--platform <%= platform %>', + '--params.platform_id=<%= params.platform_id %>', + '--params.platform_index=<%= params.platform_index %>', + '--params.platform_count=<%= params.platform_count %>', + '--params.width=<%= params.width %>', + '--params.height=<%= params.height %>', + '--params.test_id=<%= params.test_id %>', + ].map(function(argument) { + return _.template(argument, snapshotValues); + }); + + return protractor(done, [configFile].concat(protractorArgs)); + } + + function protractor(done, args) { + console.log('Start protractor'); + + var child = cp.spawn('protractor', args, { + stdio: [process.stdin, process.stdout, 'pipe'] + }); + + var finish = _.once(function(err) { + err && done(err) || done(); + protractorHttpServer.close(); + }); + + child.stderr.on('data', function(data) { + finish('Protractor tests failed. Error:', data.toString()); + }); + child.on('exit', function() { + finish(); + }); + } +}; diff --git a/src/components/button/examples/button-block/test.scenario.js b/src/components/button/examples/button-block/test.scenario.js new file mode 100644 index 0000000000..86300719ab --- /dev/null +++ b/src/components/button/examples/button-block/test.scenario.js @@ -0,0 +1,6 @@ + +describe('Protractor Demo App', function() { + it('should just be', function() { + browser.get('http://localhost:8876/examples/button/button-block/'); + }); +});