External promises never finish in unit tests with ngMock - javascript

I'm trying to unit test the resolution/rejection of external promises.
The problem is that those promises are never finished when ngMock module is injected - which is mandatory, as I'm triggering HTTP requests and timeouts.
Take for example angular-pouchdb, which is a dependency of my project. As of v2.0.0, because PouchDB promises are used, the tests no longer finish when ngMock is injected.
Example code, adapted from angular-pouchdb tests:
var scope;
beforeEach(function() {
var $injector = angular.injector(['ng', 'ngMock', 'pouchdb']);
var pouchDB = $injector.get('pouchDB');
scope = $injector.get('$rootScope');
db = pouchDB('db');
});
it('should wrap destroy', function(done) {
db.destroy()
.then(shouldBeOK)
.catch(shouldNotBeCalled)
.finally(done);
// No matter what's used ($apply, $applySync, $timeout.flush, etc) -- the tests will never finish.
scope.$apply();
});
The log for the above test is the following:
C:\Users\Gustavo\Projetos\main\angular-pouchdb (master)
λ karma start
INFO [karma]: Karma v0.12.37 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.8 (Windows 8 0.0.0)]: Connected on socket s1A7jIzmtcOxkGCtzYke with id 42899233
PhantomJS 1.9.8 (Windows 8 0.0.0) Angular-aware PouchDB public API should wrap destroy FAILED
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
So, my question boils down to: is there something I'm missing from unit testing Angular? Or is it an internal bug in ngMock?
This issue has been created in the angular-pouchdb repository as well.

Edit 8/25: So we have actually solved this issue but I am still looking into what caused it. I will have a more updated write up in a few days.
Alright after hitting at this problem in my spare time for a number of days I have narrowed this down to some sort of exception failure in a 3rd party promise from pouchDB where it hits a 10 second timeout and doesn't throw a clean error back. I am able to get this by updating my
jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
If I got back through and tweak a custom build and put some logging in to pouchDB I can confirm that the promise is not getting returned correctly in there, I have been able to replicate this in 3.6 and 4.0 pouchDB. From my wrapped console logs around the call nothing ever comes out of the wrapped call. Around test 3 I get uncaught 409 errors so there is clearly something not being handled correctly in pouchDB.
Here is an example without ngMock turned on so you can see how far it gets without it.

I do not think it's ngMock issue, but I think it's because PhantomJS is not ready to support PouchDB yet.
https://github.com/ariya/phantomjs/issues/10992
https://github.com/pouchdb/pouchdb/blob/c55db55d7edbf0821538e027399536c260af5876/docs/adapters.md

Related

Why do I get "Ajax authorization fails" in my tests

