I'm using a "core" ember addon in a boilerplate, with
npm link core-addon
This addon contains generic components, helpers, routes...
Is there a way to exclude some of these components in the boilerplate's ember-cli-build file?
I already tried the following in the ember-build-cli in my boilerplate project, which is probably wrong:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const environment = EmberApp.env();
module.exports = function (defaults) {
let app = new EmberApp(defaults, {
funnel: {
enabled:true,
exclude:['core-addon/pods/components/pages/**/*']
},
});
return app.toTree();
};
Ember version: 3.5.0
Ember cli version 3.5.0 node version 8.11.3
Addons generally take the reverse approach of this: The addon manages what gets merged into the consuming app via configuration in the consuming app.
At the highest level, each addon has an entry point that is the index.js file sitting in the root directory of the addon. The addon provides certain configuration options that it reads from config/environment.js of the consuming app when installing.
I think a really good case study for you would be ember-bootstrap. Look at their configuration options and more specifically the blacklist option. They allow the consuming application to only install a subset of the bootstrap components. Furthermore, the project supports bootstrap 3 or bootstrap 4, but the consuming app isn't getting both! The work is done in index.js
Let's look just at how they blacklist (ie exclude) certain components from being added to the consuming app:
treeForApp(tree) {
tree = this.filterComponents(tree);
return this._super.treeForApp.call(this, tree);
},
filterComponents(tree) {
let whitelist = this.generateWhitelist(this.bootstrapOptions.whitelist);
let blacklist = this.bootstrapOptions.blacklist || [];
// exit early if no opts defined
if (whitelist.length === 0 && blacklist.length === 0) {
return tree;
}
return new Funnel(tree, {
exclude: [(name) => this.excludeComponent(name, whitelist, blacklist)]
});
}
where this.excludeComponent at it's core is a boolean returning filter function that returns true if the blacklist contains it in the blacklist case (there for excluding it). The treeForApp function returns the tree for all app files, ie what will be merged from the addon's app dir into the consuming app:
The consuming app's ember-cli-build would look something like this:
//your-bootstrap-app/ember-cli-build.js
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
'ember-bootstrap': {
blacklist: ['bs-popover', 'bs-accordion']
}
});
return app.toTree();
};
and the result would be no bs-popover and no bs-accordion available in the consuming apps app tree. These options are obtained in the index.js file like so:
let options =Object.assign({}, defaultOptions, app.options['ember-bootstrap']);
this.bootstrapOptions = options;
Check this general guide to building addons and the more advanced api for more info.
I had a look at the official guide at http://yeoman.io/authoring/testing.html
but it left me a bit confused.
helpers.run returns a running context on which I can call methods etc.
But I'm confused how I can test if a file exists (with assert.file )
I was under the impression that helpers.run wil create the files either in memory or on the file system. But assert.file always fails.
It won't fail for files which are present on the filesystem prior to running the test.
When I run my generator (yo my-generator) I see that the files are created.
How can I test if files haven been created?
Here's my code so far which doesn't work.
I'm using jasmine for testing.
let helpers = require('yeoman-test');
let assert = require('yeoman-assert');
describe('generator:test', function () {
let path = require('path');
beforeEach(function () {
console.log(path.join(__dirname, '../generators/app'));
// The object returned acts like a promise, so return it to wait until the process is done
helpers.run(path.join(__dirname, '../generators/app'))
.withPrompts({
name: 'test',
appName: 'test',
appTitle: 'test',
apiEndpoint: 'http://localhost'
});
})
it('all config files', function () {
assert.file(#arrayOfFiles#);
});
});
I know this is an old question, but since it ranks quite high in Google's search results for testing Yeoman generators it seems useful to try to answer it:
Your beforeAll needs to return the Promise that helpers.run returns. Otherwise the test runner will not wait long enough before running the actual tests and the files will not be there. So your example should look like this:
let helpers = require('yeoman-test');
let assert = require('yeoman-assert');
describe('generator:test', function () {
let path = require('path');
beforeEach(function () {
return helpers.run(path.join(__dirname, '../generators/app'))
.withPrompts({
name: 'test',
appName: 'test',
appTitle: 'test',
apiEndpoint: 'http://localhost'
});
})
it('all config files', function () {
assert.file(#arrayOfFiles#);
});
});
I need help with testing action which using document.location.
I'm running react/redux + sinon/mocha.
Here is my action:
export function importFile(file) {
return (dispatch) => {
dispatch(importFile());
return jQuery.post('/api/import/', {file}).done((data) => {
window.location = `/edit/${data.id}`;
}).fail((err) => {
return dispatch(importFileError(err));
});
};
}
If u run my test in command line - i can not check if there was a redirect to page after done, but if i run my test in browser i had redirect and all other tests was dropped.
Here is my test:
it('should create an action to upload import file', (done) => {
const id = '123';
const file = 'testname';
const server = sinon.fakeServer.create();
const expectedActions = [
{type: actions.IMPORT_FILE}
];
const store = mockStore({}, expectedActions, done);
store.dispatch(actions.importFileUpload(file));
server.respondWith([201, { 'Content-Type':'application/json' }, `{"id":"${id}","version":12}`]);
window.XMLHttpRequest = sinon.useFakeXMLHttpRequest();
server.respond();
});
What is the best way to test functions like this? Prevent redirect or use a wrapper for redirect function?
Thanks.
I have a fairly complex piece of code I test with Mocha, in order to isolate it from Mocha I run it in an iframe. This prevents my application from messing up the Mocha tests. It should work also to prevent assignments to window.location from stopping the tests as window.location is local to the frame. (The Window object in the frame is a different object from the one in the page that contains the frame.)
I am setting up gulp/jasmine with a project running with browserify. I am using absolute paths to load modules. E.g. modules/deposit/card
When I want to load this module in my spec file to test it with jasmine, I am getting an error: Cannot find module 'card'. If i change the path to relative, then I get an error from require within the card.js file where another module is required using an absolute path. Looks like if I had everywhere relative path used, I would not have problems with loading modules in spec files. How can I teach my spec file to load modules via absolute path?
GulpFile.js
...
var jasmine = require('gulp-jasmine');
...
gulp.task('jasmine', function () {
return gulp.src('app/js/**/*.spec.js')
.pipe(jasmine());
});
card.spec.js
/* jslint node: true */
/* global describe, it, expect, beforeEach */
'use strict';
var CardView = require('modules/deposit/card');//throws and error : 'Cannot find module 'card''
describe('Card.js', function() {
var view, template, collection;
beforeEach(function() {
view = new CardView();
});
describe('When view is initialized', function() {
it('template should be defined', function() {
expect(template).toBeDefined();
});
});
describe('without collection', function() {
it('should throw exception', function() {
expect(function() {
new CardView();
}).toThrow(new Error('collection is required'));
});
});
});
And I want to see test results in the console where I run gulp serve.
I have uses angular translate from here (http://pascalprecht.github.io/angular-translate/) and it's just work fine, but it break my controller's unit test whith Error:
Unexpected request: GET scripts/i18n/locale-en.json
I don't understant why?
I use yeoman and test with karma.
app.js:
'use strict';
(function() {
angular.module('wbApp', ['authService', 'authUserService', 'checkUserDirective', 'ui.bootstrap', 'pascalprecht.translate'])
.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/login.html',
controller: 'LoginCtrl',
access: {
isFree: true
}
})
.when('/main', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
access: {
isFree: false
}
})
.otherwise({
redirectTo: '/'
});
});
})();
configTranslate.js:
'use strict';
(function() {
angular.module('wbApp')
.config(['$translateProvider',
function($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'scripts/i18n/locale-',
suffix: '.json'
});
$translateProvider.preferredLanguage('en');
}]);
})();
karma.conf.js:
files = [
...
'app/bower_components/angular-translate/angular-translate.js',
'app/bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js',
...
];
controller test:
'use strict';
describe('Controller: LoginCtrl', function() {
// load the controller's module
beforeEach(module('wbApp'));
var LoginCtrl, scope, location, httpMock, authUser;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller, $rootScope, $location, $httpBackend, AuthUser) {
authUser = AuthUser;
location = $location;
httpMock = $httpBackend;
scope = $rootScope.$new();
LoginCtrl = $controller('LoginCtrl', {
$scope: scope
});
httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();
}));
it(...);
...
});
if i add this in test controller, product same error:
httpMock.when('GET', 'scripts/i18n/locale-en.json').respond(200);
httpMock.flush();
or
httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();
httpMock.flush();
i find this post How do I test controllers with Angular Translate initialized in App Config? but not helped me :/
I extensively use $httpBackend in my tests and it works fine, but in this case it is ineffective. If I comment the line:
$translateProvider.preferredLanguage('en');
obviously an error, if I add on the runtime (in my controllers)
$translate.uses(local);
I end up with the same error?
So I turn to the translation configuration (configTranslate.js) or at runtime is the same result:
Unexpected request: GET scripts/i18n/locale-en.json
Here is the syntax that I tested, either in a "beforeEach(inject(function(...});"
or in a test "it('...', function() {...});"
httpMock.expectGET('scripts/i18n/locale-en.json');
httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();
httpMock.when('GET', 'scripts/i18n/locale-en.json').respond(data);
with at end
httpMock.flush();
I also tried a $ apply
httpMock.expectGET('scripts/i18n/locale-fr.json');
scope.$apply(function(){
$translate.uses('fr');
});
httpMock.flush();
nothing happens, Still this error is driving me crazy ..
If you have any suggestion
it's a known issue, please follow the documentation here: unit testing angular
The solution
Unfortunately, this issue is caused by the design of
angular-translate. To get around these errors, all we can do is to
overwrite our module configuration in our test suite, that it doesn't
use asynchronous loader at all. When there's no asynchronous loader,
there's no XHR and therefore no error.
So how do we overwrite our module configuration at runtime for our
test suite? When instantiating an angular module, we can always apply
a inline function which is executed as configuration function. This
configuration function can be used to overwrite the modules
configuration since we have access to all providers.
Using the $provide provider, we can build a custom loader factory,
which should then be used instead of the static files loader.
beforeEach(module('myApp', function ($provide, $translateProvider) {
$provide.factory('customLoader', function () {
// loader logic goes here
});
$translateProvider.useLoader('customLoader');
}));
Please read more in the above link provided.
We took the approach of ignoring the translation loader in unit tests, rather than being forced to modify each of the spec files.
One way to do it could be by separating the loader configuration to a separate file and then exclude it in karma.
So for example you can create a file app-i18n-loader.js (all other module configurations takes place in a different file):
angular
.module('myApp')
.config(loaderConfig);
loaderConfig.$inject = ['$translateProvider', '$translatePartialLoaderProvider'];
function loaderConfig($translateProvider, $translatePartialLoaderProvider) {
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: 'assets/i18n/{part}/{lang}.json'
});
$translatePartialLoaderProvider.addPart('myApp');
}
And in your karma.conf.js exclude the file:
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
//...
'bower_components/angular-translate/angular-translate.js',
'bower_components/angular-translate-loader-partial/angular-translate-loader-partial.js',
'app/**/*.mdl.js',
'app/**/*.js'
],
exclude: [
'app/app-i18n-loader.js'
],
(Note: Answer edited to a solution that does not require grunt/gulp).
I wanted a solution,
which was not too hacky
which didn't require me to change my actual application code,
which wouldn't interfere with the ability to load additional modules
and most importantly which wouldn't require me to change every
single test.
This is what I ended up with:
// you need to load the 3rd party module first
beforeEach(module('pascalprecht.translate'));
// overwrite useStaticFilesLoader to get rid of request to translation file
beforeEach(module(function ($translateProvider) {
$translateProvider.useStaticFilesLoader = function () {
};
}));
Assuming you don't need the actual translations for your unit tests, this works great. Just put the beforeEach on a global level, preferably in it's own file inside the test folder. It will be executed before every other test then.
I encountered this problem with protractor tests. My solution was to mock translations like this:
angular.module('app')
.config(function ($translateProvider) {
$translateProvider.translations('en', {});
$translateProvider.preferredLanguage('en');
})
Now no language files are downloaded, no strings get translated and I just test against the string keys in specifications:
expect(element(by.css('#title')).getText()).toEqual('TITLE_TEXT');
Try putting to test method:
it('should ...', function() {
httpMock.when('GET', 'scripts/i18n/locale-en.json').respond({});
httpMock.expectGET('scripts/i18n/locale-en.json');
scope.resetForm(); // Action which fires a http request
httpMock.flush(); // Flush must be called after the http request
}
See examples from Angular docs
Please have a look at https://github.com/PascalPrecht/angular-translate/blob/master/test/unit/service/loader-static-files.spec.js as a reference.
In general, I would recommend using a standard translation loader for unit tests (without the hassle of http loadings) which means you can provide the labels with $translateProvider.translations(). Why? Because you do not have to test the remote loading functionality which is part of angular-translate project.
None of the solutions worked for me but I came with these solutions:
1) If you need to use scope.$apply(), or should deal with states in your test (after the $apply() the 2nd approach won't work), override your app's translations with the $translateProvider.translations() method, using a plugin to load JSON files
beforeEach(module(function ($translateProvider) {
$translateProvider.translations('en', readJSON('scripts/i18n/locale-en.json'));
}));
2) If your tested controller depends on the $translate service you can use a plugin to load JSON files and combine it with $httpBackend to load your locale file when angular-translate requests it.
beforeEach(inject(function (_$httpBackend_) {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('scripts/i18n/locale-en.json').respond(readJSON('scripts/i18n/locale-en.json'));
$httpBackend.flush();
})));
Note this should be below your beforeEach(module('myApp')); or you will get an $injector error.
I made a simple mock service for $translate
$translate=function (translation) {
return {
then: function (callback) {
var translated={};
translation.map(function (transl) {
translated[transl]=transl;
});
return callback(translated);
}
}
};
Usage example here : https://gist.github.com/dam1/5858bdcabb89effca457
I use this pattern.
ApplicationModule set regular angular-translate config.
test code load 'testModule' instead of 'applicationModule'
// application module .js
(function() {
'use strict';
angular
.module('applicationModule', [
'ngAnimate',
'ngResource',
'ui.router',
'pascalprecht.translate'
])
.config(['$stateProvider', '$urlRouterProvider', '$translateProvider', '$translatePartialLoaderProvider', config]);
function config($stateProvider, $urlRouterProvider, $translateProvider, $translatePartialLoaderProvider) {
// set routing ...
$translateProvider.useStaticFilesLoader({
prefix: 'i18n/locale-',
suffix: '.json'
});
$translateProvider.useMessageFormatInterpolation();
$translateProvider.fallbackLanguage(['en']);
$translateProvider
.registerAvailableLanguageKeys(['en', 'ko'], {
'en_US': 'en',
'ko_KR': 'ko'
})
.determinePreferredLanguage(navigator.browserLanguage);
$translateProvider.addInterpolation('$translateMessageFormatInterpolation');
$translateProvider.useSanitizeValueStrategy('escaped');
}
})();
// test.module.js
(function() {
'use strict';
angular
.module('testModule', ['applicationModule'])
.config(['$translateProvider', '$translatePartialLoaderProvider', config])
.run(['$httpBackend', run]);
function config($translateProvider, $translatePartialLoaderProvider) {
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: 'i18n/locale-en.json'
});
$translatePartialLoaderProvider.addPart('applicationModule');
}
function run($httpBackend) {
$httpBackend.when('GET', 'i18n/locale-en.json').respond(200);
}
})();
// someDirective.spec.js
describe("a3Dashboard", function() {
beforeEach(module("testModule"))
var element, $scope;
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
element = angular.element("<div>{{2 + 2}}</div>");
$compile(element)($rootScope)
}))
it('should equal 4', function() {
$scope.$digest();
expect(element.html()).toBe("4");
})
})
Late to the table with this, but I got round this by specifying that Karma simply serve the files as per this entry in karma.conf.js:
files: [
...
{pattern: 'scripts/i18n/*.json', included: false, served: true},
...
]
The 2016 answer for this is to preprocess your json into your tests and properly test translations work on your directives.
I use karma-ng-json2js-preprocessor. Follow all the steps to setup your karma.conf then in your test file, prepend the relevant file as a module, then set that information in $translateProvider.
beforeEach(module('myApp', '/l10n/english-translation.json'));
// Mock translations for this template
beforeEach(module(function($translateProvider, englishTranslation) {
$translateProvider.translations('en_us', englishTranslation);
$translateProvider.useSanitizeValueStrategy(null);
$translateProvider.preferredLanguage('en_us');
}));
Note according to the plugin, it uses your filename to generate a camelcased module name. You can play with the function inside the module's /lib but basically it remove all dashes but KEEPS underscores in a camelCase. So en_us becomes En_us.
You'll also need to tell your test that it is expecting that file as a GEt.
$httpBackend.expect('GET', '/l10n/english-translation.json').respond(200);