JS Puppeteer: enter multiple chars atomically - node.js

I'm trying to fill an html form with puppeteer using type. According to the docs:
page.type(selector, text[, options])
...
Sends a keydown, keypress/input, and keyup event for each character in the text.
I have an issue with other events interfering with my typing process. How can I type my text atomically? I.e. with a single keydown event?

you can do, page.keyboard.sendCharacter("text"), which will do something akin to pasting in the text all at once. Make sure to first focus the selector you want to type into.
await page.focus('selector');
await page.keyboard.sendCharacter('text');

I don't know if it fits your needs but if you want to avoid keyboard events you could set directly the input value
await page.evaluate(() => document.querySelector("YOUR_SELECTOR").value = "Puppeteer");
or, if you need just one keydown event, you can trick Puppeteer by setting the value to "almost" what you need
await page.evaluate(() => document.querySelector("YOUR_SELECTOR").value = "Puppetee");
and use type just for the last char
await page.type("YOUR_SELECTOR", "r");

Related

Directline choice prompt not displaying correctly

Hi we have a chatbot which is developed using bot framework and integrated in Webchat. In this choice prompt display is not correct. At some time it will display as buttons sometimes not. What may be the issue?
This is by design defaulting to ListStyle.auto as can be seen in the ChoicePrompt class here. The ChoicePrompt class extends the Prompt class which, if no prompt style (inline, list, suggest action, hero card, or none) is supplied, then it defaults to calling ChoiceFactory.forChannel(). This method runs an algorithm that checks a variety of factors to determine the best style for the given channel.
The forChannel() method checks, among other things, the number of choices included and the length of each choice title. If the title's length is too long, limited to 20 characters (ref here), and the number of choices is over 3 (ref here), then default to a list.
This is what is happening to you. However, you can overwrite this by simply passing in the style property in the prompt, like so:
async choiceStep(stepContext) {
const choices = ['Hello', 'No soup for you!', 'Execute Order 66', 'You shall not pass!', 'Make it so, number 1', "You can't handle the truth!"]; // , `${ Number(66) }`];
return await stepContext.prompt(CHOICE_DIALOG_SUB_PROMPT, {
prompt: "Choose and option ,eh?",
choices: ChoiceFactory.toChoices(choices),
style: ListStyle.suggestedAction
});
}

How to send key combination to selenium chromedriver?

i am using uirecorder to create mocha test cases to test my web program. I want to send some key combination like "Metakey + R". But i couldn't accomplish that.
here is an example of ui recorder generated step:
it('sendKeys: {DOWN}', async function(){
await driver.sendKeys('{DOWN}');
});
this works perfectly. but i couldn't figure out how to send key combinations.
The question is how can i send key combinations like ctrl+a ( holding ctrl and pressing a then leaving ctrl)
SOLUTION THAT I USE:
i just did it like this, and works fine.
await driver.sendKeys('{CTRL}a{CTRL}');
i just did it like this, and works fine.
await driver.sendKeys('{CTRL}a{CTRL}');
You can use ActionSequence class to perform actions in selenium using Node.
For pressing Left control + a you can simulate mouse action like this :
new webdriver.ActionSequence(driver).keyDown(webdriver.Key.LEFT_CONTROL).sendKeys("a").keyUp(webdriver.Key.LEFT_CONTROL).perform();
More Reference :
Reference 1
Reference 2
Using Keys class:
String keypress = Keys.chord(Keys.CONTROL, "a");
driver.findElement(By.locator("value of locator")).sendKeys(keypress);
Using Actions class:
Actions action = new Actions(driver);
action.keyDown(Keys.CONTROL).sendKeys("a").keyUp(Keys.CONTROL).perform();
To anyone who finds this in the future, here is a solution that worked for me:
const {Key} = require('selenium-webdriver');
...
await driver.actions()
.keyDown(Key.SHIFT)
.sendKeys(Key.TAB)
.keyUp(Key.SHIFT)
.perform();

Chrome Bookmarklet which open in new tab with specific action

I want to make a Chrome Bookmarklet which open a new tab with specific action.
To be more exact I want to have a fixed URL like "https://www.blablabla.com/search=" inside the bookmarklet and when I press it, I want a popup window to appear with an input field.
When I type something in the input field and press enter or OK/submit it should "run" the whole link plus my query.
For example, I press the bookmarklet, the input field appears and input the word "test" (without the quotes).
When I press submit the query, a new tab will open with the address of https://www.blablabla.com/search=test as the URL.
How do I do that?
I tried with prompt function but I can't get it work...
My question is a bit similar to How do I get JavaScript code in a bookmarklet to execute after I open a new webpage in a new tab?.
Although it remains unclear what exact issue you encounter, try the following bookmarklet:
javascript:(function() {
var targetUrl = "http://www.blablabla.com/search=";
new Promise (
(setQuery) => {var input = window.prompt("ENTER YOUR QUERY:"); if (input) setQuery(input);}
)
.then (
(query) => window.open(targetUrl + query)
);
})();
If it doesn't work, you should provide the problem description in more detail.
#Shugar's answer is mostly correct, but you don't need the promise.
javascript:(function() {
var targetUrl = "http://www.blablabla.com/search=";
var input = window.prompt("ENTER YOUR QUERY:");
if (input)
window.open(targetUrl + input)
})();
javascript:void(window.open('http://www.URL.com/'+prompt ('Enter your Query:')));
I hope this helps. Works for me and is much simpler code than I see above. (as long as we reach the end result, that is all that matters right?)

