Configured ChromeDriver capabilities are lost after building the WebDriver in Node Selenium - javascript

I'm trying to add in the default download path with chrome capabilities using the code shown below:
const test = async () => {
let builder = await new Builder().forBrowser("chrome");
let chromeCapabilities = builder.getCapabilities();
// chromeCapabilities.set("download.default_directory", downloadFolder);
chromeCapabilities.merge({ "download.default_directory": downloadFolder });
console.log(chromeCapabilities.get("download.default_directory"));
// builder.setChromeOptions(chromeCapabilities);
builder.withCapabilities(chromeCapabilities);
// builder.setChromeOptions({ "download.default_directory": downloadFolder });
// builder.withCapabilities({ "download.default_directory": downloadFolder });
console.log(builder.getCapabilities().get("download.default_directory"));
const driver = builder.build();
await driver.get(tempoboxLoginUrl);
const driverCapabilities = await (await driver).getCapabilities();
console.log(await driverCapabilities.get("download.default_directory"));
};
test();
After merging the capabilities with the new capability I want to add, I can log the value of the capability and it shows as expected. However once the driver is built, logging the value of the capability returns undefined. The following is the output when running this code with Node:
> node Test.js
C:\Users\dummy\Desktop << first log
C:\Users\dummy\Desktop << second log
DevTools listening on ws://127.0.0.1:57980/devtools/browser/d53946e4-cedc-4809-a20c-b8b3416463cc
undefined << third log

So I eventually figured it out. The capabilities must be added in a very specific way. See example here:
const builder = new Builder().withCapabilities({
browserName: "chrome",
"goog:chromeOptions": {
args: ["--start-maximized"],
prefs: { "download.default_directory": downloadFolder },
},
});
const driver = await builder.build();

Related

How to take full web page screenshot using webdriverIO command?

I am using browser.saveDocumentScreenshot('folder/filename.png') I am getting error as browser.saveDocumentScreenshot is not a function
If you want support of all browser and devices use https://github.com/wswebcreation/wdio-image-comparison-service
Alternately, with WebdriverIO 6 (maybe with 5 as well) it's possible to use Puppeteer commands.
With Puppeteer, it's possible to take full-page screenshots, see https://github.com/puppeteer/puppeteer/blob/v5.3.1/docs/api.md#pagescreenshotoptions
// Mocha example
describe('Screenshot', () => {
// replace with beforeAll in Jasmine!
before(() => {
// add command to reuse easily everywhere in the project
// https://webdriver.io/docs/customcommands.html
browser.addCommand('takeFullPageScreenshot', function (options = {}) {
// Puppeteer commands should be wrapped with browser.call
// because they return Promises
// https://webdriver.io/docs/api/browser/call.html
return browser.call(async () => {
// https://webdriver.io/docs/api/browser/getPuppeteer.html
const puppeteer = await browser.getPuppeteer()
// now we interact with Puppeteer API
// https://github.com/puppeteer/puppeteer/blob/v5.3.1/docs/api.md#browserpages
const pages = await puppeteer.pages()
// https://github.com/puppeteer/puppeteer/blob/v5.3.1/docs/api.md#pagescreenshotoptions
return pages[0].screenshot({ ...options, fullPage: true })
})
})
})
it('should take full page screenshot', () => {
browser.url('https://stackoverflow.com/questions/64242220/how-to-take-full-web-page-screenshot-using-webdriverio-command')
// somehow wait for page to load
expect($('.user-details')).toBeVisible()
// take screenshot and save to file
browser.takeFullPageScreenshot({ path: './screenshot.png' })
// take screenshot but don't save to file
const screenshot = browser.takeFullPageScreenshot()
})
})

GCloud Function with Puppeteer - 'Process exited with code 16' error

