ask for user input for selenium - javascript

I'm trying to automate my workflow using selenium in nodejs. When accessing sellercentral.amazon.com it sends an OTP code to my phone. How can I ask for a prompt at nodejs so I can input the code?
I've tried using readline-sync, but the prompt is always displayed even before selenium starts.
const webdriver = require('selenium-webdriver'),
By = webdriver.By,
until = webdriver.until;
const driver = new webdriver.Builder()
.forBrowser('firefox')
// .setFirefoxOptions(options)
.build();
//Main body
driver.get('https://sellercentral.amazon.com');
driver.wait(until.elementLocated(By.id('sign-in-button')));
driver.findElement(By.id('sign-in-button')).click();
const fillForm = (idToLook, keys) => {
this.idToLook = idToLook;
if (keys) {
driver.wait(until.elementLocated(By.id(idToLook)));
driver.findElement(By.id(idToLook)).sendKeys(keys);
}
else {
keys = readline.question(`what are the keys for ${this.idToLook}: `);
driver.findElement(By.id(idToLook)).sendKeys(keys);
}
}
fillForm('ap_email', amazon.id);
fillForm('ap_password', amazon.password);
driver.findElement(By.name('rememberMe')).click();
driver.findElement(By.id('a-autoid-0')).click();
driver.wait(until.elementIsNotVisible(By.id('auth-mfa-optcode')));
// fillForm('auth-mfa-otpcode');
driver.findElement(By.id('auth-mfa-remember-device')).click();
driver.quit();