I am following the ember tutorials, and specifically I'm on services.
I am 99.9% certain that I have the exact code in place -- I am copying by hand, because I believe that helps me absorb it more completely, but if anything fails I start using a diff checker to see if I made a typo. To my knowledge, no typos.
The App I have written performs identically to the screen shots in the tutorials, and the only error I get is a lint error for having a test that doesn't have an assert in it (yet).
Prior to this unit, all other tests have passed as well. But now I am getting failed tests that previously passed. They appear to all stem from the stubbed call to the map service failing. The first test that fails is integration/component/rental-listing-test.js:
hooks.beforeEach(function() {
this.rental = {
image: 'fake.png',
title: 'test-title',
owner: 'test-owner',
type: 'test-type',
city: 'test-city',
bedrooms: 3
};
});
test('should display rental details', async function(assert) {
await render(hbs`{{rental-listing rental=rental}}`);
assert.equal(this.element.querySelector('.listing h3').textContent.trim(), 'test-title', 'Title: test-title');
assert.equal(this.element.querySelector('.listing .owner').textContent.trim(), 'Owner: test-owner', 'Owner: test-owner');
});
If I remove the new line from rental-listing.hbs ( {{location-map location=rental.city}} ), thus preventing the map from being used, these tests once again pass (though the new tests for the component using the service have issues).
So either I am doing something wrong that I can't find, or else the fine folk at emberjs.com have not provided complete information in this tutorial. Do I need to somehow stub the map service? that appears in the .hbs file for the above test to pass? If so, why do you think they failed to mention this?
ETA assertion:
Ajax authorization failed # 273 ms
Source: Error: Ajax authorization failed
at new EmberError (http://localhost:7357/assets/vendor.js:13635:31)
at new AjaxError (http://localhost:7357/assets/vendor.js:116954:13)
at new UnauthorizedError (http://localhost:7357/assets/vendor.js:116968:13)
at Class._createCorrectError (http://localhost:7357/assets/vendor.js:117533:25)
at Class.handleResponse (http://localhost:7357/assets/vendor.js:117528:25)
at Object.jqXHR.done.fail (http://localhost:7357/assets/vendor.js:117380:41)
at fire (http://localhost:7357/assets/vendor.js:3609:31)
at Object.fireWith [as rejectWith] (http://localhost:7357/assets/vendor.js:3739:7)
at done (http://localhost:7357/assets/vendor.js:9648:14)
at XMLHttpRequest.<anonymous> (http://localhost:7357/assets/vendor.js:9889:9)
You shouldn't need the api key to run the tests. Have you tried the super rentals repo to see if it has the same issue? https://github.com/ember-learn/super-rentals
If it does have the same problem we'll probably need to PR a fix to the tutorial.
Update
I see that the integration test in question is missing a stub maps service definition. It is there in the rentals repo, but not mentioned in the guides tutorial. See https://github.com/ember-learn/super-rentals/blob/master/tests/integration/components/rental-listing-test.js for the code. I've added this info to an issue for updating the guides: https://github.com/ember-learn/guides-source/issues/347
So I finally had time to look at it. The problem is that this is set up for the external map service to use an environment variable for an API key. This is why it runs the app fine (I use KEY=value ember s to start the app) but the tests were not. Simply using KEY=value ember t -s causes these tests to pass. And I'm left with only linting issues.
For the record, this is the sort of thing that should be in the tutorial itself, and I'm not sure why I didn't think of it before.

Finding the UI element in protractor is not consistent. Working with localhost and not with the server

Here is the code I am using expect(element(by.id('title')).getAttribute('value')).toMatch('Sample Title')
in local machine it is working perfectly fine and on server it is not with the following error.
Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
While waiting for element with locator - Locator: By(css selector, *[id="title"])
and surprisingly sometimes these tests are working on server when I execute them alone.
to add to the question. I observed that protractor is able to find only one element in the tests and all the remaining are ignored with the error as above.
what could be the solution for this?
That could be application issue. Sometimes it happens that angular never reports to protractor that all tasks are done, so you might get this timeout error that you have.
http://www.protractortest.org/#/timeouts
AngularJS If your AngularJS application continuously polls $timeout or
$http, Protractor will wait indefinitely and time out. You should use
the $interval for anything that polls continuously (introduced in
Angular 1.2rc3).
Angular For Angular apps, Protractor will wait until the Angular Zone
stabilizes. This means long running async operations will block your
test from continuing. To work around this, run these tasks outside the
Angular zone. For example:
this.ngZone.runOutsideAngular(() => { setTimeout(() => {
// Changes here will not propagate into your view.
this.ngZone.run(() => {
// Run inside the ngZone to trigger change detection.
}); }, REALLY_LONG_DELAY); }); As an alternative to either of these options, you could disable waiting for Angular, see below.
As said in the error, it seems your app is not an angular. Isn't it?
If so, you need to use it:
browser.waitForAngularEnabled(false);

Determining when karma-pact mock server has started

We're using the karma-pact plugin to run our pact JS client tests, based on the example from https://github.com/pact-foundation/pact-js/blob/master/karma/mocha/client-spec.js .
In the example there's a timeout in the before(), I believe to ensure the mock service has started before running the tests (see comment "required for slower Travis CI builds").
I'm reluctant to set a fixed timeout in our tests as it'll either be too short or too long in different environments (e.g. CI vs local) and so I was looking for a way to check if the server has started.
I'd tried using the pact API https://github.com/pact-foundation/pact-node#check-if-a-mock-server-is-running , however this appears to start a new mock server which conflicts with the one started by the karma-pact plugin (an Error: kill ESRCH error is reported when trying to run pact.createServer().running from within a test).
Is there a way to determine if the mock server has started up e.g. by waiting for a URL to become available? Possibly there's a way to get a reference the mock server started by the karma-pact plugin in order to use the pact-node API?
Actually the simplest way is to wait for the port to be in use.
Karma Pact by default will start the Mock on port 1234 (and you can specify your own). Once the port is up, the service is running and you can proceed.
For example, you could use something like wait-for-host to detect the running mock service:
var waitForPort = require('wait-for-port');
waitForPort('localhost', 1234, function(err) {
if (err) throw new Error(err);
// ... Mock Service is up - now we can run the tests
});

Protractor functions not running (I've looked through configs, logs, etc.)

I'll describe the problem best as I can - if I've left out details, please tell me - I'm still learning about Protractor.
I'm writing my first protractor test for a website, and its contents are really simple.
teacher.js
describe("teacher list", function () {
beforeEach(function () {
browser.get('http://127.0.0.1:9000/teachers');
});
it("has teacher", function () {
initiateDebug();
var teachers = element.all(by.repeater("teacher in items track by $index"));
initiateDebug();
expect(teachers.count()).toBe(20);
initiateDebug();
})
});
...where initiateDebug() is a function defined in the onPrepare function in protractor_conf.js as follows:
initiateDebug()
global.initiateDebug = function() {
browser.pause(debugPortNumber);
debugPortNumber++;
};
The reason I included this was because calling browser.pause() results in the error:
Port 5858 is already in use. Please specify another port to debug.
[launcher] Process exited with error code 1
and I copied a temp fix proposed by a member in this issue's thread: https://github.com/angular/protractor/issues/2206.
Back to the problem
Question 1
If I run this code, the page just hangs there, and nothing loads. Googling came up with this (Getting error: Error while waiting for Protractor to sync with the page: {}), and I directly used browser.driver.get() and that seems like a workable solution.
However, my first question is - is this a case of it not being able to find angular? Because I distinctly remember that when I ran Protractor a week before, if it couldn't find Angular, it throws this error:
UnknownError: javascript error: angular is not defined
The error from my console, however, is this:
1) teacher list has teacher
Message:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Stack:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
at [object Object]._onTimeout (/Users/Baggio/project/project_bugs/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1812:23)
Question: Is the fact that this error is thrown because the page is forever stuck loading, and the async call never runs? Or could it be that angular doesn't load? Or is it something else?
I'm quite sure protractor loads because I outputted it in the console, it it enumerates all of the object properties.
And as an extension of question 1, here's the next question I don't understand.
Question 2
If I try running this test with browser.driver.get(), I get the exact same error message:
1) teacher list has teacher
Message:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Stack:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
at [object Object]._onTimeout (/Users/Baggio/project/project_bugs/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1812:23)
So it shouldn't have anything to do with the page loading or not.
What I tried
I tried, after calling initiateDebug() in interactive debug mode (entering the exact same line proceeding the first breakpoint, and I got this:
wd-debug> repl
> element.all(by.repeater("teacher in items track by $index"))
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
F
If I read this correct (https://github.com/angular/protractor/blob/master/docs/debugging.md), entering a protractor command should yield something, but here, it just resumed the control flow outside debug mode, failing the Jasmine spec.
I'll present the relevant code here that:
The html the element.all line is supposed to grab from
<section ng-if="staFlag" class="teacher-item yes-cursor cursor-over" ng-repeat="teacher in items track by $index">
Question 2: Why does Protractor fail here?
Question 3
What I've tried as well - thinking that this is a case where Protractor might not be able to find Angular, defining the rootElement property in protractor_conf.js, (adding ng-app='kp' in the root html file, and rootElement: 'html' in the config file) but it returns yet the exact same error message:
1) teacher list has teacher
Message:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Stack:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
at [object Object]._onTimeout (/Users/Baggio/project/project_bugs/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1812:23)
Message:
Thought 3: Can this effectively prove that this isn't a problem where Protractor can't find Angualr?
Additional information
I have to run this test on the same server where the website is running locally, because for some reason if this starts on a different server, the website doesn't work at all. By "doesn't work", I mean: the page loads correctly, but all bits that require data to be fetched from the backend via Restangular don't show.
Short version question
What exactly is preventing this Protractor test from running correctly? I think I'm missing something very obvious - but I've been looking at this for hours and I haven't come up with any ideas.
I'd really appreciate pointers to the right direction, or a solution if possible - thank you very much.

How to keep protractor running?

I'm trying to access Db in protractor tests using a sql server driver for NodeJs (protractor is a nodejs application so this is no problem)
The idea is to check Db data in our e2e tests:
We can check whether some hidden things are written correctly in the Db that cannot be seen on the UI (e.x Logs,..)
We can isolate features in our e2e testing: we don't rely on another feature to display the data to check whether the feature writing the data works correctly.
The problem I'm having is whenever protractor finishes interacting with the browser, it will terminate. Therefore, my code to access the Db cannot verify the data retrieved (e.x expect(dataFromDb).toEqual('foo')) because requests to Db are asynchronous in NodeJs.
At the time when I retrieve the data via the callback, protractor has been terminated.
It looks to me that protractor is only aware of web browser promises and terminates when there are no outstanding browser promises.
Is there any solution to keeping protractor alive so that I can verify my Db data? Thanks.
Two things to keep in mind.
1) expect(dataFromDb).toEqual('foo')): Protractor wrapped expect to understand promises. However, it only understands webdriver.promise (i.e. no $q or any other promise). If you want to make assertions against non webdriver promises, you have to resolve the promise yourself like:
dataFromDb.then(function(resolvedData) {
expect(resolvedData).toEqual('foo')
})
2) Protractor does not "terminate". Protractor only helps you kick off your test using another test framework (i.e. jasmine, mocha); once it does that it is only a library of tools (i.e. locators, waitForAngular, etc) that you run on top of that test framework. It's that other framework you must prevent from terminating. I don't know what framework you're using, but I'll use jasmine as an example:
it('call db', function(done) { //notice the inclusion of `done`
browser.get('something'); //this is protractor
element(by.xyz).click(); //this is protractor
var data = queryDatabase(); // you must tell jasmine to wait for this.
data.then(function(resolvedData) {
expect(resolvedData).toBe('foo');
done(); // tell jasmine you're done.
})
})
Side note, protractor patched jasmine it to wait for webdriver commands to finish (just like how it patched expect) for user's convenience. However, if you don't use webdriver's promise you need to tell it when the test is done via the done callback

Categories

Resources