Having trouble finding any documentation or cause for this sort of issue. I'm trying to run a headless chrome browser script that pulls the the current song playing from kexp.org and returns it as a JSON object. Testing with the NPM package #Google-clound/functions-framework does return the correct response however when deployed into GCloud, I receive the following error when hitting the API trigger:
Error: could not handle the request
Error: Process exited with code 16
at process.on.code (invoker.js:396)
at process.emit (events.js:198)
at process.EventEmitter.emit (domain.js:448)
at process.exit (per_thread.js:168)
at logAndSendError (/workspace/node_modules/#google-cloud/functions framework/build/src/invoker.js:184)
at process.on.err (invoker.js:393)
at process.emit (events.js:198)
at process.EventEmitter.emit (domain.js:448)
at emitPromiseRejectionWarnings (internal/process/promises.js:140)
at process._tickCallback (next_tick.js:69)
Full Script:
const puppeteer = require('puppeteer');
let browserPromise = puppeteer.launch({
args: [
'--no-sandbox'
]
})
exports.getkexp = async (req, res) => {
const browser = await browserPromise
const context = await browser.createIncognitoBrowserContext()
const page = await context.newPage()
try {
const url = 'https://www.kexp.org/'
await page.goto(url)
await page.waitFor('.Player-meta')
let content = await page.evaluate(() => {
// finds elements by data type and maps to array note: needs map because puppeeter needs a serialized element
let player = [...document.querySelectorAll('[data-player-meta]')].map((player) =>
// cleans up and removes empty strings from array
player.innerHTML.trim());
// creates object and removes empty strings
player = {...player.filter(n => n)}
let songList = {
"show":player[0],
"artist":player[1],
"song":player[2].substring(2),
"album":player[3]
}
return songList
});
context.close()
res.set('Content-Type', 'application/json')
res.status(200).send(content)
} catch (e) {
console.log('error occurred: '+e)
context.close()
res.set('Content-Type', 'application/json')
res.status(200).send({
"error":"occurred"
})
}
}
Is there documentation for this error type? It's been deployed on GCloud via CLI shell with the following parameters:
gcloud functions deploy getkexp --trigger-http --runtime=nodejs10 --memory=1024mb

Using testCafe 'requestLogger' to retrieve chrome performance logs fails

This is in continuation of this thread: Is there a way in TestCafe to validate Chrome network calls?
Here is my testCafe attempt to retrieve all the network logs(i.e. network tab in developer tools) from chrome. I am having issues getting anything printed on console.
const logger = RequestLogger('https://studenthome.com',{
logRequestHeaders: true,
logRequestBody: true,
logResponseHeaders: true,
logResponseBody: true
});
test
('My test - Demo', async t => {
await t.navigateTo('https://appURL.com/app/home/students');//navigate to app launch
await page_students.click_studentNameLink();//click on student name
await t
.expect(await page_students.exists_ListPageHeader()).ok('do something async', { allowUnawaitedPromise: true }) //validate list header
await t
.addRequestHooks(logger) //start tracking requests
let url = await page_studentList.click_tab();//click on the tab for which requests need to be validated
let c = await logger.count; //check count of request. Should be 66
await console.log(c);
await console.log(logger.requests[2]); // get the url for 2nd request
});
I see this in console:
[Function: count]
undefined
Here is picture from google as an illustration of what I am trying to achieve. I navigate to google.com and opened up developer tools> network tab. Then I clicked on store link and captured logs. The request URLs I am trying to collect are highlighted. I can get all the urls and then filter to the one I require.
The following, I have already tried
await console.log(logger.requests); // undefined
await console.log(logger.requests[*]); // undefined
await console.log(logger.requests[0].response.headers); //undefined
await logger.count();//count not a function
I would appreciate if someone could point me in the right direction?
You are using different urls in your test page ('https://appURL.com/app/home/students') and your logger ('https://studenthome.com'). This is probably the cause.
Your Request Logger records only requests to 'https://studenthome.com'.
In your screenshot I see the url 'http://store.google.com', which differs from the logger url, so the logger does not process it.
You can pass a RegExp as a first arg of the RequestLogger constructor to all requests which match your RegExp.
I have created a sample:
import { RequestLogger } from 'testcafe';
const logger = RequestLogger(/google/, {
logRequestHeaders: true,
logRequestBody: true,
logResponseHeaders: true,
logResponseBody: true
});
fixture `test`
.page('http://google.com');
test('test', async t => {
await t.addRequestHooks(logger);
await t.typeText('input[name="q"]', 'test');
await t.typeText('input[name="q"]', '1');
await t.typeText('input[name="q"]', '2');
await t.pressKey('enter');
const logRecord = logger.requests.length;
console.log(logger.requests.map(r => r.request.url));
});

protractor: random test fail

So I just started work on protractor tests and I'm facing the following problem - my tests fail inconsistently. Sometimes the test may pass and the next time it fails. Reasons to fail is very different, it may because it failed to find an element on a page or element does not have text in it (even if it has).
I'm running on Ubuntu 14.04, the same problem relevant for Chrome Version 71.0.3578.80 and Firefox Version 60.0.2. AngularJS Version 1.7.2 and Protractor Version 5.4.0. I believe the problem is somewhere in my code, so here below I provided an example of an existing code base.
Here is my protractor config
exports.config = {
rootElement: '[ng-app="myapp"]',
framework: 'jasmine',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['./e2e/**/*protractor.js'],
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'https://localhost/',
allScriptsTimeout: 20000,
jasmineNodeOpts: {
defaultTimeoutInterval: 100000,
},
capabilities: {
browserName: 'firefox',
marionette: true,
acceptInsecureCerts: true,
'moz:firefoxOptions': {
args: ['--headless'],
},
},
}
And here capabilities for chrome browser
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: [ "--headless", "--disable-gpu", "--window-size=1920,1080" ]
}
},
And finally, my test kit that failed a few times
const InsurerViewDriver = require('./insurer-view.driver');
const InsurerRefundDriver = require('./insurer-refund.driver');
const { PageDriver } = require('#utils/page');
const { NotificationsDriver } = require('#utils/service');
const moment = require('moment');
describe(InsurerViewDriver.pageUrl, () => {
beforeAll(async () => {
await InsurerViewDriver.goToPage();
});
it('- should test "Delete" button', async () => {
await InsurerViewDriver.clickDelete();
await NotificationsDriver.toBeShown('success');
await PageDriver.userToBeNavigated('#/setup/insurers');
await InsurerViewDriver.goToPage();
});
describe('Should test Refunds section', () => {
it('- should test refund list content', async () => {
expect(await InsurerRefundDriver.getTitle()).toEqual('REFUNDS');
const refunds = InsurerRefundDriver.getRefunds();
expect(await refunds.count()).toBe(1);
const firstRow = refunds.get(0);
expect(await firstRow.element(by.binding('item.name')).getText()).toEqual('Direct');
expect(await firstRow.element(by.binding('item.amount')).getText()).toEqual('$ 50.00');
expect(await firstRow.element(by.binding('item.number')).getText()).toEqual('');
expect(await firstRow.element(by.binding('item.date')).getText()).toEqual(moment().format('MMMM DD YYYY'));
});
it('- should test add refund action', async () => {
await InsurerRefundDriver.openNewRefundForm();
const NewRefundFormDriver = InsurerRefundDriver.getNewRefundForm();
await NewRefundFormDriver.setPayment(`#555555, ${moment().format('MMMM DD YYYY')} (amount: $2,000, rest: $1,500)`);
await NewRefundFormDriver.setPaymentMethod('Credit Card');
expect(await NewRefundFormDriver.getAmount()).toEqual('0');
await NewRefundFormDriver.setAmount(200.05);
await NewRefundFormDriver.setAuthorization('qwerty');
await NewRefundFormDriver.submit();
await NotificationsDriver.toBeShown('success');
const interactions = InsurerRefundDriver.getRefunds();
expect(await interactions.count()).toBe(2);
expect(await InsurerViewDriver.getInsurerTitleValue('Balance:')).toEqual('Balance: $ 2,200.05');
expect(await InsurerViewDriver.getInsurerTitleValue('Wallet:')).toEqual('Wallet: $ 4,799.95');
});
});
});
And here some functions from driver's, that I'm referencing in the test above
// PageDriver.userToBeNavigated
this.userToBeNavigated = async function(url) {
return await browser.wait(
protractor.ExpectedConditions.urlContains(url),
5000,
`Expectation failed - user to be navigated to "${url}"`
);
};
this.pageUrl = '#/insurer/33';
// InsurerViewDriver.goToPage
this.goToPage = async () => {
await browser.get(this.pageUrl);
};
// InsurerViewDriver.clickDelete()
this.clickDelete = async () => {
await $('[ng-click="$ctrl.removeInsurer()"]').click();
await DialogDriver.toBeShown('Are you sure you want to remove this entry?');
await DialogDriver.confirm();
};
// NotificationsDriver.toBeShown
this.toBeShown = async (type, text) => {
const awaitSeconds = 6;
return await browser.wait(
protractor.ExpectedConditions.presenceOf(
text ? element(by.cssContainingText('.toast-message', text)) : $(`.toast-${type}`)
),
awaitSeconds * 1000,
`${type} notification should be shown within ${awaitSeconds} sec`
);
}
// InsurerRefundDriver.getRefunds()
this.getRefunds = () => $('list-refunds-component').all(by.repeater('item in $data'));
// InsurerViewDriver.getInsurerTitleValue
this.getInsurerTitleValue = async (text) => {
return await element(by.cssContainingText('header-content p', text)).getText();
};
I can't upload the whole code here to give you better understanding because I have a lot of code till this moment, but the code provided above is the exact sample of approach I'm using everywhere, does anyone see a problem in my code? Thanks.
First of all add this block before exporting your config
process.on("unhandledRejection", ({message}) => {
console.log("\x1b[36m%s\x1b[0m", `Unhandled rejection: ${message}`);
});
this essentially colorfully logs to the console if you missed async/await anywhere, and it'll give confidence that you didn't miss anything.
Second, I would install "protractor-console" plugin, to make sure there is no errors/rejections in the browser console (i.e. exclude possibility of issues from your app side) and add to your config
plugins: [{
package: "protractor-console",
logLevels: [ "severe" ]
}]
Then the next problem that I would expect with these signs is incorrect waiting functions. Ideally you have to test them separately as you develop your e2e project, but since it's all written already I'll tell you how I debugged them. Note, this approach won't probably help you if your actions are less than a sec (i.e. you can't notice them). Otherwise follow this chain.
1) I created run configuration in WebStorm, as described in my comment here (find mine) How to debug angular protractor tests in WebStorm
2) Set a breakpoint in the first line of the test I want to debug
3) Then execute your test line by line, using the created run config.
When you start debugging process, webstorm opens up a panel with three sections: frames, console, variables. When the variables section has a message connected to localhost and no variables listed, this means your step is still being executed. Once loading completed you can see all your variables and you can execute next command. So the main principle here is you click Step Over button and watch for variables section. IF VARIABLES APPEAR BEFORE THE APPS LOADING COMPLETED (the waiting method executed, but the app is still loading, which is wrong) then you need to work on this method. By going this way I identified a lot of gaps in my custom waiting methods.
And finally if this doesn't work, please attach stack trace of your errors and ping me
I'm concerned about this code snippet
describe(InsurerViewDriver.pageUrl, () => {
beforeAll(async () => {
await InsurerViewDriver.goToPage();
});
it('- should test "Delete" button', async () => {
await InsurerViewDriver.clickDelete();
await NotificationsDriver.toBeShown('success');
await PageDriver.userToBeNavigated('#/setup/insurers');
await InsurerViewDriver.goToPage(); // WHY IS THIS HERE?
});
describe('Should test Refunds section', () => {
it('- should test refund list content', async () => {
// DOESN'T THIS NEED SOME SETUP?
expect(await InsurerRefundDriver.getTitle()).toEqual('REFUNDS');
// <truncated>
You should not depend on the first it clause to set up the suite below it. You didn't post the code for InsurerRefundDriver.getTitle() but if that code does not send the browser to the correct URL and then wait for the page to finish loading, it is a problem. You should probably have await InsurerViewDriver.goToPage(); in a beforeEach clause.
After some time research I found what was the problem. The cause was the way I'm navigate through the app.
this.goToPage = async () => {
await browser.get(this.pageUrl);
};
Turns out, that browser.get method is being resolved when url changed, but now when angularjs done compile. I used the same approach in every test kit, that's why my tests were failing inconsistently, sometimes page was not fully loaded before test start.
So here is an approach that did the trick
this.goToPage = async () => {
await browser.get(this.pageUrl);
await browser.wait(EC.presenceOf(`some important element`), 5000, 'Element did not appear after route change');
};
You should ensure that page done all the compiling job before moving on.
It seems this could be due to asynchronous javascript.
browser.ignoreSynchronization = true; has a global effect for all your tests. you may have to set it back to false, so protractor waits for angular to be finished rendering the page. e.g. in or before your second beforeEach function

How to automate ElectronJS app

We're looking to develop an ElectronJS app for particular website automation at our desk job, which includes common tasks like login, form filling, report downloading etc.
We've tried basic tutorial of ElectronJS, Spectron, NightmareJS, Puppeteer etc and all of them work fine separately, but very less documentation (although open github issues) are available on integration of each other.
We want to achieve following:
Login state (session) should not be deleted on ElectronJS app closing and should be available on restart of app.
Few menu buttons which initiates some automation tasks like download, form fill etc on existing browserWindow
We don't need headless automation, where some magic happens behind the scene. We need menu/button click based actions/tasks on current page only.
NightmareJS, Puppeteer etc all seems to start their own instances of web pages (since because they were built for testing of standalone apps) but what we need is automation of existing BrowserWindows.
Is puppeteer or nightmarejs correct tools for such goals? If yes, any documentation?
Or else, should we inject our own native JS events like mouseclick etc events in console to perform action?
You can use puppeteer-core. core version by default does not download Chromium, which you do not need if you want to control an Electron app.
In the test you then call launch method, where you define electron as the executable file instead of Chromium, like in following snippet:
const electron = require("electron");
const puppeteer = require("puppeteer-core");
const delay = ms =>
new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
(async () => {
try {
const app = await puppeteer.launch({
executablePath: electron,
args: ["."],
headless: false,
});
const pages = await app.pages();
const [page] = pages;
await page.setViewport({ width: 1200, height: 700 });
await delay(5000);
const image = await page.screenshot();
console.log(image);
await page.close();
await delay(2000);
await app.close();
} catch (error) {
console.error(error);
}
})();
Update for electron 5.x.y and up (currently up to 7.x.y, I did not test it on 8.x.y beta yet), where puppeteer.connect is used instead of launch method:
// const assert = require("assert");
const electron = require("electron");
const kill = require("tree-kill");
const puppeteer = require("puppeteer-core");
const { spawn } = require("child_process");
let pid;
const run = async () => {
const port = 9200; // Debugging port
const startTime = Date.now();
const timeout = 20000; // Timeout in miliseconds
let app;
// Start Electron with custom debugging port
pid = spawn(electron, [".", `--remote-debugging-port=${port}`], {
shell: true
}).pid;
// Wait for Puppeteer to connect
while (!app) {
try {
app = await puppeteer.connect({
browserURL: `http://localhost:${port}`,
defaultViewport: { width: 1000, height: 600 } // Optional I think
});
} catch (error) {
if (Date.now() > startTime + timeout) {
throw error;
}
}
}
// Do something, e.g.:
// const [page] = await app.pages();
// await page.waitForSelector("#someid")//
// const text = await page.$eval("#someid", element => element.innerText);
// assert(text === "Your expected text");
// await page.close();
};
run()
.then(() => {
// Do something
})
.catch(error => {
// Do something
kill(pid, () => {
process.exit(1);
});
});
Getting the pid and using kill is optional. For running the script on some CI platform it does not matter, but for local environment you would have to close the electron app manually after each failed try.
Simple demo repo:
https://github.com/peterdanis/electron-puppeteer-demo
Automation Script in Java using Selenium and ChromeDriver
package setUp;
import helper.Constants;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.DesiredCapabilities;
public class Test {
public static void main(String[] args) {
System.setProperty(Constants.WebDriverType, Constants.WebDriverPath + Constants.WindowsDriver);
ChromeOptions opt = new ChromeOptions();
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("chromeOptions", opt);
capabilities.setBrowserName("chrome");
capabilities.setVersion("73.0.3683.121");
ChromeOptions options = new ChromeOptions();
options.merge(capabilities);
options.setBinary("C:\\\\Program Files\\\\Audio\\\\Audio-Configuration\\\\Audio-Configuration.exe");
options.setCapability("chromeOptions", options);
ChromeDriver driver = new ChromeDriver(options);
try {
Thread.sleep(5000);
WebElement webElement = driver.findElement(By.xpath(
"/html/body/app-root/mat-drawer-container/mat-drawer/div/app-bottom-side-nav/div/app-settings-nav/div/div/a/div"));
webElement.click();
} catch (Exception e) {
System.out.println("Exception trace");
System.out.println(e);
}
}
}
Automation Script in JavaScript using Spectron (built on top-of ChromeDriver and WebDriverIO).
const Application = require("spectron").Application;
const path =
"C:/Program Files/Audio/Audio-Configuration/Audio-Configuration.exe";
const myApp = new Application({
path: path,
chromeDriverArgs: ["--disable-extensions"],
env: {
SPECTRON: true,
ELECTRON_ENABLE_LOGGING: true,
ELECTRON_ENABLE_STACK_DUMPING: true
}
});
const windowClick = async app => {
await app.start();
try {
// Identifying by class name
await app.client.click(".ic-setting");
// Identifying by Id
// await app.client.click("#left-btn");
} catch (error) {
// Log any failures
console.error("Test failed", error.message);
}
// Stop the application
await app.stop();
};
windowClick(myApp);
Spectron is the best match for electron build applications.
You will have access to all electron API.we can start and stop your app by spectron only.
We can run both packaged app or with out even packaging.
https://electronjs.org/spectron
You can use Spectron but if you want to look at documentation, Spectron is using webdriverio which has good documentation.
I recommend you to use Spectron because I tried to automate my tests with java-selenium but it fails some of case. If you want to use selenium, write below code to set capabilities to setup electron app to chromedriver.
ChromeOptions options = new ChromeOptions();
options.setBinary(binaryPath);
options.addArguments("--app=" + argPath);
options.setCapability("chromeOptions", options);
driver = new ChromeDriver(options);
Hope this will help to you.
If integrating with electron nightmare is a very good library to achieve this even it will be ready to distribute with it, here is the following useful documentation for the same resource1
and

Categories

Resources