how to use Protractor on non angularjs website? - javascript

I have found Protractor framework which is made for AngularJS web applications.
How can I use Protractor on a website which is not using AngularJS?
I wrote my first test and Protractor triggers this message:
Error: Angular could not be found on the page https://www.stratexapp.com/ : retries looking for angular exceeded

Another approach is to set browser.ignoreSynchronization = true before browser.get(...). Protractor wouldn't wait for Angular loaded and you could use usual element(...) syntax.
browser.ignoreSynchronization = true;
browser.get('http://localhost:8000/login.html');
element(by.id('username')).sendKeys('Jane');
element(by.id('password')).sendKeys('1234');
element(by.id('clickme')).click();

If your test needs to interact with a non-angular page, access the webdriver instance directly with browser.driver.
Example from Protractor docs
browser.driver.get('http://localhost:8000/login.html');
browser.driver.findElement(by.id('username')).sendKeys('Jane');
browser.driver.findElement(by.id('password')).sendKeys('1234');
browser.driver.findElement(by.id('clickme')).click();

waitForAngular should now be used instead of the deprecated ignoreSynchronization property.
The following waitForAngular guidance is taken from the Protractor docs for timeouts:
How to disable waiting for Angular
If you need to navigate to a page which does not use Angular, you can turn off waiting for Angular by setting `browser.waitForAngularEnabled(false). For example:
browser.waitForAngularEnabled(false);
browser.get('/non-angular-login-page.html');
element(by.id('username')).sendKeys('Jane');
element(by.id('password')).sendKeys('1234');
element(by.id('clickme')).click();
browser.waitForAngularEnabled(true);
browser.get('/page-containing-angular.html');

Forget about ignoreSynchronization!!! It no longer exists in protractor starting from protractor 5.3.0
browser.waitForAngularEnabled(false) should be used instead
Step by step instructions how to use it
Protractor has builtin handling of waiting for angular and this is what makes it different. However, if the page is not angular protractor will hang until it hits timeout error, so you need to disable that feature in order to test non-angular page. There is also an edge case, when the page is angular, but this feature will STILL make protractor error out. Perform the steps below, to find out when to disable protractor's waiting for angular
Find out if your page is Angular: open dev console in the browser and inside of 'console' tab run command
getAllAngularTestabilities()
If the output is getAllAngularTestabilities is not defined, then your page is not angular, go to the last step to disable built-in waiting
Run these commands in the console
getAllAngularTestabilities()[0]._ngZone.hasPendingMacrotasks
getAllAngularTestabilities()[0]._ngZone.hasPendingMicrotasks
If any of these return true (if there are micro or macro tasks pending), then go to the last step. If all are false, congrats, you can use the builtin protractor's waiting for angular. But if you don't like it as I don't, then read the last step to find out how to disable it
Run the above mentioned command. BUT! It returns a promise, which needs to be handled, preferably using await keyword
await browser.waitForAngularEnabled(false)
This command can be ran anywhere: in the it block, in beforeAll, or in onPrepare of your configuration. Just make sure, if you use await to make the respective block async
beforeAll(async () => await browser.waitForAngularEnabled(false))

To test on non angular site, you should remove the synchronisation. for that use the following:
browser.ignoreSynchronisation = true;
browser.get('url');

In some occasions, to avoid errors need to add both values .
browser.driver.ignoreSynchronization = true;
browser.waitForAngularEnabled(false);
You can add them in spec.js file .
describe('My first non angular class', function() {
it ('My function', function() {
browser.driver.ignoreSynchronization = true;
browser.waitForAngularEnabled(false);
Or as #Mridul Suggested ,add them in config.js file .
exports.config = {
directConnect: true,
framework: 'jasmine',
onPrepare: function () {
browser.driver.ignoreSynchronization = true;// for non-angular set true. default value is false
browser.waitForAngularEnabled(false); // for non-angular set false. default value is true
},

Personally, I didn't get any success with proposed solutions as the DOM elements weren't properly loaded in time.
I tried many ways of handling that asynchronous behavior, including browser.wait with browser.isElementPresent, but none of them were satisfying.
What did the trick is using Protractor returned Promises from its methods in onPrepare :
onPrepare: () => {
browser.manage().window().maximize();
browser.waitForAngularEnabled(true).then(function () {
return browser.driver.get(baseUrl + '/auth/');
}).then(function () {
return browser.driver.findElement(by.name('login')).sendKeys('login');
}).then(function () {
return browser.driver.findElement(by.name('password')).sendKeys('password');
}).then(function () {
return browser.driver.findElement(by.name('submit')).click();
}).then(function () {
return true;
});
return browser.driver.wait(function () {
return browser.driver.getCurrentUrl().then(function (url) {
return /application/.test(url);
});
}, 10000);
},
I was inspired by https://github.com/angular/protractor/blob/master/spec/withLoginConf.js

add the below snippet in your .js spec file
beforeAll(function() {
browser.waitForAngularEnabled(false);
});

Add the following piece of code in the conf.js file
onPrepare: function () {
browser.ignoreSynchronization = true;
}

Add below snippet for non angular applications:
app- browser.ignoreSynchronization = true;

Use below snippet in your config.js file for non-angular applications-
browser.ignoreSynchronization = true;
and for angular application -
browser.ignoreSynchronization = false;

I am working on aurelia web-app which is a FE framework similar to Angular , React. In this I am using Protractor for automation.
Tech Stack which of my project:-
Protractor
Typescript
Page Object Modal
Cucumber
Chai
node
npm
VS Code (IDE)
The main change happens only in the configuration file, I can add code in github if that would help, here is the config file I am using in my project which works perfect for me.
Posted some blogs as well in my wordpress , hope that can be of help.
const reporter = require('cucumber-html-reporter');
exports.config = {
SELENIUM_PROMISE_MANAGER: false,
directConnect: true,
specs: ["./e2e/features/*/EndToEnd.feature"],
format: 'json:cucumberReport.json',
framework: 'custom',
frameworkPath: require.resolve('protractor-cucumber-framework'),
cucumberOpts: {
strict: true,
format: 'json:cucumberReport.json',
keepAlive: false,
require: [
'./e2e/hooks/*.ts',
'./e2e/stepDefinition/*/*.ts',
],
tags: '#Regression'
},
beforeLaunch: function () {
require('ts-node/register')
},
onPrepare: async () => {
await browser.waitForAngularEnabled(false);
await browser.ignoreSynchronization == true;
await browser.manage().window().maximize();
await browser.manage().timeouts().implicitlyWait(10000);
},
onComplete: async () => {
var options = {
theme: 'bootstrap',
jsonFile: './reports/cucumberReport.json',
output: './reports/cucumberReport.html',
reportSuiteAsScenarios: true,
launchReport: false,
screenshotsDirectory: './reports/screenshots',
storeScreenshots: true,
metadata: {
"Test Environment": "SAND-DEV-1",
"Platform": "Windows 10",
}
};
reporter.generate(options);
},
};

Instead of Protractor, you can use for e2e testing the Testcafe.
Pros:
ES2016 syntax
no need an additional dependencies, configs and browser plugins
flexible selectors
easy setup

Related

Running javascript e2e tests on a local appium server

I'm wanting to run e2e tests written in javascript with mocha on an Appium server instance running a local android emulator. The app on test is an apk originally written in react-native.
On Windows I have the server up and running with an Android Studio emulator through using the Appium desktop app. The server all looks good and has the apk of the native app I want to test working fine. I also have a basic describe/assert test written in mocha that I want to apply to the app.
My question is what do I need to include (presumably in the test file) to make the tests actually test the emulator application? I'm finding the documentation pretty confusing and the sample code seems pretty specific to a different use case.
Many thanks for your help!
There are at least 2 good js client libraries to use for Appium based project: webdriverio and wd. Personally, I'm using the second one so I can advice you how write tests with it and mocha:
my test file looks like this:
'use strict'
require(path.resolve('hooks', 'hooks'))
describe('Suite name', function () {
before('Start new auction', async function () {
//do before all the tests in this file, e.g. generate test data
})
after('Cancel auction', async function () {
//do after all the tests in this file, e.g. remove test data
})
it('test1', async () => {
// test steps and checks are here
})
it('test2', async () => {
// test steps and checks are here
})
it('test3', async () => {
// test steps and checks are here
})
})
where hooks.js contains global before/after for all the tests:
const hooks = {}
before(async () => {
// before all the tests, e.g. start Appium session
})
after(async () => {
// after all the tests, e.g. close session
})
beforeEach(async () => {
// before each test, e.g. restart app
})
afterEach(async function () {
// e.g. take screenshot if test failed
})
module.exports = hooks
I'm not saying its the best practice of designing tests, but its one of multiple ways.
Cool so I managed to get it working to a degree. I was checking through the Appium console logs as I was trying to run stuff and noticed that the session id was missing from my requests. All that was needed was to attach the driver using the session id. My code looks a bit like this:
"use strict";
var wd = require("wd")
var assert = require("assert")
var serverConfig = {
host: "localhost",
port: 4723,
}
var driver = wd.remote(serverConfig)
driver.attach("0864a299-dd7a-4b2d-b3a0-e66226817761", function() {
it("should be true", function() {
const action = new wd.TouchAction()
action
.press({x: 210, y: 130})
.wait(3000)
.release()
driver.performTouchAction(action)
assert.equal(true, true)
})
})
The equals true assert is just there as a placeholder sanity check. The only problem with this currently is that I'm copy-pasting the alpha-numeric session id inside the attach method each time I restart the Appium server so I need to find a way to automate that.

Testing Visual Studio Code Extension with Spectron - How to determine when VSCode is ready?

Goal: Perfom real end-to-end tests for a VSCode extension using Spectron.
As an example I installed the vim extension.
I adapted the usage example from Spectron's README like this:
var Application = require('spectron').Application
var assert = require('assert')
describe('VSCode extension', function () {
this.timeout(10000)
beforeEach(function () {
this.app = new Application({
path: '.vscode-test/VSCode-linux-x64/bin/code',
args: [
'--extensionDevelopmentPath=' + process.cwd(),
'--locale=en',
process.cwd(),
],
requireName: 'nodeRequire',
})
return this.app.start()
})
afterEach(function () {
if (this.app && this.app.isRunning()) {
return this.app.stop()
}
})
it('suggest commands', function () {
return this.app.client
.waitUntilWindowLoaded()
//.pause(5000)
//.waitUntilTextExists('span', 'OPEN EDITORS', 10000)
.keys('F1')
.waitForVisible('.quick-open-widget:not(.hidden)')
.keys('vim')
.waitForVisible('.quick-open-entry*=Vim: Show Command Line')
})
})
Problem: How to exactly determine if VSCode is ready.
Calling client.waitUntilWindowLoaded() is not sufficient. In some test runs entering text via client.keys(...) into the Command Palette (F1) does not suggest any commands.
I don't want to use pause(...) after waitUntilWindowLoaded() as it wastes useful time and may still not be sufficient when the system is under heavy load.
For the moment I just came up with .waitUntilTextExists('span', 'OPEN EDITORS', 10000) which seems to work most of the time. Sometimes it runs into the timeout.
Is there anything more reliable (in the DOM) that is set by VSCode and can be checked by Spectron that states that VSCode is really ready?

protractor + Angular + requireJs

I want to test my app with protracor but test fails with this errors :
Failed: Error while waiting for Protractor to sync with the page: "root element (html) has no injector. this may mean it is not inside ng-app."
it seems that angular doesn't load completely, and browser.waitForAngular(); not working.
how can I setup protractor to continue test after RequireJs load dependencies ?
also adding this :
onPrepare:function(){
browser.manage().timeouts().pageLoadTimeout(40000);
browser.manage().timeouts().implicitlyWait(25000);
}
to ocnfig file(As mentioned here) cause this error:
Failed: Error while waiting for Protractor to sync with the page: "angular could not be found on the window"
You will need a manual way to know that Angular has bootstrapped from within your specs. Here's the basic run-down of how I have this set up with Angular, RequireJS and Protractor. This works for me with jasmine2 and old jasmine.
We want to add a class of ng-app to the element that Angular bootstraps. For example:
index.html
<html lang="en" class="ng-app">
But rather than putting this in the HTML file, we want to add the class using the same RequireJS module that is manually bootstrapping your Angular App. Eg:
ng-bootstrap.js
require(['angular'], function (angular, otherdeps) {
// Start the Angular App
angular.bootstrap(document, ['MyApp']);
// Set the ng-app class for Angular Protractor tests
var root = document.documentElement;
angular.element(root).addClass('ng-app');
});
Check that your page adds this class after bootstrapping. then set up your protractor.conf exports to run the onprepare test. This spec is executed each time Protractor is launched and we will use it to check for the class you added in the ng-bootstrap.js module.
protractor-conf.js
exports.config = {
// Every time protractor is launched:
onPrepare: 'onprepare.e2e.js',
};
In your onprepare.e2e.js spec file, you can trigger the load of the home page. Then ask Protractor to wait until the class .ng-app is found on the root element, Ie: Angular has bootstrapped and is ready to run Protractor tests.
onprepare.e2e.js
describe("On prepare", function () {
// Replace with your own URL
var baseUrl = 'http://127.0.0.1:8001/#/';
// Begin fetching the page
browser.driver.get(baseUrl);
// Wait until `.ng-app` is found on the root elem
browser.driver.wait(function () {
return browser.driver.getCurrentUrl().then(function (url) {
return browser.driver.isElementPresent(by.className('ng-app')).then(function () {
return true;
});
});
});
});
Keep in mind that if you a running lots of spec files together, your page could is being re-loaded when a new test starts. Your page also may be being reloaded if your Angular router is using a reload: true param.
This means that the app has to bootstrap again; And you will need to wait for the bootstrap class again before you can use Protractor.
Add a helper for this and include it in your protractor-conf.js.
helpers.js
module.exports = {
get: function (url) {
browser.driver.get(url);
browser.driver.wait(function () {
return browser.driver.getCurrentUrl().then(function (url) {
return browser.driver.isElementPresent(by.className('ng-app')).then(function () {
return true;
});
});
});
},
};
protractor-conf.js
helpers = require('helpers.js');
exports.config = {
onPrepare: 'onprepare.e2e.js',
specs: [
'my-spec.js'
]
};
Now your helper is globally visible to your specs and you can use your new helper.get(url) method instead of browser.driver.get(url). Example:
my-spec.js
describe("Users", function() {
it('should login', function () {
// Wait for Angular and RequireJS to finish
helpers.get('http://127.0.0.1:8001/#/login');
// ...tests here...
});
});
I had some similar problem, maybe it is because the way our app is loaded, but you can try having some custom wait:
browser.driver.wait(function() {
return browser.driver.isElementPresent(by.css('.ng-scope'));
}, 50000);// ^^or some other locator for your angular
});
inside your beforeEach() for example.
Edit:
Also for someone it helps to change browser windows size:
browser.manage().window().setSize(1280, 1024);
in onPrepare()
I can run test by adding browser.driver.sleep(3000) to beforeEach.
but this isn't the right solution.

Setting up Screenshot Reporter for Protractor

Since I'm a newbie with automated tests and protractor, I'm having some trouble setting this up in my tests.
According to the guide, every time that I create a new instance of screenshot reporter, I have to pass a directory path. Right, this means that every time I create a new instance in my spec file?
Also, there are functions to take screenshots of my skipped and my failed tests. Where i supposed to use takeScreenShotsForSkippedSpecs and takeScreenShotsOnlyForFailedSpecs? In my config file?
This is my onPrepare:
onPrepare: function () {
browser.driver.manage().window().maximize();
global.dvr = browser.driver;
global.isAngularSite = function (flag) {
browser.ignoreSynchronization = !flag;
}
jasmine.getEnv().addReporter(new ScreenShotReporter({
baseDirectory: '/tmp/screenshots',
takeScreenShotsForSkippedSpecs: true,
takeScreenShotsOnlyForFailedSpecs: true
}));
Note: If you are using jasmine2, use protractor-jasmine2-screenshot-reporter.
For jasmine1:
I've been using successfully using protractor-html-screenshot-reporterpackage. It is based on protractor-screenshot-reporter, but also provides a nice HTML report.
Here is what I have in the protractor config:
var HtmlReporter = require("protractor-html-screenshot-reporter");
exports.config = {
...
onPrepare: function () {
// screenshot reporter
jasmine.getEnv().addReporter(new HtmlReporter({
baseDirectory: "test-results/screenshots"
}));
},
...
}
After running tests, you would get an HTML file containing (example):
You can click "view" to see the test-case specific screenshot in the browser.
The readme in the library is pretty self explanatory. After installing the library, add it onto protractor's onPrepare in your protractor config file.
i.e.
protractorConf.js:
var ScreenShotReporter = require('protractor-screenshot-reporter');
exports.config = {
// your config here ...
onPrepare: function() {
// Add a screenshot reporter and store screenshots to `/tmp/screnshots`:
jasmine.getEnv().addReporter(new ScreenShotReporter({
baseDirectory: '/tmp/screenshots',
takeScreenShotsForSkippedSpecs: true
}));
}
}
then protractor protractorConf.js to run protractor.
Just recently I published a brand new plugin called protractor-screenshoter-plugin that captures for each browser instance a screenshot and console logs. The snapshot is made optionally for each expect or spec. It comes with a beautiful angular and bootstrap based analytics tool to visually check and fix tests results.
Check it out at https://github.com/azachar/protractor-screenshoter-plugin.
Also, I created a list of all available alternatives, so if you find something else, please do not hesitate to add it there.

How to reuse code in Protractor / AngularJS Testing

We have several Protractor end to end tests for our AngularJS app in several JS files and they work great. But, there is a lot of duplicated code throughout the tests and we would like to DRY that up.
For instance, every time we log in, we have to click on the text elements, type in the username and password, and then click enter. And right now every single JS file has its own copy of the login function which is called before every test.
It would be nice to refactor those out into modules that we can then import. I have been searching for hours, but not found a good solution.
How should we do this?
You can create nodejs modules and include them in protractor configuration
login-helpers.js
exports.loginToPage = function () {
//nodejs code to login
};
protractor.conf.js
exports.config = {
//...
onPrepare: function () {
protractor.loginHelpers = require('./helpers/login-helpers.js');
}
//...
};
page.spec.js
it('should do smth', () => {
protractor.loginHelpers.loginToPage()
//expect(...).toBe(...);
});
Our team uses Orchid-js with Jasmine and Protractor, it's designed to do exactly this.
Your test
Describe('Login user',require('../login.js'))("username","password");
login.js
module.exports = function(username,password){
describe('login the user',function(){
it('should login the user',function(){
element(by.id('usernameField')).sendKeys(username);
element(by.id('passwordField')).sendKeys(password);
element(by.id('loginButton')).click();
});
});
}

Categories