Error : profile.getTemplateDir is not a function - selenium

I'm trying to implement a multi-browser test with protractor with firefox and chrome. But for the test, I need to change the download path. In chrome, it works but in firefox, the solution I found involved changing the profile of firefox. I also found a way to do it as in the answers of this question Set firefox profile protractor .
But when I try to run multiple instance of navigator alongside of one instance of firefox with modified profile, I always get an error as profile.getTemplateDir is not a function.
Here is the code in my protractor configuration file :
var q = require('q');
var FirefoxProfile = require("selenium-webdriver/firefox").Profile
function getProfiles() {
let deferred = q.defer();
let multiCapabilities = [{
browserName: 'chrome'
}]
deferred.resolve(multiCapabilities);
let firefoxProfile = new FirefoxProfile();
firefoxProfile.setPreference("browser.download.folderList", 2);
firefoxProfile.setPreference("browser.download.manager.showWhenStarting", false);
firefoxProfile.setPreference("browser.download.dir", '/tmp');
let foxCapabilities = {
browserName: 'firefox',
firefox_profile: firefoxProfile
};
multiCapabilities.push(foxCapabilities);
deferred.resolve(multiCapabilities);
return deferred.promise;
}
exports.config = {
seleniumAddress: "http://localhost:4444/wd/hub",
specs: [
'spec.js'
],
framework: 'jasmine2',
getMultiCapabilities: getProfiles,
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
},
};
Does anyone knows why this error is triggered and how to resolve it ?
Thanks

So I have digged into this issue debugging the code and this seems to be limitation/bug currently in selenium web-driver
When getMultiCapabilities returns multiple browsers, then protractor creates a fork of the process to run the test
This forked process is then sent the profile you created as JSON
The childprocess can only receive JSON data in form of text and not in form of a Profile object
Later createGeckodriver function is called with profile object. But as the code of createGeckodriver it is always expecting a profile object
So this code needs to be fixed in Selenium webdriver itself and it should check if the profile sent is already encoded or not. Also the code you have used needs to be fixed to encode the profile. Below is what would work once Selenium driver is patched to allow sending encoded profile
var firefox = require('selenium-webdriver/firefox');
var q = require('q');
var FirefoxProfile = require("selenium-webdriver/firefox").Profile
var makeFirefoxProfile = function (preferenceMap) {
var deferred = q.defer();
var firefoxProfile = new FirefoxProfile();
for (var key in preferenceMap) {
firefoxProfile.setPreference(key, preferenceMap[key]);
}
firefoxProfile.encode().then(function(encodedProfile){
cap = {
"browserName": "firefox",
marionette: true,
"firefox_profile": encodedProfile,
};
deferred.resolve(cap);
});
return deferred.promise;
};
exports.config = {
seleniumAddress: "http://localhost:4444/wd/hub",
specs: [
'spec.js'
],
framework: 'jasmine',
getMultiCapabilities: function () {
return q.all([
{
browserName: 'chrome'
},
makeFirefoxProfile(
{
"browser.download.folderList": 2,
"browser.download.dir": "/path/to/save/downloads",
"browser.helperApps.neverAsk.saveToDisk": "application/zip"
}
)
]);
},
jasmineNodeOpts: {
defaultTimeoutInterval: 180000
}
};

Related

How to run the UI script in Headless mode for edge browser

I have an scenario where I want to trigger the UI script in Microsoft Edge with headless mode. How to achieve this?
* configure driver = { type: 'msedgedriver', webDriverSession: { capabilities: { browserName: 'edge' } }, executable: '#(executable)' }
Have you tried passing the headless flag inside the capabilities? Something like this
* configure driver = { type: 'msedgedriver', webDriverSession: { capabilities: { browserName: 'edge', headless: true } }, executable: '#(executable)'}
Taken from the documentation mentioned here.

Selenium node JavaScript disable downloads