You may try something in similar fashion . Wrap initializeSite to launch the site as a Promise .
A base script like the below:
function main() {
var initializeSite = initialize();
initializeSite.then(function(result) {
// Do your different actions to bring up the form that need OTP
readline.question (“Add OTP”, (otp) =>{
// Add rest of your codes here
}
console.log(“success”)
}, function(err) {
console.log(err);
})
}

Related

Data not returning from async function with database connection

The goal is to call a function from my main script that connects to a database, reads a document from it, stores pieces of that document in a new object, and returns that object to my main script. The problem is I cannot get it all to work together. If I try one thing, I get the results but my program locks up. If I try something else I get undefined results.
Long story short, how do I open a database and retrieve something from it to another script.
The program is a quiz site and I want to return the quiz name and the questions.
const myDb = require('./app.js');
var myData = myDb.fun((myData) => {
console.log(myData.quizName);
});
Here is the script that tries to open the database and find the data
const { MongoClient } = require("mongodb");
const {mongoClient} = require("mongodb");
const uri = connection uri goes here but my name is hard coded into it at the moment so I removed for privacy
const client = new MongoClient(uri);
const fun = async (cback) => {
try {
await client.connect();
const database = client.db('Quiz-Capstone');
const quizzes = database.collection('Quiz');
const query = {quizName: "CIS01"};
const options = {
sort: {},
projection: {}
};
const quiz = await quizzes.findOne(query, options);
var quizObject = {
quizName: quiz.quizName,
quizQuestions: quiz.quizQuestions
}
//console.log(testOb);
} finally {
await client.close();
cback(quizObject);
}
}
fun().catch(console.dir);
module.exports = {
fun: fun
}
UPDATE: Still stuck. I have read several different threads here about asynchronous calls and callbacks but I cannot get my function located in one file to return a value to the caller located in another file.

Javascript "EventSource" in C# .NET 5.0

my company choose "Mercure" (https://mercure.rocks/docs/getting-started) to manage Server-Sent Events.
We install "Mercure HUB" on a server and now, in C# .NET 5.0, I must implement the server-side (publisher, that I already implemented) and the client-side (subscriber).
The subscriber must be done with a WPF
From the "getting-started" page I can see a Javascript example that I need to transform into C#
I don't know how to manage a "EventSource" in C#
Any ideas ?
// The subscriber subscribes to updates for the https://example.com/users/dunglas topic
// and to any topic matching https://example.com/books/{id}
const url = new URL('https://localhost/.well-known/mercure');
url.searchParams.append('topic', 'https://example.com/books/{id}');
url.searchParams.append('topic', 'https://example.com/users/dunglas');
// The URL class is a convenient way to generate URLs such as https://localhost/.well-known/mercure?topic=https://example.com/books/{id}&topic=https://example.com/users/dunglas
const eventSource = new EventSource(url);
// The callback will be called every time an update is published
eventSource.onmessage = e => console.log(e); // do something with the payload
The code of this page works (https://makolyte.com/event-driven-dotnet-how-to-consume-an-sse-endpoint-with-httpclient/)
static async Task Main(string[] args)
{
HttpClient client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(5);
string stockSymbol = "VTSAX";
string url = $"http://localhost:9000/stockpriceupdates/{stockSymbol}";
while (true)
{
try
{
Console.WriteLine("Establishing connection");
using (var streamReader = new StreamReader(await client.GetStreamAsync(url)))
{
while (!streamReader.EndOfStream)
{
var message = await streamReader.ReadLineAsync();
Console.WriteLine($"Received price update: {message}");
}
}
}
catch(Exception ex)
{
//Here you can check for
//specific types of errors before continuing
//Since this is a simple example, i'm always going to retry
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine("Retrying in 5 seconds");
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
}

Connecting to a running process in Winappdriver using Javascript

I am fairly new to JS/Winappdriver.
The application I am trying to test is a windows based "Click Once" application from .Net, so I have to go to a website from IE and click "Install". This will open the application.
Once the application is running, I have no way to connect the application to perform my UI interactions while using JavaScript.
Using C#, I was looping through the processes looking for a process name, get the window handle, convert it to hex, add that as a capability and create the driver - it worked. Sample code below,
public Setup_TearDown()
{
string TopLevelWindowHandleHex = null;
IntPtr TopLevelWindowHandle = new IntPtr();
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.StartsWith($"SomeName-{exec_pob}-{exec_env}"))
{
TopLevelWindowHandle = clsProcess.Handle;
TopLevelWindowHandleHex = clsProcess.MainWindowHandle.ToString("x");
}
}
var appOptions = new AppiumOptions();
appOptions.AddAdditionalCapability("appTopLevelWindow", TopLevelWindowHandleHex);
appOptions.AddAdditionalCapability("ms:experimental-webdriver", true);
appOptions.AddAdditionalCapability("ms:waitForAppLaunch", "25");
AppDriver = new WindowsDriver<WindowsElement>(new Uri(WinAppDriverUrl), appOptions);
AppDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(60);
}
How do I do this in Javascript ? I can't seem to find any code examples.
Based on an example from this repo, I tried the following in JS to find the process to latch on to but without luck.
import {By2} from "selenium-appium";
// this.appWindow = this.driver.element(By2.nativeAccessibilityId('xxx'));
// this.appWindow = this.driver.element(By2.nativeXpath("//Window[starts-with(#Name,\"xxxx\")]"));
// this.appWindow = this.driver.elementByName('WindowsForms10.Window.8.app.0.13965fa_r11_ad1');
// thisappWindow = this.driver.elementByName('xxxxxxx');
async connectAppDriver(){
await this.waitForAppWindow();
var appWindow = await this.appWindow.getAttribute("NativeWindowHandle");
let hex = (Number(ewarpWindow)).toString(16);
var currentAppCapabilities =
{
"appTopLevelWindow": hex,
"platformName": "Windows",
"deviceName": "WindowsPC",
"newCommandTimeout": "120000"
}
let driverBuilder = new DriverBuilder();
await driverBuilder.stopDriver();
this.driver = await driverBuilder.createDriver(currentEwarpCapabilities);
return this.driver;
}
I keep getting this error in Winappdriver
{"status":13,"value":{"error":"unknown error","message":"An unknown error occurred in the remote end while processing the command."}}
I've also opened this ticket here.
It seems like such an easy thing to do, but I couldn't figure this one out.
Any of nodes packages I could use to get the top level window handle easily?
I am open to suggestions on how to tackle this issue while using JavaScript for Winappdriver.
Hope this helps some one out there,
Got around this by creating an exe using C# that generated hex of the app to connect based on the process name, it looks like something like this.
public string GetTopLevelWindowHandleHex()
{
string TopLevelWindowHandleHex = null;
IntPtr TopLevelWindowHandle = new IntPtr();
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.StartsWith(_processName))
{
TopLevelWindowHandle = clsProcess.Handle;
TopLevelWindowHandleHex = clsProcess.MainWindowHandle.ToString("x");
}
}
if (!String.IsNullOrEmpty(TopLevelWindowHandleHex))
return TopLevelWindowHandleHex;
else
throw new Exception($"Process: {_processName} cannot be found");
}
Called it from JS to get the hex of the top level window handle, like this,
async getHex () {
var pathToExe =await path.join(process.cwd(), "features\\support\\ProcessUtility\\GetWindowHandleHexByProcessName.exe");
var pathToDir =await path.join(process.cwd(), "features\\support\\ProcessUtility");
const result = await execFileSync(pathToExe, [this.processName]
, {cwd: pathToDir, encoding: 'utf-8'}
, async function (err, data) {
console.log("Error: "+ err);
console.log("Data(hex): "+ data);
return JSON.stringify(data.toString());
});
return result.toString().trim();
}
Used the hex to connect to the app like this,
async connectAppDriver(hex) {
console.log(`Hex received to connect to app using hex: ${hex}`);
const currentAppCapabilities=
{
"browserName": '',
"appTopLevelWindow": hex.trim(),
"platformName": "Windows",
"deviceName": "WindowsPC",
"newCommandTimeout": "120000"
};
const appDriver = await new Builder()
.usingServer("http://localhost:4723/wd/hub")
.withCapabilities(currentAppCapabilities)
.build();
await driver.startWithWebDriver(appDriver);
return driver;
}
Solution:
In WebDriverJS (used by selenium / appium), use getDomAttribute instead of getAttribute. Took several hours to find :(
element.getAttribute("NativeWindowHandle")
POST: /session/270698D2-D93B-4E05-9FC5-3E5FBDA60ECA/execute/sync
Command not implemented: POST: /session/270698D2-D93B-4E05-9FC5-3E5FBDA60ECA/execute/sync
HTTP/1.1 501 Not Implemented
let topLevelWindowHandle = await element.getDomAttribute('NativeWindowHandle')
topLevelWindowHandle = parseInt(topLevelWindowHandle).toString(16)
GET /session/DE4C46E1-CC84-4F5D-88D2-35F56317E34D/element/42.3476754/attribute/NativeWindowHandle HTTP/1.1
HTTP/1.1 200 OK
{"sessionId":"DE4C46E1-CC84-4F5D-88D2-35F56317E34D","status":0,"value":"3476754"}
and topLevelWindowHandle have hex value :)

How to wait for the promise when using get in Firestore

I am just trying a simple get command with Firestore, using this code from Google it doesn't work because it's not waiting for the promise?
Earlier I had put only a snippet of code, this is the entirety of index.js -- I'm using Firestore with Dialogflow to build a Google Assistant app and trying to call a function from the welcome intent that gets a field from Firestore, then writes that field to a string (named question1), and then this string should be spoken by the assistant as part of the ssml response. I've been on this for at least 30 hours already, can't seem to comprehend promises in regards to intents, firestore, etc. I've tried about 10 different solutions, this one works, only it says "undefined" in other variations I have tried it would say undefined several times but after 2-3 passes the get command would be complete and then the variable would be read out. I'm just trying to figure out how to get the get command and variable set before moving onto the SSML response. Can anyone point me in the right direction?
'use strict';
const functions = require('firebase-functions'); //don't forget this one
// Import Admin SDK
var admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();
const collectionRef = db.collection('text');
const Firestore = require('#google-cloud/firestore');
var doc;
var question1;
const url = require('url');
const {
dialogflow,
Image,
Permission,
NewSurface,
} = require('actions-on-google');
const {ssml} = require('./util');
const config = functions.config();
const WELCOME_INTENT = 'Default Welcome Intent';
const app = dialogflow({debug: true});
async function dbaccess(rando) {
console.log("dbaseaccess started")
var currentquestion2 = 'question-' + rando.toString();
var cityRef
try { return cityRef = db.collection('text').doc(currentquestion2).get();
console.log("get command completed")
//do stuff
question1 = cityRef.data().n111
} catch(e) {
//error!
}
console.log("one line above return something");
return rando;
}
app.fallback((conv) => {
// intent contains the name of the intent
// you defined in the Intents area of Dialogflow
const intent = conv.intent;
switch (intent) {
case WELCOME_INTENT:
var rando = Math.floor(Math.random() * 3) + 1;
dbaccess(rando);
const ssml =
'<speak>' +
question1 +
'</speak>';
conv.ask(ssml);
break;
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
You have 2 options: you can use async/await or you can use Promise.then() depending on how you want the code to execute.
Async/await:
async function databasetest {
var cityRef;
try{
cityRef = await db.collection('cities').doc('SF');
// do stuff
} catch(e) {
// error!
}
Promise.then():
db.collection('cities').doc('SF').then((cityRef) => {
cityRef.get()
.then(doc => { /* do stuff */ })
.catch(err => { /* error! */ });
});
maybe a little of work around could help you, I'm not sure yet how you are trying to implement it.
function databasetest () {
var cityRef = db.collection('cities').doc('SF');
return cityRef.get()
}
// so you can implement it like
databasetest().then(doc => {
if (!doc.exists) {
console.log('No such document!');
} else {
console.log('Document data:', doc.data());
}
})
.catch(err => {
console.log('Error getting document', err);
});
More context would help to understand your use case better :)

Unable to retrieve contents of Chrome Console using Selenium Webdriver in Node.js

I am trying to read the Chrome console using Selenium Webdriver in node.js, but so far it is unsuccessful. There are no errors. But all it returns is an empty array [].
The following is a snippet of the HTML and JavaScript function. When run manually in Chrome, these write to the console just fine.
<button name="button1" type="button" onclick="test_console()">Test</button>
function test_console() {
console.log("Hello World");
}
The following is the code I am using in node.js to try to get the output to Chrome.
const webdriver = require('selenium-webdriver');
const chromeDriver = require('selenium-webdriver/chrome');
const logging = require('selenium-webdriver').logging;
const path = require('chromeDriver').path;
const service = new chromeDriver.ServiceBuilder(path).build();
chromeDriver.setDefaultService(service);
const {By, Key} = webdriver;
webdriver.promise.USE_PROMISE_MANAGER = false;
const CHROME_BIN_PATH = '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome';
const prefs = new logging.Preferences();
prefs.setLevel(logging.Type.BROWSER, logging.Level.ALL);
const options = new chromeDriver.Options();
options.setChromeBinaryPath(CHROME_BIN_PATH);
options.addArguments(
'headless',
'disable-gpu',
'verbose',
'disable-impl-side-painting',
);
const main = async () => {
try {
const driver = await new webdriver.Builder()
.withCapabilities(webdriver.Capabilities.chrome())
.setLoggingPrefs(prefs)
.forBrowser('chrome')
.setChromeOptions(options)
.build();
await driver.get('http://example.com/example.html');
//clicking this button manually in Chrome writes to the console
await driver.findElement(By.name('button1')).click();
await driver.manage().logs().get(logging.Type.BROWSER)
.then(function(entries) {
console.log(entries);
});
await driver.close();
await driver.quit();
} catch (error) {
await driver.close();
await driver.quit();
console.log(error);
}
};
main();
I'm sure the issue is simple, probably a configuration problem. I just cant figure out what the problem might be. I even resorted to reading the webdriver source code in Git to see if I could see anything, but to no avail.
As far as I can tell, getting the contents of the console from Chrome using webdriver is a no go.
I ended up solving the issue in this manner:
//append a div to the body of the page
await driver.executeScript("var div = document.createElement('div'); div.id = 'console_log'; document.body.appendChild(div);");
//override console.log to write the log message to the new div
await driver.executeScript("console.log = function(message){document.getElementById('console_log').innerHTML += message}");
//get the contents of the new div
const console_log = await driver.findElement(By.id('console_log'));
console.log(await console_log.getAttribute('innerHTML'));

Categories