Use First() and Repeat() without restarting whole stream RxJS

I am building a trading bot using RxJS. For that i have to convert ticker data from a socket connection to candles that is getting emitted every x seconds.
I created the socketObservable like this
const subscribeObservable = Observable.fromEventPattern(h => bittrex.websockets.subscribe(['USDT-BTC'], h))
const clientCallBackObservable = Observable.fromEventPattern(h => bittrex.websockets.client(h))
const socketObservable = clientCallBackObservable
.flatMap(() => subscribeObservable)
.filter(subscribtionData => subscribtionData && subscribtionData.M === 'updateExchangeState')
.flatMap(exchangeState => Observable.from(exchangeState.A))
.filter(marketData => marketData.Fills.length > 0)
.map(marketData => marketData && marketData.Fills)
Which works fine - when i connect to the client i flatMap to the subscription connection.
Then i have the candleObservable that is causing problems
export const candleObservable = (promise, timeFrame = TIME_FRAME) =>
promise
.scan((acc, curr) => [...acc, ...curr])
.skipWhile(exchangeData => dateDifferenceInSeconds(exchangeData) < timeFrame)
// take first after skipping
.first()
// first will complete the stream, so we repeat it
.repeat()
// we create candle data from the timeFrame array
.map(fillsData => createCandle(fillsData))
// accumulate candles
.scan((acc, curr) => [...[acc], curr])
What i am trying to achieve is to accumulate data until i have for a full candle that can be x seconds. Then i would like to take that emit and reset the scan function so i start for a new candle. Then i create the candle and accumulate it in another scan.
My problem is that when i call repeat() my socketObservable also gets called again. I do not know if this causes any overhead with the node-bittrex-api but i would like to avoid it.
I have tried putting the accumulating candle part in a flatMap or similar but couldn't get anyt of that to work.
Do you know how i can avoid to repeat() the whole stream or another way of make candles where i can accumulate and then reset the accumulator after first emit?
From what you've described it sounds like you have an observable you want to cut up into buckets of some kind based on some condition. In general, the reduction of a stream to another stream with fewer elements (without filtering) is referred to as "backpressure". In your specific case, it sounds like the backpressure operator you'd be interested in is buffer. The buffer operator can accept an observable as an argument that functions as a "closing selector", i.e. emissions in this observable can be used to regulate when you tie off one buffer and start a new one.
I'd suggest replacing your scan, skipWhile, first, and repeat with a buffer call, passing in a closing selector that will yield a value when your "TIME_FRAME" expires. This should be easy to express as an observable either using timer (in the case of a fixed amount) or a debounced version of the driving stream (if you want to stop when there's a pause in the data). If your buffer is strictly time-based, there's even a specialization of buffer called bufferTime that handles this. Because you'll wind up with an observable of arrays (rather than raw values), you'll likely want to replace your final scan with a regular array reduce.
It's hard to give concrete code without a simpler example to work with. I'd urge you to consult the sample code for the various backpressure operators to see if you can find something similar to what you're attempting to achieve.

IntentDialog matches only once at the start in bot framework

I have a simple piece of code where I want to run a first run operation in the beginning and let the default dialog handle everything else. In the code below.
bot.dialog('/firstRun', [firstRun1])
function firstRun1(session) {
session.replaceDialog('/')
}
bot.dialog('/', new builder.IntentDialog()
.matches(/wishlist/, '/wishlist')
.matches(/exclusive/, '/exclusive')
.onDefault('/runCampaign')
)
bot.dialog('/wishlist', (session, args) => {
session.endDialog('Add to wishlist')
})
bot.dialog('/exclusive', (session, args) => {
session.endDialog('Exclusive')
})
bot.dialog('/runCampaign', [runCampaign1])
function runCampaign1(session) {
session.send('default')
}
My problem is right here. When the person types exclusive I believe the default dialog will run its matches and should ideally trigger exclusive no matter how many times but for some reason its not happening that way as per the screenshot below. Once a person types exclusive or wishlist at the start it always runs the default at the bottom, how can I fix this. Also how to share data commonly between the different match clauses in the IntentDialog, lets say I want to have some variable session.dialogData.name across the matches and the default
UPDATE 1
Instead of saying session.endDialog inside the 2 areas in my code above, I simply wrote session.send() without ending any dialogs and now nothing happens when i type exclusive or wishlist. Am I missing something?
You should always call session.endDialog if you want to return to the calling(previous) dialog.
In your first example:
|current dialog|user input|next dialog|
|first run |go | / |
| / |whishlist | / |
| / |freak |runCampaign|
|runCampaign |<any> |runCampaign|
as you didn't call session.endDialog in the runCampaign dialog, you will always be in it and on any user input the 'default' will be printed.
In you second example, you missed to type "whishlist", so you immediately got into the runCampaign dialog. So, you changes to send there not called. But if you were typed 'whislist' as in the first example before "freak", you would have gotten into wishlist and you would always have printed "Added to wishlist" on any input.

Resources