Hi my selenium chromedriver in node automatically downloads files.
I want to disable automatically downloads,
therefor I found many threads for python with a solution to set download_restrictions to 3, but in JS is no experimental settings I guess, and userPreferences seems to be the wrong place.
Someone know how to disable automatically downloads in a headless chrome in node.
Src:
Disable all downloads with ChromeDriver and Selenium
https://chromeenterprise.google/policies/?policy=DownloadRestrictions
https://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/pref_names.cc?view=markup
my example code, download directory seems to be working
import { Builder, Capabilities } from 'selenium-webdriver';
import * as Chrome from 'selenium-webdriver/chrome.js'
export async function getDriver() {
const service = new Chrome.ServiceBuilder("./selenium/chromedriver");
const chromeOpts = await new Chrome.Options()
.addArguments("--disable-infobars")
.addArguments("--headless")
.setUserPreferences({
"download_restrictions" : 3
})
const driver = new Builder()
.withCapabilities(Capabilities.chrome())
.setChromeService(service)
.setChromeOptions(chromeOpts)
.build();
return driver;
}
const driver = await getDriver()
driver.get("http://www.africau.edu/images/default/sample.pdf")
As per the selenium webdriver js's documentation found here.
Sets the user preferences for Chrome's user profile. See the "Preferences" file in Chrome's user data directory for examples.
If we have to block all downloads, then going by the python example shared above. You need to set the chrome options as below :
const chromeOpts = await new Chrome.Options()
.addArguments("--disable-infobars")
.setUserPreferences({
"download_restrictions" : 3
})
I tried this for my case and I found this configuration to be working. Below are the logs that I found during the test execution :
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
[1615982301.751][INFO]: [0ecc2308b910c768a3c91d84cdacbdd8] COMMAND InitSession {
"capabilities": {
"alwaysMatch": {
"browserName": "chrome",
"goog:chromeOptions": {
"args": [ "--disable-infobars" ],
"prefs": {
"download_restrictions": 3
}
}
}
},
"desiredCapabilities": {
"browserName": "chrome",
"goog:chromeOptions": {
"args": [ "--disable-infobars" ],
"prefs": {
"download_restrictions": 3
}
}
}
}
And below is the snippet of the response where I can see the preference got reflected :
"dns_prefetching": {
"enabled": false
},
"download_restrictions": 3,
find my own dirty hack and not a good solution but it works
{ "download.default_directory": "/dev/null", "download_restrictions": 3 }

What is the correct config settings to automate test in parallel using Protractor?

Here is my settings. Is there a correct setting for angular/non-angular application test in parallel? Sometimes, either my firefox or chrome hangs while the other one is running. Is it the ignoreSynchronization suppose to be set to true and waitForAngular to be false? I feel like there is too much time syncing problem that is causing one of the browsers to hang?
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
getPageTimeout: 600000,
allScriptsTimeout: 500000,
defaultTimeoutInterval: 30000,
framework: 'custom',
// path relative to the current config file
frameworkPath: require.resolve('protractor-cucumber-framework'),
multiCapabilities:
[{
'browserName': 'firefox',
specs: 'features/firefox/*.feature',
},
{
'browserName': 'chrome',
specs: 'features/chrome/*.feature',
}],
maxSessions: 2,
baseUrl: 'https://localhost:8080',
cucumberOpts: {
strict: true,
require: [
'hooks/hooks.js',
'specs/*Spec.js'
],
tags: [
"#runThis",
"~#ignoreThis"
],
profile: false,
format: 'json:./e2e/reports/cucumber-report.json',
resultJsonOutputFile: './e2e/reports/cucumber-report.json'
},
beforeLaunch: function() {
const fs = require('fs');
const path = require('path');
const directory = './e2e/reports';
//cleans up the json results from the previous build when using node flake
fs.readdir(directory, (err, files) => {
if (err) throw err;
for (const file of files) {
fs.unlink(path.join(directory, file), err => {
if (err) throw err;
});
}
});
},
onPrepare: function() {
var chai = require('chai');
chai.use(require('chai-as-promised'));
global.expect = chai.expect;
browser.ignoreSynchronization = true;
browser.manage().window().maximize();
browser.waitForAngular(false);
browser.manage().timeouts().implicitlyWait(30000);
},
ghostMode:false
}
browser.ignoreSynchronization is deprecated so you do not need to set that. However you do need to set browser.waitForAngularEnabled(false) instead of browser.waitForAngular(false) like you have in your conf. waitForAngular is what is called before every action when waitForAngularEnabled is true.
Setup your onPrepare like
onPrepare: function() {
var chai = require('chai');
chai.use(require('chai-as-promised'));
global.expect = chai.expect;
browser.manage().window().maximize();
browser.waitForAngularEnabled(false);
browser.manage().timeouts().implicitlyWait(30000);
},
Your specific situation will depend on what you are trying to achieve from your parallel execution.
This setup will divide your tests across 2 browser types, chrome and firefox, and execute your tests in parallel. It will support 3 chrome and 2 firefox browsers running at any one time. Tests are divided based on whichever finishing first
multiCapabilities: [
{ browserName: "chrome", shardTestFiles: true, maxInstances: 3 },
{ browserName: "firefox", shardTestFiles: true, maxInstances: 2 },
],
maxSessions: 10, //controls the total number of instances, won't be relevant in this case
This setup will execute all your tests on BOTH a chrome and firefox browser. If you are running 5 specs you will have 10 results.
multiCapabilities: [
{ browserName: "firefox" },
{ browserName: "chrome" },
],
Above given answers with adding shardTestFiles: true, and number of maxInstances should work. However I would still want to highlight that if you are using webdriver-manager start to start selenium server (which I think you are based on the selenium address given in config file) then do not expect that it will work as smooth as selenium grid solution to run tests in parallel on different browsers.
Webdriver-manager is designed as a quickest solution to start selenium server and is not a replacement of selenium-grid.

