I have some integration tests using wdio, they are all passing. However, when I run them in headless chrome, one of these tests fails. I get this error:
1) Reset password by submitting a new password "before all" hook:
element ("#identifierNext") still not existing after 15000ms
The problem is in this line of code:
browser.waitForExist('#identifierNext');
Which is weird, because I am using the waitForExist(<element id>) in other tests as well and they pass even in headless chrome. I also tried to increase the waitFor limit up to 30000ms, but it fails anyway.
This is my wdio config:
exports.config = {
specs: [
'./test/pageobjects/**/reset_password.spec.js'
],
maxInstances: 1,
capabilities: [{
maxInstances: 1,
browserName: 'chrome',
'goog:chromeOptions': {
args: ['--headless', '--disable-gpu', '--window-size=1280,800', '--no-sandbox', '--disable-dev-shm-usage']
}
}],
sync: true,
logLevel: 'silent',
coloredLogs: true,
deprecationWarnings: true,
bail: 0,
screenshotPath: './errorShots/',
baseUrl: 'http://127.0.0.1:3000',
waitforTimeout: 10000,
connectionRetryTimeout: 90000,
connectionRetryCount: 3,
services: ['selenium-standalone'],
framework: 'mocha',
reporters: ['spec'],
mochaOpts: {
ui: 'bdd',
timeout: 30000
},
}
When I remove headless from chromeOptions, this test passes normally. Any ideas why this happens?
EDIT: this is my reset_password.spec.js file:
describe ('Reset password by submitting a new password', function(){
//test fails in this before function
before(function(){
browser.url(ResetPassword.token_url(valid_email, email_password));
});
it ('Password reset without passwords', function(){
.
.
.
})
});
And my reset_password.page.js file:
const Page = require('./page');
class ResetPassword extends Page {
get email() {
return $('input[type="text"]');
}
get url() {
return browser.getUrl();
}
open() {
super.open('/reset-password');
}
get signIn(){
browser.waitForExist('*=Sign in');
return $('*=Sign in');
}
get enterEmail(){
browser.waitForExist('input[type="email"]');
return $('input[type="email"]');
}
get submitEmail(){
//this fails in headless mode
browser.waitForExist('#identifierNext');
return $('#identifierNext');
}
get enterPassword(){
browser.waitForExist('#password > div.aCsJod.oJeWuf > div > div.Xb9hP > input');
return $('#password > div.aCsJod.oJeWuf > div > div.Xb9hP > input');
}
get submitPassword(){
browser.waitForExist('#passwordNext');
return $('#passwordNext');
}
get tokenEmail(){
browser.waitForExist('span[email="profiq.ldap#gmail.com"]');
return $$('span[email="profiq.ldap#gmail.com"]');
}
get tokenURL(){
browser.waitForExist('a[id*=reset_link]');
const links = $$('a[id*=reset_link]');
return links[links.length-1].getAttribute('href');
}
token_url(email, password){
browser.url('https://www.google.com/gmail/about/#');
this.signIn.click();
browser.switchTab(browser.getTabIds()[1]);
this.enterEmail.setValue(email);
this.submitEmail.click();
this.enterPassword.setValue(password);
this.submitPassword.click();
this.tokenEmail[1].click();
browser.pause(3000);
return this.tokenURL;
}
}
module.exports = ResetPassword;
I found the problem. Gmail displays differently in different browsers. I took a screenshot using browser.saveScreenshot('screenshot.jpg'); just before the test fails, and it shows that the page looked different (was in my local language instead of english and had different appearance and buttons). So this is the reason why the test could not find the button with given identifier.
Related
My Playwright scripts are configured to run in parallel (default) on Chrome, FF and Safari.
import { devices, PlaywrightTestConfig } from '#playwright/test';
const config: PlaywrightTestConfig = {
...
workers: 3,
fullyParallel: false,
},
projects: [
{
name: 'Chrome',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'Firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'Safari',
use: { ...devices['Desktop Safari'] },
},
],
One of my spec files has the below code
test.describe('Home Tests', () => {
let page: Page;
let homeView: HomeView;
test.beforeEach(async ({ page }) => {
homeView = new HomeView(page);
});
test('01', async () => {
await dashboard.login(false, ownerEmail);
...
});
test('02', async () => {
await dashboard.login(false, ownerEmail);
...
});
.
.
.
When I run this test, test 01 is taken up by all 3 browsers and executed in parallel. Due to technical login restrictions of the app, only one test passes and the rest fails. I want test 1 to run in Chrome first and once it is completed it should run on FF second and Safari next (which browser first doesn't matter)
I do not want to change the settings globally or make this possible on the CLI level. I want to handle this at the same spec file level. How can I achieve this?
try to use "test.describe.serial"
I'm facing a problem working with protractor along with firefox.
In my test i first direct the browser to a login page and after entering user and password i do another browser.get action that refers me to a different page.
Both the login page and the second page are non-angular pages.
The issue i'm facing is that firefox doesn't wait for the initial page to load and right away tries to do the redirect action.
I tried firefox versions: 27.0.1, 28.0, 42.0, 45.0.1 and 46.0.1 (all 32bit versions) all versions shows the same behavior.
The webdriver-manager version is 10.0.4, selenium webdriver version is 2.53, os win8.1 64 bit and protractor version is 3.1.1.
When running the same test on chrome browser the test run as it should, it waits for the login to finish and only then it goes to the next it block that performs the browser.get action.
My conf file:
var HtmlScreenshotReporter = require('protractor-jasmine2-screenshot-reporter');
var path = require('path');
exports.config = {
framework: 'jasmine2',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['multiTestRun.js'],
getPageTimeout: 60000,
rootElement: '[ng-app]',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 10000000,
isVerbose: true,
includeStackTrace: true
},
params: {
test: 'Regression',
resources: 3,
locations: 4,
skills: 2,
services: 0,
useNameSpace: 0
},
capabilities: {
'browserName': 'firefox'
},
onPrepare: function(){
var dest = path.normalize('./Results');
browser.manage().timeouts().setScriptTimeout(120000);
jasmine.getEnv().addReporter(
new HtmlScreenshotReporter({
dest: dest,
filename: 'my-report.html',
showQuickLinks: true,
reportOnlyFailedSpecs: false,
captureOnlyFailedSpecs: true,
restartBrowserBetweenTests: true
})
);
global.isAngularSite = function(flag){
browser.ignoreSynchronization = !flag;
};
browser.driver.manage().window().maximize();
}
};
My code file:
describe('test', function () {
beforeEach(function(){
isAngularSite(false);
}, 60000);
it('it1', function () {
browser.get('https://example.com/');
element(By.id('username')).clear();
element(By.id('username')).sendKeys('sanu#field.com');
element(By.id('password')).clear();
element(By.id('password')).sendKeys('1234.org');
element(By.id('Login')).click();
});
it('it2', function () {
browser.get('https://example.com/SecondPage');
browser.get('https://example.com/SecondPage');
browser.executeScript('return RemoteActions;')
.then(function(remoteAction) {
console.log('remoteAction.doAction');
console.log(remoteAction.doAction);
browser.executeAsyncScript(function(remoteAction) {
var callback = arguments[arguments.length - 1];
Visualforce.remoting.Manager.invokeAction(remoteAction.doAction, function (res, ev) {
callback(res);
}, { buffer: false, escape: false, timeout: 10000 });
},remoteAction).then(function(res) {
console.log(res);
});
});
});
any advice ?
Since these are not angular pages, and you are turning off synchronization, you will have to tell webdriver how to wait for something to happen after clicking the login button before performing the next action.
In this example, you can wait for the login button to no longer be present, so that you know it is ready to navigate to the next page.
element(By.id('Login')).click();
browser.wait(function() {
element(By.id('Login')).isDisplayed().then(function(loginButtonIsDisplayed) {
// break out of the loop by returning 'true' when the login button is not displayed
return !loginButtonIsDisplayed;
), browser.getPageTimeout, 'after clicking the login button, the next page did not load within ' + browser.getPageTimeout + 's'};
browser.get('https://example.com/SecondPage');
browser.getPageTimeout is a protractor timeout that is set to 10 seconds by default
Referencing the information from the question Fetching values from email in protractor test case, I am still unable to reference the emails. In my test case, the 'expect' is not getting executed for some unknown reason to me.
Also if I use the line,
browser.controlFlow().await(getLastEmail()).then(...)
There is a 'browser.controlFlow(...).await is not a function error'
conf.js
var MailListener = require("mail-listener2")
exports.config = {
framework: 'jasmine2',
specs: ['./test.js'],
jasmineNodeOpts: { defaultTimeoutInterval: 360000 },
allScriptsTimeout: 60000,
onPrepare: function () {
var mailListener = new MailListener({
username: "username",
password: "password",
host: "imapPort",
port: 993, // imap port
secure: true,
tls: true,
tlsOptions: { rejectUnauthorized: false },
mailbox: "INBOX", // mailbox to monitor
searchFilter: ["UNSEEN", "FLAGGED"], // the search filter being used after an IDLE notification has been retrieved
markSeen: true, // all fetched email willbe marked as seen and not fetched next time
fetchUnreadOnStart: true, // use it only if you want to get all unread email on lib start. Default is `false`,
mailParserOptions: {streamAttachments: true}, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: { directory: "attachments/" } // specify a download directory for attachments
})
mailListener.start()
mailListener.on("server:connected", function(){
console.log("Mail listener initialized")
})
mailListener.on("error", function(err){
console.log(err)
})
mailListener.on("server:disconnected", function(){
console.log("imapDisconnected")
})
global.mailListener = mailListener
},
onCleanUp: function () {
mailListener.stop()
}
}
The test case:
describe('Email Testing', function () {
it('should login with a registration code sent to an email', function () {
//this line causes a 'browser.controlFlow(...).await is not a function' error
// browser.controlFlow().await(getLastEmail()).then(function (email) {
getLastEmail().then(function (email) {
// The expect does not get executed as it should fail
expect(email.subject).toEqual('My Subject')
})
})
})
function getLastEmail () {
var deferred = protractor.promise.defer()
console.log('Waiting for an email...')
mailListener.on('mail', function (mail) {
console.log('No Console Log Here!')
deferred.fulfill(mail)
})
return deferred.promise
}
I am not certain what I am missing in my test case to be able to read the subject or body of the email?
Ran into the same issue today. Turns out the API for the webdriver and ControlFlow has been updated and await has been changed to wait. Yeah, subtle difference. See the new API reference here: https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/promise_exports_ControlFlow.html
To schedule a task for the wait condition, change your code to look like this:
browser.controlFlow().wait(getLastEmail()).then(...)
you would basically have to wrap that asynchronous code inside of a promise and the pass that promise/function into the flow.execute()
var flow = protractor.promise.controlFlow();
flow.execute( getLastEmail() ).then(function(email){
text = email.text
});
I've been working on some protractor tests lately and from time to time some of my tests randomly fail with the following error:
DEBUG - WebDriver session successfully started with capabilities { caps_:
{ platform: 'LINUX',
acceptSslCerts: true,
javascriptEnabled: true,
browserName: 'chrome',
chrome: { userDataDir: '/tmp/.com.google.Chrome.czw4dR' },
rotatable: false,
locationContextEnabled: true,
mobileEmulationEnabled: false,
'webdriver.remote.sessionid': '3afc09d9-d06d-4c99-a788-d1118093c08d',
version: '40.0.2214.111',
takesHeapSnapshot: true,
cssSelectorsEnabled: true,
databaseEnabled: false,
handlesAlerts: true,
browserConnectionEnabled: false,
nativeEvents: true,
webStorageEnabled: true,
applicationCacheEnabled: false,
takesScreenshot: true } }
Started
token: a62e88d34991f4eef0894102e004e92032857700
.F...........................
Failures:
1) login form filled should fail on wrong credentials
Message:
Failed: Timed out waiting for Protractor to synchronize with the page after 11 seconds. Please see https://github.com/angular/protractor/blob/master/docs/faq.md
Looking at protractor documentation this error usually happens when there are pending $http requests or i'm using $timeout for something. I've tried setting a longer timeout for my tests(minutes) but it hasn't helped. My latest idea has been to report what requests are pending so i made the following Jasmine Reporter:
var AngulaRequestsReporter = function(dir){
dir = (dir || '/tmp/protractors/');
this.requests = function(testDescription) {
var fname = testDescription.replace(/\s/g, '_') + '.pending_requests';
mkdirp(dir);
browser.executeScript(function(){
try{
var $http = angular.injector(["ng"]).get("$http");
return $http.pendingRequests;
}catch(e){
return [];
}
}).then(function(pendingRequests){
var stream = fs.createWriteStream(path.join(dir, fname));
stream.write(util.inspect(pendingRequests, {showHidden: false, depth: null}));
stream.end();
});
};
return this;
};
// takes screenshot on each failed spec (including timeout)
AngulaRequestsReporter.prototype = {
specDone: function(result) {
if (result.status !== 'passed') {
this.requests(result.description );
}
}
};
However the result is always an empty []. Have you guys had this problem before and if so how did you solve it? Also, is there anything i can make to improve this reporter?
The following code works well when i'm running protractor using chrome browser. But when i'm changing browser to phantomjs, looks like he cant click at login button.
describe('Authentication capabilities', function() {
var siteUrl = 'http://localhost:5000/';
var loginURL = 'http://localhost:5000/views/account/login';
browser.get("http://localhost:5000/");
var username = element(by.model('credential.username'));
var password = element(by.model('credential.password'));
var loginButton = element(by.xpath('//form[1]/button[#type="submit"]'));
var error = element(by.xpath('//form[1]/div[1]'));
it('should redirect to the login page if trying to load protected page while not authenticated', function() {
browser.get(loginURL);
loginURL = browser.getCurrentUrl();
browser.get(siteUrl);
expect(browser.getCurrentUrl()).toEqual(loginURL);
});
it('should warn on missing/malformed credentials', function() {
username.clear();
password.clear();
password.sendKeys('test');
loginButton.click();
expect(error.getText()).toMatch('Missing credentials');
username.sendKeys('example');
password.clear();
loginButton.click();
expect(error.getText()).toMatch('Missing credentials');
username.clear();
username.sendKeys("admin");
password.sendKeys("someinvalidpassword");
loginButton.click();
expect(error.getText()).toMatch("Password not valid.");
username.sendKeys("admin2");
password.sendKeys("someinvalidpassword");
loginButton.click();
expect(error.getText()).toMatch("Incorrect username.");
});
it('should accept a valid email address and password', function() {
username.clear();
password.clear();
username.sendKeys('admin');
password.sendKeys('fubotv');
loginButton.click();
expect(element(by.binding('{{user.displayName}}')).getText()).toEqual("Administrator");
});
it('should return to the login page after logout', function() {
element(by.xpath('//li[#class="dropdown user-dropdown"]/a[1]')).click();
var logoutButton = element(by.xpath('//ul[#class="dropdown-menu"]/li[1]/a'));
logoutButton.click();
expect(browser.getCurrentUrl()).toEqual(loginURL);
});
});
Config for phantomjs:
// myConf.js
exports.config = {
seleniumAddress: 'http://localhost:9515',
capabilities: {
'browserName': 'phantom',
'phantomjs.binary.path':'./../../node_modules/phantomjs/bin/phantomjs',
'phantomjs.cli.args':['--logfile=PATH', '--loglevel=DEBUG']
},
specs: [
'e2e/**/*Test.js'
],
jasmineNodeOpts: {
onComplete: null,
isVerbose: false,
showColors: true,
includeStackTrace: false
}
}
i'm getting error:
Failures:
1) Authentication capabilities should accept a valid email address and password
Message:
Error: No element found using locator: by.binding("{{user.displayName}}")
2) Authentication capabilities should return to the login page after logout
Message:
Error: Error while waiting for Protractor to sync with the page: {"message":"Can't find variable: angular","line":4,"sourceId":140550656205136,"stack":"ReferenceError: Can't find variable: angular\n at :4\n at anonymous (:9)\n at Na (phantomjs://webpage.evaluate():14)\n at phantomjs://webpage.evaluate():15\n at phantomjs://webpage.evaluate():15\n at phantomjs://webpage.evaluate():16\n at phantomjs://webpage.evaluate():16\n at phantomjs://webpage.evaluate():16","stackArray":[{"sourceURL":"","line":4},{"function":"anonymous","sourceURL":"","line":9},{"function":"Na","sourceURL":"phantomjs://webpage.evaluate()","line":14},{"sourceURL":"phantomjs://webpage.evaluate()","line":15},{"sourceURL":"phantomjs://webpage.evaluate()","line":15},{"sourceURL":"phantomjs://webpage.evaluate()","line":16},{"sourceURL":"phantomjs://webpage.evaluate()","line":16},{"sourceURL":"phantomjs://webpage.evaluate()","line":16}],"name":"ReferenceError"}
Finished in 7.568 seconds
4 tests, 7 assertions, 2 failures
witch means it still at the login page, because this element {{user.displayName}} exists only at protected page.
when i'm running at chrome, it works as well.
Are you using twitter bootstrap on your site? It might be that phantomJS is being sized too small. I had the same issue until I did:
onPrepare() {
browser.driver.manage().window().setSize(1600, 800);
}