AngularJS 1.7 Module Injection For Tests Error - javascript

My AngularJS (1.7.x) application has a custom filter that I want to write tests around it, and I decided to use Jest to perform this task.
My main issue is that as I follow this tutorial by Curt, I am struggling to properly get the filter (which has no outside dependencies so I thought it was the prime target to introduce unit tests with) to load within the test harness.
For starters, here is a simplified version of the filter for the purpose of this question:
angular.module('app.module').filter('takeTheThing', () =>
function (parameter1) {
return `${parameter1} thing!`;
}
);
And, after following the tutorial above, as well as reading up on another SO question specific to testing AngularJS filters, I have attempted every conceivable version of my simple test file as follows but receive a cryptic error message from Angular about it:
require('../node_modules/angular/angular.min.js');
require('../node_modules/angular-mocks/angular-mocks.js');
//Commenting or un-commenting this, results in module-related injector errors:
//require('./takeTheThing.filter.js');
describe('The thingerizer', () => {
let $filter;
beforeEach(angular.mock.module('app.module'));
//Injecting this specifically, or _$filter_ still errors:
beforeEach(inject(takeTheThingFilter => {
$filter = takeTheThingFilter;
}));
//Injecting here instead of in the beforeEach, same issue
it('should give me something', () => {
//Calling the specific filter or thru the Angular $filter... Same
const actual = $filter(1);
expect(actual).toEqual('1 thing!');
});
});
I'm pulling my hair out, but there's something quite basic I'm missing in regards to the test setup (specifically, how to load the app "correctly" without whole-hog loading my entire application). What gives?
AngularJS: 1.7.5
Angular Mocks: 1.7.6
Jest: 23.6.0 (I am using gulp-jest but even when I directly call jest from within the bin folder, I get the exact same errors, so I omitted most of those details here)

Related

writing unit tests for vue-multiselect