Gulp-protractor not starting webdriver-manager

I'm trying to run protractor automatically using gulp-protractor plugin. This whole process works fine when using protractor commands and explicitly running the web drivers individually.
The same works when running using gulp-protractor, provided ie webdriver is started manually in background before triggering the gulp task.
Below is the code snippet of my Gulp task
var protractor = require("gulp-protractor").protractor;
var webdriverupdate = require("gulp-protractor").webdriver_update;;
var webdriver_standalone = require("gulp-protractor").webdriver_standalone;
// This task is to update & run the webdriver
gulp.task('webdriver_standalone', webdriver_standalone);
gulp.task('webdriverUpdate', ['webdriver_standalone'], function () {
browsers: ['chrome', 'ie']
});
//for running protractor E2E test cases
gulp.task('protractor', function (callback) {
gulp
.src(['./e2e/sanity/shared/*.spec.ts',
'./e2e/sanity/app-header/*.spec.ts',
])
.pipe(protractor({
'configFile': 'Protractor.conf.js',
}))
.on('error', function (e) {
console.log(e);
})
.on('end', callback);
});
gulp.task('default',['webdriverUpdate','protractor']);
Below is the code snippet of my protractor.config.js
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 1100,
suites: {
shared: ['./e2e/sanity/shared/*.ts'] ,
appheader: ['./e2e/sanity/app-header/*.spec.ts']
},
multiCapabilities:
[{
seleniumAddress: 'http://localhost:5555/',
'browserName': 'internet explorer',
'platform': 'windows 10',
'version': '11',
'ignoreProtectedModeSettings': true,
},
{
seleniumAddress: 'http://localhost:4444/wd/hub',
'browserName': 'chrome',
'ignoreProtectedModeSettings': true,
}],
};
How do I run webdriver standalone task ahead of protractor task via gulp??
I used gulp with gulp-angular-protractor like below, hope this helps. It will work for gulp-protractor plugin as well.
//Gulpfile
var gulp = require('gulp');
var gulpProtractor = require('gulp-angular-protractor');
var paths = require('../paths.js');
// Execute e2e Tests
gulp.task('e2e-test', function(callback) {
gulp.src(paths.tests)
.pipe((gulpProtractor({
configFile: 'protractor.conf.js'
})).on('error', function(e) {
console.log(e);
}).on('end', callback));
});
gulp.task('webdriver-update', gulpProtractor.webdriver_update);
gulp.task('webdriver-standalone', ['webdriver-update'], gulpProtractor.webdriver_standalone);
//paths.js:
module.exports = {
tests: 'test/e2e/**/*.spec.js'
};

Pass parameters for each browser using Protractor

Just got started with Protractor for E2E testing.
I want to pass parameters (login and password) for each instance of chrome selenium server.
I want test the same spec file with different user account in parallel.
This is my conf.js :
capabilities: {
'browserName': 'chrome',
'chromeOptions': {
'args': ['--disable-web-security']
},
count: 10
},
You can use the onPrepare-method of Protractor for that. If multiple capabilities are being run, this will run once per capability. You can add data to the browser-object that you can use during your execution.
What you can do is something like this
// A JSON file or something
var login = {
"chrome": {
"user": "usernameChrome",
"pass": "passwordChrome"
},
"firefox": {
"user": "usernameFirefox",
"pass": "passwordFirefox"
}
};
// in your config
// An example configuration file.
exports.config = {
directConnect: true,
// Capabilities to be passed to the webdriver instance.
multiCapabilities: [{
'browserName': 'chrome'
},
{
'browserName': 'firefox'
}
],
// Framework to use. Jasmine is recommended.
framework: 'jasmine',
// Spec patterns are relative to the current working directory when
// protractor is called.
specs: ['example_spec.js'],
// Options to be passed to Jasmine.
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
},
onPrepare: function() {
return browser.getCapabilities()
.then((capabilities) => {
// Get the current browser you are using
browser.browserName = capabilities.get('browserName').toLowerCase();
// Add the user and pass to the browser-object
browser.user = login[browser.browserName].user;
browser.pass = login[browser.browserName] pass;
});
}
};
// In your spec
describe('logon', function() {
it('should logon', function() {
browser.get('http://www.example.com');
element(by.model('user')).sendKeys(browser.user);
element(by.model('pass')).sendKeys(browser.pass);
element(by.tagName('button')).click();
});
});
You can handle this with Protractor's params on the command line. For example, you can start each test with a different username/password like this:
protractor conf.js --params.username user1 --params.password password1
Then, in your test, you would use them something like this:
logIntoMyApp(browser.params.username, browser.params.password);
You can also set defaults in your config file (see the docs for details).