Jasmine Controller test: $timeout.flush() causes an 'Unexpected GET request' error - angularjs

I have the following test:
it('should load productGroups into the scope', function(){
scope._section = 'modifiers';
scope.userData = {method: 'manual'};
scope.$digest();
timeout.flush();//causes the error
expect(scope.productGroups).toEqual(productGroupService.getProductGroups());
});
Now, the action that I am trying to test occurs within a timeout of 0, because of some issues I had with syncing the data stored in the cookies.
Now, without the marked line, the test runs find, except the expected result is not obtained.
With the marked line, I get the following error:
Error: Unexpected request: GET views/main.html
No more request expected
at $httpBackend (/home/oleg/dev/vita-webapp-new/bower_components/angular-mocks/angular-mocks.js:1178:9)
at sendReq (/home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:8180:9)
at $http.serverRequest (/home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:7921:16)
at wrappedCallback (/home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:11319:81)
at wrappedCallback (/home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:11319:81)
at /home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:11405:26
at Scope.$eval (/home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:12412:28)
at Scope.$digest (/home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:12224:31)
at Scope.$apply (/home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:12516:24)
at Object.fn (/home/oleg/dev/vita-webapp-new/bower_components/angular/angular.js:14023:36)
Process finished with exit code 0
main.html, is obviously the view of this controller, tried placing it in templateCache with the following code, but it did not help:
$templateCache.put('views/main.html', $templateCache.get('app/views/views/main.html'));
Any assistance would be much appreciated.

Solution I've been able to come up with is:
$httpBackend.when('GET', 'views/main.html').respond('');
in the beforeEach function
Nothing else doesn't seem to work.
While it's a working solution for unit-testing purposes, it would never work for an E2E test.

Related

How to fix the below test case when i am migrating from angularjs 1.6.9 to 1.7.8

i am trying to migrate my angularjs application from v1.6.9 to v1.7.8. my test case are failing with
angular-mock.js($rootScope)
ERROR: No response Defined! in angularjs/angular-mocks.js(1607)
ERROR:$rootScope:INPROG
since i am new to jasmine karma testing i have tried with $rootscope.flush(). but i am not able to resolve it .
this is my first unit testing with Angularjs and jasmine karma, not able to find the exact reason and i have gone through the release notes of the other releases as well.
with $rootScope i found that they have modified its scope to the function not generic.
Below is my spec.js:
I am getting error with $rootscope,flush(). could some please help me out to resolve this issue as it is required.
$httpBackend.when('GET','/application/api/prefernce-definition')
.respond(successResponsePrefernces);
$httpBackend.when('POST','/application/api/setprefernce-definition')
.respond(201);
$httpBackend.when('POST','/application/api/v1/set-prefernces');
$httpBackend.when('POST','/application/api/v2/balances')
.respond(accountApiResponse);
//assertion for expecting proper get request for preferences-definition
$httpBackend.expectGET(,'/application/api/v1/prefernce-prefernces');
//fetch and set prefernces
$scope.onclickSetting();
$rootScope.flush();
$timeout.flush();
//now save the settings
$scope.saveViewSettings();
// getting error in this line after the flush
$rootScope.flush();
$timeout.flush();
var viewMode = $scope.model.viewSettings.viewMode,
viewModeExpected = successResponsePreferences.preferenceDefinitionList[0];
expect(viewMode.default).toBe(viewModeExpected.defaultPreferenceValue);
expect(viewMode.selected).toBe(viewModeExpected.currentUserPreferenceValue);
expect($scope.$broadcast).toHaveBeenCalledWith("ShowFilter");
//should update the prefernces in cache
expect(viewMode.selected).toBe(cacheService.getPreferences().viewMode);
expect(analyticsService.sendAction).toHaveBeenCalledwith(
Constant.ACCOUNT_BALANCES.MODULE_NAME,
Constant.ACCOUNT_BALANCES,VIEW_SETTINGS,
[]
);
expect(analyticsService.sendAction).toHaveBeenCalledwith(
Constant.ACCOUNT_BALANCES.MODULE_NAME,
Constant.ACCOUNT_BALANCES,VIEW_SETTINGS,
[Constant.EXTD_MODE, Constant.NO_GROUPING]
);
});
Below are the errors:
Error: No response defined! In
../bower_components/angular-mocks/angular-mocks.js(line 1607) $httpBackend#/../bower_components/angular-mocks/angular-mocks.js:1607:63.