I'm trying to work out how to write tests for vue-multiselect and having trouble.
Specifically, I'm trying to "click" the input to open the dropdown (before then selecting an option), however nothing is working
I'ved tried
vm.$el.querySelector('.multiselect').click()
and in the browser:
document.querySelector('.multiselect').click()
and lots of other combinations but nothing's working.
Odd for a library which emphasises it's test coverage not to give an docs (I could find on how) to use it in unit tests.
At last got this working, the key was .dispatchEvent(new window.Event('focus')) not .focus().
Relevant bit of final test:
expect(vm.$el.querySelectorAll('input')).to.have.lengthOf(1)
vm.$el.querySelector('.multiselect').dispatchEvent(new window.Event('focus'))
await tick()
const s = vm.$el.querySelectorAll('.multiselect__element')[2].querySelector('span')
expect(s.innerText).to.equal('Science')
s.dispatchEvent(new window.Event('mousedown'))
await tick()
expect(routes_visited).to.deep.equal(['/s/3-science'])
vm.$el.querySelector('.cross').click()
await tick()
expect(routes_visited).to.deep.equal(['/s/3-science', '/'])
(note my component changes the vue-router route, so I'm watching routes and pushing into routes_visited in the test to track changes.)

ng:test no injector found for element argument to getTestability

There other question on SO with same problem, but the solutions didnt worked for me.
Here my spec.js
describe('Protractor Demo App', function() {
it('should have a title', function() {
browser.driver.get('http://rent-front-static.s3-website-us-east-1.amazonaws.com/');
expect(browser.getTitle()).toEqual('How It Works');
});
});
And here my conf.js
exports.config = {
framework: 'jasmine',
rootElement: 'body',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['spec.js']
}
So when i try to run my test im getting the error
Message:
Failed: Error while waiting for Protractor to sync with the page: "[ng:test] no injector found for element argument to getTestability\nhttp://errors.angularjs.org/1.5.0/ng/test"
Stack:
Error: Failed: Error while waiting for Protractor to sync with the page: "[ng:test] no injector found for element argument to getTestability\nhttp://errors.angularjs.org/1.5.0/ng/test"
at C:\Users\ShapeR\PycharmProjects\ratest\node_modules\jasminewd2\index.js:101:16
at Promise.invokeCallback_ (C:\Users\ShapeR\PycharmProjects\ratest\node_modules\selenium-webdriver\lib\promise.js:1329:14)
at TaskQueue.execute_ (C:\Users\ShapeR\PycharmProjects\ratest\node_modules\selenium-webdriver\lib\promise.js:2790:14)
at TaskQueue.executeNext_ (C:\Users\ShapeR\PycharmProjects\ratest\node_modules\selenium-webdriver\lib\promise.js:2773:21)
1 spec, 1 failure
I have a manual bootstrapping for body element and set the rootElement to body in config, but it didnt help. I even tried to remove manual boostraping and just add ng-app='rentapplicationApp' to body element, but it changes nothing, still same error.
So what is wrong?
The core value of Protractor is that it manages the angular loading for you, including sync, so you are very right in not wanting to use: browser.ignoreSynchronization = true.
The error message you are getting is that protractor is unable to locate angular in order to sync. This is because of a combination of two issues:
The page isn't ready, angular isn't loaded
It is unable to locate the ng-app, even once the page is loaded
Firstly, from Protractor setup page.
If your page does manual bootstrap Protractor will not be able to load your page using browser.get. Instead, use the base webdriver instance - browser.driver.get. This means that Protractor does not know when your page is fully loaded, and you may need to add a wait statement to make sure your tests avoid race conditions.
Solution 1
Add a wait statement.
Solution 2
If you don't have a good reason for manually bootstrapping, or don't want to wait:
Stop manually bootstrapping the app
Use browser.get over browser.driver.get
[ng:test] no injector found for element argument to getTestability
I suspect there is something wrong with the application itself, the way it is bootstrapped, since Protractor actually finds the root element (you can explicitly set the rootElement: "body.root" inside your config as well), but fails to setup the injector for the root element.
I'd try to figure out what is happening it step by step - first, try running the protractor test against a non-built application started directly from the source to ensure that this is not the webpack's or other part's of the build fault.
Then, I'd try upgrading to the latest 1.x Angular and Protractor (3.3.0 is the most recent version).
The most simple workaround at the moment would be to turn the sync between Protractor and Angular off, by using browser.ignoreSynchronization = true:
describe("Strange Protractor/Angular problem", function () {
beforeEach(function () {
browser.ignoreSynchronization = true;
browser.get("https://dl.dropboxusercontent.com/u/597502/vxcv/index.html");
var elm = $(".navbar-brand");
browser.wait(EC.presenceOf(elm), 5000);
});
it("should have an expected title", function () {
expect($(".navbar-brand").getText()).toEqual('RENT APPLICATION');
});
});
Of course, there are downsides to this approach - you would have to use browser.wait calls here and there to tackle the timing issue. The test flow would not be as natural and simple as it would be when sync is on.
The problem was in bootstrapping my app. For some reason it doesnt work with ng-app tag. The only working solution was to manual bootstrap it
angular.bootstrap(document, ["rentapplicationApp"]);
And first argument should be dom node, not a string, like it was in my case, although with string the app will work, but getTestability will fail.

how to remove include module error in angular ?

I make a simple demo in my Pc which is working fine .But when I make fiddle to ask Question say
Uncaught Error: [$injector:nomod] Module 'myapp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
can you please tell why it is occur ? I am getting data n my pc.Actually my real Question how to refresh or call same webservice after some tome mean after 1 minutes.As in jquery we have setinterval function .how I will achieve in this angular ?
here is fiddle
http://jsfiddle.net/acboLcv2/1/
var app=angular.module("myapp");
app.factory('test', function($http) {
//This whole object is returned when the Service runs. It is a singleton
//and its properties can be accessed from any controller
return {
stationDashBoard: function(callback,error) {
$http.get('http://184.106.159.143:8180/FGRailApps/jservices/rest/a/departure?crsCode=VIC').success(callback).error(error);
}
}
});
function departureContrl($scope,test){
$scope.loading=true;
test.stationDashBoard(function(data){
console.log(data);
$scope.data=data.data;
$scope.loading=false;
//alert(data);
},function(error){
alert('error')
}) ;
}
Thanks
So there's a few things with your site you need to focus on:
Getting rid of the module error:
I think it's fixed, but heuristically I suggest:
Ensure that the declaration of an angular module includes the empty array as as already been mentioned. This is necessary, thusly:
angular.module("test", []);
Ensure you reference the angular app in the html. I suggest body for most applications:
CORS error
You're trying to load data from a different domain with $http.get(...). This won't work unless you do some kind of CORS hackery or you get the data from the same domain. Ie, you'd have to host this code on http://184.106.159.143:8180 (in this example).
Polling request
You're asking about fetching data from a server every n seconds. This is quite easy and the method you suggest with setTimeout() would work but would need to be integrated into the angular Digest Loop:
I suggest using $timeout because it will work with angular's rendering something like this (this is pseudocode, not tested):
var fetchFromServer(cb){
$timeout(function(){
$http.get(...).then(function(data){
//Do something with the retrieved data
cb(fetchFromServer); //Recurse
});
}, 15000);
};
fetchFromServer(fetchFromServer);
Otherwise you can use setTimeout as you normally would in javascript, but don't forget to call the $scope.$apply() method to render things in angular if you do it outside the digest loop or else it will appear as if there has been no effect.

How to inject an entire module dynamically in AngularJS?

Alright, so I'm trying to set-up $httpBackend to use as a mock server for local development against an API. I have two services in separate modules: search and searchMock:
search returns a $resource object that exposes the API verbs when in staging or production environments, and works as expected
searchMock exposes $httpBackend which, in turn, responds with mock JSON objects, and works as expected by itself
I have another service, APIInjector, that determines what the current environment is based on a config file that's included dynamically by Grunt when the app is built and injects either search or searchMock accordingly.
My problem is, as far as I can tell from searching high and low, $httpBackend needs to be set-up within a module's run method. The problem with this is I can't inject the run method within my APIInjector's conditional logic.
How can I expose $httpBackend if the dev environment condition is met, and my $resource service otherwise? Note that I didn't include the calling controller's or the searchService's code, but I can if needed for clarification.
searchMock:
var searchMockService = angular.module('searchMockService', []);
searchMockService.run([
'$httpBackend',
function($httpBackend) {
results = [{name: 'John'}, {name: 'Jane'}];
$httpBackend.whenGET('/search').respond(results);
}]);
APIInjector:
var APIInjectorService = angular.module('APIInjectorService', [
'searchService',
'searchMockService'
]);
APIInjectorService.factory('APIInjector', [
'$injector',
'ENV_CONF',
function($injector, ENV_CONF) {
var isDevelopment = ENV_CONF.IS_DEVELOPMENT;
// Check to see if we're in dev and, if so, inject the local API services
if (isDevelopment) {
return {
Search: // Not sure what to do here to expose searchMock's run method???
};
} else {
return {
Search: $injector.get('Search') // This returns $resource from the search service, as expected
};
}
}]);
Just to put a final point on this question, I ultimately pulled this out to my build script (Grunt) and abandoned the above approach completely. Instead, I'm passing in the environment I want to build for (development, staging, production, etc) as a flag to Grunt and then loading the appropriate API service during the build.
Hope that helps someone else trying to implement backendless development!
As bad as it looks at first place, i'd use the document.write method,if you are not using requirejs(it shouldnt be a problem with requirejs).
1 - bootstrap your ENV_CONF variable in a script tag as first script in HEAD tag.
2 - load all the common scripts
3 - test the ENV_CONF.IS_DEVELOPMENT in another SCRIPT tag.
4 - if true document.write('<script src="angular-mocks.js"></script><script src="main-dev-module.js"></script>')
5 - if false document.write('<script src="main-prod-module.js"></script>')
6 - profit
That way you dont need to deal with ENV_CONF.IS_DEVELOPMENT inside your modules.
html5 boilerplate uses this technique to either fetch jquery from a cdn or locally
https://github.com/h5bp/html5-boilerplate/blob/master/index.html
Edit: I'm sure there are cleaner ways to inject services dynamically in angularjs,but that's what i'd do.I'd like someone to propose a better alternative.

Destroy a Backbone.Router and all Side effects after Creation

Let's say you want to be able to create a Backbone.Router instance that calls Backbone.history.start, destroy all traces of it, and create it again.
I need to do this for unit testing.
The problem is that creating a Backbone Router has global implications. Like Backbone.history, which is undefined until you make it.
var ReversibleRouter = Backbone.Router.extend({
initialize: function(){
_buildAppPaths(this.options) // Implementation not shown. Builds the application paths
Backbone.history.start()
},
destroy: function(){
// TODO: Implement this
}
})
I'd like to be able to create and completely destroy so that I can do some unit testing on my application's implementation of the Router.
It it enough to call Backbone.history.stop() and set Backbone.history = undefined?
If you're looking to do more full stack integration types of testing, you can reset your application's router by using:
Backbone.history.stop()
Backbone.history = undefined // Could also probably use delete here :)
This has been successful so far--our tests probably instantiate and kill the router 5 times during our qunit testing. Works great.
Maybe mocking like #AdamTerlson is saying is the better way to go long term, but I'm still learning unit and integration testing philosophy. I don't mind having a full stack test or two, though.
This might be cheating, but I'm of the opinion you should be mocking functions outside of your unit of functionality that your testing and be especially sure your unit tests are not be allowed to hit the live DOM, server or the browser.
Look into SinonJS, it's great for this.
Here's an example of testing the initialize method of the router without ever affecting the browser history state.
test('...', function () {
// Arrange
var buildAppPaths = sinon.stub(ReversibleRouter.prototype, '_buildAppPaths');
var startHistory = sinon.stub(Backbone.history, 'start');
var options = {};
// Act
new ReversibleRouter(options);
// Assert
ok(buildAppPaths.calledWith(options));
ok(startHistory.calledOnce); // or ok(Backbone.history.start.calledOnce);
});
Under this approach, you have successfully asserted that all calls were made to external units of functionality, including params. At that point, you can trust that the external call will do its job (proven by additional unit tests).
To make calls outside of this one unit would be doing integration tests and not unit tests.

Categories

Resources