So I have the following GulpJS tasks that are all related with installing and copying bower files:
gulp.task('bower-install', function() {
var command = 'bower install';
gutil.log(gutil.colors.cyan('running command:'), command);
return gulp.src('', {read: false})
.pipe(shell([
command
]));
});
gulp.task('bower-copy', function() {
return gulp.src(gulpConfig.bowerCopy.map(function(item) {
return './bower_components/' + item;
}), {base: './bower_components'})
.pipe(gulp.dest('./htdocs/components'));
});
gulp.task('bower-copy-clean', function() {
return gulp.src('./bower_components')
.pipe(clean());
});
gulp.task('bower-clean', function() {
return gulp.src('./htdocs/components')
.pipe(clean());
});
gulp.task('bower', 'Download and move bower packages', function(done) {
runSequence(
'bower-install',
'bower-clean',
'bower-copy',
'bower-copy-clean',
done
);
});
I am doing it this way because I need these tasks to run sequentially one after the other. While when I run gulp bower everything works as expected, I would like to structure this code so that the only exposed task is bower as all the bower-* are pointless to run by themselves.
Is there any way to write this code so all the bower-* task run one after the other but only expose the bower task?
Because a gulpfile is just a regular node app, usually when we get into stuck situations like this, it's good to ask "what would we do if we didn't have gulp?" Here's my solution:
var async = require('async');
var del = require('del');
var spawn = require('child_process').spawn;
function bowerInstall(cb) {
var command = 'bower install';
gutil.log(gutil.colors.cyan('running command:'), command);
var childProcess = spawn('bower', ['install'], {
cwd: process.cwd(),
stdio: 'inherit'
}).on('close', cb);
});
function bowerCopy(cb) {
gulp.src(gulpConfig.bowerCopy.map(function(item) {
return './bower_components/' + item;
}), {base: './bower_components'})
.pipe(gulp.dest('./htdocs/components'))
.on('end', cb);
});
function bowerCopyClean(cb) {
del('./bower_components', cb);
});
function bowerClean() {
del('./htdocs/components', cb);
});
gulp.task('bower', 'Download and move bower packages', function(done) {
async.series([
bowerInstall,
bowerClean,
bowerCopy,
bowerCopyClean
], done);
});
Note that I also sped up your build substantially by not reading in the entirety of the bower_components and htdocs/components directories into ram.
Related
I've run into a weird problem writing integration tests for my component. When I run each one individually, they pass. When I run multiple, the first one passes and the other ones fail. I think it has something to do with closure actions but I don't know.
Here's my component code
// components/game-nav-key.js
triggerKeyAction(code) {
if (this.get('prevKeyCode').contains(code)) {
this.sendAction('onPrevKey', true);
} else if (this.get('nextKeyCode').contains(code)) {
this.sendAction('onNextKey', true);
} else if (this.get('openKeyCode').contains(code)) {
this.sendAction('onOpenKey');
}
},
didInsertElement() {
var self = this;
Ember.$('body').keydown(function(e) {
self.triggerKeyAction(e.which);
});
Ember.$('body').keyup(function(e) {
});
}
And my tests
// game-nav-key-test.js
it('tracks key commands and sends an action for K', function() {
let spy = sinon.spy();
this.set('gotoPrev', spy);
this.render(hbs`
{{game-nav-key onPrevKey=(action gotoPrev)}}
`);
triggerKeydown($('body'), 75);
triggerKeyup($('body'), 75);
sinon.assert.calledOnce(spy);
sinon.assert.calledWith(spy, true);
});
it('tracks key commands and sends an action for J', function() {
let spy = sinon.spy();
this.set('gotoNext', spy);
this.render(hbs`
{{game-nav-key onNextKey=(action gotoNext)}}
`);
triggerKeydown($('body'), 74);
triggerKeyup($('body'), 74);
sinon.assert.calledOnce(spy);
sinon.assert.calledWith(spy, true);
});
it('tracks key commands and sends an action for R', function() {
let spy = sinon.spy();
this.set('open', spy);
this.render(hbs`
{{game-nav-key onOpenKey=(action open)}}
`);
triggerKeydown($('body'), 82);
triggerKeyup($('body'), 82);
sinon.assert.calledOnce(spy);
});
I removed all beforeEach's, so it's literally just those three tests. Like I said, each one passes individually, and when it is listed first, but the second two fail when run together. Note that using console.log statements I have verified that the code hits the line directly above each of the this.sendAction calls in their respective tests
seems you need to destroy your listeners created in didInsertElement
willDestroyElement() {
Ember.$('body').off('keydown');
Ember.$('body').off('keyup');
}
Ember has the following implementation of setTimeout, which it is recommended developers use because the code gets added to the run loop, which has advantages for tests.
Ember.run.later((function() {
console.log("will run once after 1000");
}), 1000);
Is there a similar Ember replacement for setInterval and, by implication, clearInterval (which is used to cancel a setInterval)? I need to run someFunc every 1000 ms
this.intervalId = setInterval(this.someFunc.bind(this), 1000);
I'm not aware of any equivalent, but I use such a code to have the functionality:
var Poller = Ember.Object.extend({
_interval: 1000,
_currentlyExecutedFunction: null,
start: function(context, pollingFunction) {
this.set('_currentlyExecutedFunction', this._schedule(context, pollingFunction, [].slice.call(arguments, 2)));
},
stop: function() {
Ember.run.cancel(this.get('_currentlyExecutedFunction'));
},
_schedule: function(context, func, args) {
return Ember.run.later(this, function() {
this.set('_currentlyExecutedFunction', this._schedule(context, func, args));
func.apply(context, args);
}, this.get('_interval'));
},
setInterval: function(interval) {
this.set('_interval', interval);
}
});
export
default Poller;
Then, you instantiate the poller: var poller = Poller.create() and then you can play with poller.start() and poller.stop() + set set the interval via poller.setInterval(interval).
In my code, I did more or less that way (polling the reports every 10 seconds):
_updateRunningReport: function(report) {
var poller = new Poller();
poller.setInterval(this.POLLING_INTERVAL);
poller.start(this, function() {
if (report.isRunning()) {
this._reloadReport(report);
} else {
poller.stop();
}
});
eventBus.onLogout(function() {
poller.stop();
});
},
Hope this helps...
I have been playing around with implement "setInterval" in Ember Octane way and I think this is THE MOST SIMPLE and CLEAN solution:
index.hbs:
{{this.counter}}
<button {{on "click" this.start}}>Start</button>
<button {{on "click" this.stop}}>Stop</button>
<button {{on "click" this.reset}}>Reset</button>
controllers/index.js
import Controller from '#ember/controller';
import { tracked } from '#glimmer/tracking';
import { action } from '#ember/object';
import { cancel, later, next } from '#ember/runloop';
export default class IndexController extends Controller {
#tracked counter = 0;
runner = null;
#action start() {
next(this, function () {
this.runner = this.tick();
});
}
#action stop() {
cancel(this.runner);
}
#action reset() {
this.counter = 0;
}
tick() {
return later(this, function () {
this.counter++;
this.runner = this.tick();
}, 1000);
}
}
Another option is to use the ember-poll add-on. Internally, it does somewhat the same as #andrusieczko Poller object (both take the Ember Run Loop into account). From an Ember developer view, you get access to a poll service.
Just install the add-on: npm install ember-poll --save-dev.
Example:
import Ember from 'ember';
export default Ember.Route.extend({
poll: Ember.service.inject(),
setupController() {
this._super(...arguments);
this.get('poll').addPoll({
interval: 1000,
label: 'my-poll',
callback: () => {
//do something every second
}
});
},
actions: {
willTransition() {
this.get('poll').clearPollByLabel('my-poll');
}
}
}
Tested in ember -v 1.18
This is what I've been doing inside my component. This works perfectly fine and should be the ideal way of approaching. If you're handling async promise objects inside the interval, make sure to kill it in willDestroyElement() hook or somewhere.
init() {
this._super(...arguments);
this.startAutoSaveTimer();
},
startAutoSaveTimer() {
let autosaveFunc = function() {
console.info('Arr .. I'm a pirate!');
};
set(this, 'timerInstance', setInterval(autosaveFunc, 1000));
},
in my test/features/support/world.js
module.exports = function(){
var PersistenceWorld = function PersistenceWorld(callback){
this.cleanUp(callback);
};
PersistenceWorld.prototype.addNewReciepe = function(callback){
};
PersistenceWorld.prototype.cleanUp = function(callback){
};
};
and My test/features/step_definitions/first_steps
module.exports = function(){
this.World = require('../support/world');
console.log(this);
this.Before(function (callback) {
console.log("hey I run before each scenario");
callback();
});
this.After(function (callback) {
console.log("hey I run after each scenario");
callback();
});
this.Given(/^user is on homepage$/, function(callback) {
callback.pending();
});
};
when I donot require the world before after is getting fired.
MyApp
.sass-cache
.tmp
app
bower_components
ember-mocha-adapter
node_modules
test
features //I added this folder for cucumberjs features.
step_definitions //I added
lib
mocha
chai.js
expect.js
spec
I'm trying to execute a promise inside Ember.RSVP.all
App.Foo = Ember.Object.create({
bar: function() {
var configuration = ajaxPromise("/api/configuration/", "GET");
Ember.RSVP.all([configuration]).then(function(response) {
//do something with the response in here
});
}
});
But because my integration test mocks the xhr w/out a run loop the test fails with the expected error "You have turned on testing mode, which disabled the run-loop' autorun"
So I wrapped the RSVP with a simple ember.run like so
App.Foo = Ember.Object.create({
bar: function() {
var configuration = ajaxPromise("/api/configuration/", "GET");
Ember.run(function() {
Ember.RSVP.all([configuration]).then(function(response) {
//do something with the response in here
});
});
}
});
But I still get the error for some odd reason. Note -if I run later it's fine (this won't work though as I need to exec the async code for this test to work correctly)
App.Foo = Ember.Object.create({
bar: function() {
var configuration = ajaxPromise("/api/configuration/", "GET");
Ember.run.later(function() {
Ember.RSVP.all([configuration]).then(function(response) {
//do something with the response in here
});
});
}
});
Here is my ajaxPromise implementation -fyi
var ajaxPromise = function(url, type, hash) {
return new Ember.RSVP.Promise(function(resolve, reject) {
hash = hash || {};
hash.url = url;
hash.type = type;
hash.dataType = 'json';
hash.success = function(json) {
Ember.run(null, resolve, json);
};
hash.error = function(json) {
Ember.run(null, reject, json);
};
$.ajax(hash);
});
}
How can I wrap the Ember.RVSP inside my ember run w/out it throwing this error?
Update
here is my test setup (including my helper)
document.write('<div id="ember-testing-container"><div id="wrap"></div></div>');
App.setupForTesting();
App.injectTestHelpers();
test("test this async stuff works", function() {
visit("/").then(function() {
equal(1, 1, "omg");
});
});
The only part I've left out is that I'm using jquery-mockjax so no run loop wraps the xhr mock (and in part that's why I like this library, it fails a test when I don't wrap async code with a run loop as the core team suggests)
This may have to do with how your tests are being run, so if you can provide the test, it will be helpful
I also noticed:
It turns out I believe you are also being (or will be soon) trolled by jQuery's jQXHR object being a malformed promise, the fulfills with itself for 0 reason, and enforcing its own nextTurn on you. Which is causing the autorun. This will only happen in the error scenario.
In ember data we sort this out, by stripping the then off the jQXHR object
see:
https://github.com/emberjs/data/blob/4bca3d7e86043c7c5c4a854052a99dc2b4089be7/packages/ember-data/lib/adapters/rest_adapter.js#L539-L541
I suspect the following will clear this up.
var ajaxPromise = function(url, type, hash) {
return new Ember.RSVP.Promise(function(resolve, reject) {
hash = hash || {};
hash.url = url;
hash.type = type;
hash.dataType = 'json';
hash.success = function(json) {
Ember.run(null, resolve, json);
};
hash.error = function(json) {
if (json && json.then) { json.then = null } // this line
Ember.run(null, reject, json);
};
$.ajax(hash);
});
}
This is rather unfortunate, and various separate concepts and ideas are coming together to cause you pain. We hope to (very shortly) land Ember.ajax which normalizes all these crazy away.
Also feel free to checkout how ember-data is going this: https://github.com/emberjs/data/blob/4bca3d7e86043c7c5c4a854052a99dc2b4089be7/packages/ember-data/lib/adapters/rest_adapter.js#L570-L586
I feel your pain on this Toran, I'm sure it's what Stefan's stated, we had to 1 off mockjax to get our tests to work with it.
https://github.com/kingpin2k/jquery-mockjax/commit/ccd8df8ed7f64672f35490752b95e527c09931b5
// jQuery < 1.4 doesn't have onreadystate change for xhr
if ($.isFunction(onReady)) {
if (mockHandler.isTimeout) {
this.status = -1;
}
Em.run(function () {
onReady.call(self, mockHandler.isTimeout ? 'timeout' : undefined);
});
} else if (mockHandler.isTimeout) {
// Fix for 1.3.2 timeout to keep success from firing.
this.status = -1;
}
I have a problem with my unit test for a directive where I need to setup the directive before being able to test it.
This is the way I initialize it:
var elm, $templateCache
var count = 0;
beforeEach(module('app/js/directives/templates/mytemplate.html'));
beforeEach(inject(function($injector) {
$templateCache = $injector.get('$templateCache');
}));
beforeEach(inject(
['$httpBackend','$compile','$rootScope', function(_$compile) {
var template = $templateCache.get('app/js/directives/templates/mytemplate.html');
if(count == 0 ){
$httpBackend.expectGET('js/directives/templates/mytemplate.html').respond(template);
}
elm = angular.element('<div my-directive</div>');
$compile(elm)($rootScope);
if(count == 0){
$httpBackend.expectGET('/app/js/content/some.json').respond('{ "left" : "<div>Contact information</div>"}');
$httpBackend.flush();
}
$rootScope.$digest();
count++;
}]
));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it("should work like I want it to", function() {
$rootScope.my-directive.somefunction();
expect($rootScope.somevalue).toBe(2);
});
it("should load the previous page if the function scope.layover.qa_pager.prev is called", function() {
$rootScope.my-directive.someotherfunction();
expect($rootScope.somevalue).toBe(1);
});
This works, but the hack with the count should not be needed in my opinion. If I leave it out I get problems with the $httpbackend which doesn't seem to be reinitialised every time so it gives me 'Error: Unsatisfied requests: GET /app/js/content/some.json' and 'Error: No pending request to flush !'
To me it looks like you are trying to load a template for a unit test. I'm assuming you are using karma to run the tests, so you should look at html2js. It loads your templates into the template cache for you.
https://github.com/karma-runner/karma-ng-html2js-preprocessor
// karma.conf.js
module.exports = function(config) {
config.set({
preprocessors: {
'**/*.html': ['ng-html2js']
},
files: [
'*.js',
'*.html'
],
ngHtml2JsPreprocessor: {
// strip this from the file path
stripPrefix: 'public/',
// prepend this to the
prependPrefix: 'served/',
// or define a custom transform function
cacheIdFromPath: function(filepath) {
return cacheId;
}
}
});
};