Locating elements of non angular iframe using protractor's switchTo

I have an angularjs app containing an iframe that display the page allowing to log into another website. I am trying to put some value in the fields contained in the iframe but I can't find the elements using any locator.
Here is my test :
describe("Harvest", function() {
beforeEach(function () {
browser.get('http://localhost:8110/');
expect(browser.getCurrentUrl()).toMatch('_*#/login$');
element(by.model('user.username')).sendKeys('sam');
element(by.model('user.password')).sendKeys('pass');
element(by.id('bt_signin')).click();
});
afterEach(function () {
browser.executeScript('window.sessionStorage.clear();');
});
describe('A user', function () {
beforeEach(function () {
browser.get('http://localhost:8110/');
});
it('should be able to obtain an access token from harvest', function () {
expect(browser.getCurrentUrl()).toMatch('_*#/home$');
//Display and close the window
element(by.id('btHarvest')).click();
expect(element(by.id('modalHarvest')).isPresent()).toBe(true);
element(by.id('btCloseModal')).click();
expect(element(by.id('modalHarvest')).isPresent()).toBe(false);
//Authenticate into harvest
element(by.id('btHarvest')).click();
expect(element(by.id('modalHarvest')).isPresent()).toBe(true);
browser.switchTo().frame('iframeHarvest');
//It fails here with a null exceptions, guess it can't find it
element(by.id('email')).sendKeys(browser.params.harvestLogins.user);
element(by.id('user_password')).sendKeys(browser.params.harvestLogins.password);
element(by.id('sign-in-button')).click();
expect(element(by.name('commit')).isPresent()).toBe(true);
browser.driver.switchTo().defaultContent();
});
});
});
And here is the exception generated
1) Harvest A user should be able to obtain an access token from harvest
Message:
[31mError: Error while waiting for Protractor to sync with the page: {}[0m
Stacktrace:
Error: Error while waiting for Protractor to sync with the page: {}
at Error (<anonymous>)
==== async task ====
WebDriver.executeScript()
at null.<anonymous> (/Users/samdurand/workspace/cake/subbie/src/test/web/js/features/harvest.js:45:22)
==== async task ====
Asynchronous test function: it()
Error
at null.<anonymous> (/Users/samdurand/workspace/cake/subbie/src/test/web/js/features/harvest.js:26:5)
at null.<anonymous> (/Users/samdurand/workspace/cake/subbie/src/test/web/js/features/harvest.js:20:3)
at Object.<anonymous> (/Users/samdurand/workspace/cake/subbie/src/test/web/js/features/harvest.js:1:63)
After correcting the iframe call as suggested I get this error :
1) Projects A user is possible to create a project based on harvest data
Message:
[31mError: Error while waiting for Protractor to sync with the page: {}[0m
Stacktrace:
Error: Error while waiting for Protractor to sync with the page: {}
at Error (<anonymous>)
As discussed, the issue that you are facing is because the iframe that you are switching to is non-angular. Thus, element(by.id) will not work after you switch to that iframe. Instead, findElement needs to be used!
Since yours is an Angular Application, could you please try using:
browser.switchTo().frame('iframeHarvest');
instead of:
browser.driver.switchTo().frame(element(by.id('iframeHarvest')));
I found a solution but I still wait for an explanation from the angular team, because I don't understand why using element fails but using the driver directly succeed. Here is the code that works (I included the suggestion from #Sakshi even thought it doesn't change the result) :
browser.switchTo().frame(element(by.id('iframeHarvest')));
browser.driver.findElement(by.id('email')).sendKeys(browser.params.harvestLogins.user);
edit : here is the protractor subject https://github.com/angular/protractor/issues/1465

AngularJS access to Cbssports REST API

I am trying to create an angularjs application as a cbssports plugin.
They provide a RESTful API.
The following is my $http request:
$http.get(basePath + 'league/owners?access_token=' + cbssportsTokens['access_token'] + '&response_format=JSON')
.success(function(data) {
return data;
});
That is wrapped up in a factory and used in a controller. As far as I can tell this is getting executed correctly. When I look at the chrome developer tools I can see the following error on my request from the console:
OPTIONS http://api.cbssports.com/fantasy/league/owners?access_token=U2FsdGVkX18Hyd0…J9DrpO7C-OXQQNXGMh0ej0iXVfPf5DkQwkLwSpCqGhipd6HogV_gZ&response_format=JSON angular.js:8560
(anonymous function) angular.js:8560
sendReq angular.js:8354
$http.serverRequest angular.js:8087
wrappedCallback angular.js:11572
wrappedCallback angular.js:11572
(anonymous function) angular.js:11658
Scope.$eval angular.js:12701
Scope.$digest angular.js:12513
Scope.$apply angular.js:12805
done angular.js:8378
completeRequest angular.js:8592
xhr.onreadystatechange
XMLHttpRequest cannot load http://api.cbssports.com/fantasy/league/owners?access_token=U2FsdGVkX18xBod…oQWvjDVSbpZCOVsoIKeVXKRSYdo6dBbIE0rgMWTkWhmgPUTyr_xnS&response_format=JSON. The 'Access-Control-Allow-Origin' header has a value 'https://www.cbssports.com' that is not equal to the supplied origin. Origin 'http://xx.xx.xx.xx:9001' is therefore not allowed access. ?access_token=U2FsdGVkX18xBodWEOfeqys5X4aDpghYrE22FGljlJd_TtKRHlWh4LHWFwVxay95BbAWvn4te1foQWvjDVSbp…:1
When I click the link that it said it couldn't load, it takes me to a new page with the expected output!! So clearly CORS shouldn't be an issue here from the server.
I have read many different issues with CORS about changing the headers. The follow is how I have set it up:
$httpProvider.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
$httpProvider.defaults.headers.common['Access-Control-Allow-Headers'] = 'Origin';
Any advice would be greatly appreciated, also my first post to SO, so please let me know if more info would help.
So I didn't exactly get it to work. But I found a sort of workaround.
function _get(url) {
return $resource(basePath + url, {
access_token: cbssportsTokens['access_token'],
response_format: 'JSON',
callback: 'JSON_CALLBACK'
}, {
get: {
method: 'JSONP'
}
});
}
I went ahead and used a JSONP request instead of a GET, the cbs server seems to be happier with that.

Getting error: Error while waiting for Protractor to sync with the page: {}

My e2e.conf.coffee file is:
exports.config =
baseUrl: 'http://localhost:9001'
specs: [
'e2e/**/*.coffee'
]
framework: 'jasmine'
I have my node project running and listening on port 9001.
My test is:
describe 'Happy Path', ->
it 'should show the login page', ->
console.log browser
expect(browser.getLocationAbsUrl()).toMatch("/view1");
it 'should fail to login', ->
setTimeout ->
console.log "FAIL!"
, 1200
And the error that I get is:
Failures:
1) Happy Path should show the login page
Message:
Error: Error while waiting for Protractor to sync with the page: {}
Stacktrace:
Error: Error while waiting for Protractor to sync with the page: {}
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
==== async task ====
WebDriver.executeScript()
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
==== async task ====
Asynchronous test function: it("should show the login page")
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>
at <anonymous>==== async task ====
What am I doing wrong??
The (very) short version: use browser.driver.get instead of browser.get.
The longer version: Protractor is basically a wrapper around Selenium and its Javascript WebDriver code. Protractor adds code to wait for Angular to "settle down" (i.e., finish going through its $digest loops) before proceeding with your test code. However, if your page doesn't have Angular on it, then Protractor will wait "forever" (actually just until it times out) waiting for Angular to settle.
The browser object that Protractor exposes to your test is an instance of Protractor (i.e., if you see old answers on Stack Overflow with var ptor = protractor.getInstance(); ptor.doSomething(), then you can replace ptor with browser in those old answers). Protractor also exposes the wrapped Selenium WebDriver API as browser.driver. So if you call browser.get, you're using Protractor (and it will wait for Angular to settle down), but if you call browser.driver.get, you're using Selenium (which does not know about Angular).
Most of the time, you'll be testing Angular pages, so you'll want to use browser.get to get the benefits of Protractor. But if your login page doesn't use Angular at all, then you should be using browser.driver.get instead of browser.get in the tests that test your login page. Do note that you'll also need to use the Selenium API rather than the Protractor API in the rest of the test: for example, if you have an HTML input element with id="username" somewhere in your page, you'll want to access it with browser.driver.findElement(by.id('username')) instead of element(by.model('username')).
For more examples, see this example from the Protractor test suite (or try this link if the previous one ever goes away). See also the Protractor docs which state:
Protractor will fail when it cannot find the Angular library on a page. If your test needs to interact with a non-angular page, access the webdriver instance directly with browser.driver.
Example code: In your login test above, you would want to do something like:
describe 'Logging in', ->
it 'should show the login page', ->
browser.driver.get "http://my.site/login.html"
// Wait for a specific element to appear before moving on
browser.driver.wait ->
browser.driver.isElementPresent(by.id("username"))
, 1200
expect(browser.driver.getCurrentUrl()).toMatch("/login.html");
it 'should login', ->
// We're still on the login page after running the previous test
browser.driver.findElement(by.id("username")).sendKeys("some_username")
browser.driver.findElement(by.id("password")).sendKeys("some_password")
browser.driver.findElement(by.xpath('//input[#type="submit"]')).click()
(A note of caution: I haven't done much CoffeeScript, and it's entirely possible I made a CoffeeScript syntax error in the code above. You may want to check its syntax before blindly copying and pasting it. I am, however, confident in the logic, because that's copied and pasted almost verbatim from my Javascript code that tests a non-Angular login page.)
If you need to be using browser.get and are getting this error,
the issue is most likely to be the rootElement property in protractor config file.
By default Protractor assumes that the ng-app declaration sits on the BODY-element. However, in our case, it could be declared somewhere else in the DOM. So we have to assign the selector of that element to the rootElement property:
// rootElement: 'body', // default, but does not work in my case
rootElement: '.my-app', // or whatever selector the ng-app element has
Corresponding HTML:
<div ng-app="myApp" class="my-app">
I copied the answer from http://www.tomgreuter.nl/tech/2014/03/timing-errors-with-angular-protractor-testing/
I got this error, too, but in my case my tests were working and this error occured after expanding them.
Turns out that this error may occur, if you try to call getText() in your describe block instead of your testcases. My Set-Up was like following:
describe('Test Edit Functionality', function() {
var testEntry = $$('.list-entry').first(),
testEntryOldName = testEntry.getText();
it('Should keep old name if edit is aborted', [...]);
});
That caused the Error Error while waiting for Protractor to sync with the page: {}.
I fixed it by moving the assigment into a beforeEach-block
describe('Test Delete Functionality', function() {
var testEntry = $$('.list-entry').first(),
testEntryOldName;
beforeEach(function() {
testEntryOldName = testEntry.getText();
});
});
Or, may be better, assign it in the specific testcases you need this value (if you dont need it in all).
Not sure where I picked up the pieces that put this answer at this point, but this is what works for me:
Add class='ng-app' to the element that contains your app.
<div ng-app="myApp" ng-controller="myController" class="ng-app"></div>
Add rootElement to your protractor.conf.
exports.config = {
specs: ['your-spec.js'],
rootElement: ".ng-app"
};
Use browser.driver.get not browser.get.
describe('foobar element', function() {
it('should be "baz" after view is initialized', function() {
browser.driver.get('http://localhost/view');
var inputBox = $('input[name=foobar]');
expect(inputBox.getAttribute('value')).toEqual("baz");
});
});
I got this issue a couple of days back on my CT. My tests were running fine until 3 days back, but then this protractor sync error showed up and just won't go away with any of the solutions/hacks provided here and at https://github.com/angular/protractor/issues/2643.
I checked that this issue only occurred in Firefox headless and worked fine with Chrome headless. Upgrading from Firefox v69 to the latest(v72 currently) fixed the issue. I do not know why the issue started manifesting itself and how it got fixed with the upgrade, but for what it is worth I thought this information might come in handy for someone else.

Protractor: Invalid locator error with isElementPresent

I have the following simple line of code in my e2e test...
var promise = ptor.isElementPresent(element(by.binding('firstName')));
I get an error that says TypeError: Invalid locator. I don't see many other people getting this error after googling it. Where did I make an error?
I believe the syntax is(remove element(...)...
var promise = ptor.isElementPresent(by.binding('firstName'));

Resources