I have a problem with accessing code that I am able to use through browser console.
In my case it is a Tawk_Api function Tawk_API.hideWidget();
I tried to use browser execute and call but the output saying that Tawk.Api is not defined
Code example
var expect = require('chai').expect;
function HideTawk (){
Tawk_API.hideWidget();
}
describe('', function() {
it('should be able to filter for commands', function () {
browser.url('https://arutech.ee/en/windows-price-request');
$('#uheosaline').click();
browser.execute(HideTawk());
var results = $$('.commands.property a').filter(function (link) {
return link.isVisible();
});
expect(results.length).to.be.equal(3);
results[1].click();
expect($('#getText').getText()).to.be.equal('GETTEXT');
});
});
Working fixed function:
function HideTawk (){
return new Promise(function(resolve, reject) {
Tawk_API.hideWidget();
})
}
And browser.execute(HideTawk()) is a mistake it should be browser.call(HideTawk());
docs: http://webdriver.io/api/utility/call.html
I have a below code in my application base object it can help you to understand call api:
_callClientAPI(func, args) {
let trial = 1;
return new Promise(async(res, rej) => {
while (true) {
if (trial > this._pollTrials) {
rej(`Could not retrieve the element in this method * this._pollTimeout} seconds.`);
break;
}
let result;
try {
result = await func.call(this.client, args, false);
} catch (e) { }
if (result && result !== '') {
res(result);
break;
}
await this.wait();
trial++;
}
});
}
Related
NB: this javascript uses the openui5 libraries
attachOnceAsync: function(binding, callback) {
var args = [...arguments]
args.splice(0, 2)
return new Promise((resolve, reject) => {
var dataReceived = function(oEvent) {
if (typeof callback == 'function') {
if (callback.constructor.name === "AsyncFunction") {
callback(oEvent, ...args).then(() =>
resolve(oEvent)
).catch((err) => {
reject(err)
})
} else {
try {
callback(oEvent, ...args);
resolve(oEvent)
} catch (err) {
reject(err)
}
}
} else {
resolve(oEvent, ...args);
}
}
binding.attachEventOnce("dataReceived", dataReceived);
})
}
There aren't any Promise versions of events like "dataReceived" so this is my attempt to wrap one up.
In this case, callback is undefined (it shouldn't be, but that's a different issue). The line resolve(oEvent, ...args); is hit instead, only it never returns out of the await!
I changed that line to just resolve(oEvent) but still no joy.
Any ideas what I'm doing wrong?
Thanks
PS: I'll keep an eye on this question so I can offer any extra info required
here's how I call it:
handleAsync: async function(controller) {
/* Lots of setup */
var response = await fetch(/*redacted*/ )
if (response.status === 400) {
console.log(await response.json());
throw new BadRequestException();
}
if (response.status === 204){
/* more setup */
context = oPanel.getBindingContext("odata");
var updatePurchaseOrderDocTotalFunc = this.updatePurchaseOrderDocTotal
var callback = async function() {
var docTotalUpdate = await updatePurchaseOrderDocTotalFunc(oView, context, oModel, agreedPrice);
if (docTotalUpdate && docTotalUpdate.status === 204) {
await context.refresh();
}
}
//-- HERE --
await AsyncBinding.attachOnceAsync(binding,callback)
await context.refresh();
}
return true;
}
Ok - The answer was staring at me.
Sorry I wasn't able to provide a working snippet but the ui5 scripts are quite extensive.
I have several other functions that return a Promise so I can use the await/async approach, but with this one I forgot to include the action that causes the event to fire.
I renamed the function to show what it now does, and included the action that causes the event to fire:
RefreshAsync : function(context,binding, callback){
var args = [...arguments]
args.splice(0,2)
return new Promise((resolve,reject)=>{
var dataReceived = function(oEvent){
if(typeof callback == 'function' ){
if(callback.constructor.name==="AsyncFunction"){
callback(oEvent, ...args).then(()=>{
resolve(oEvent)
}).catch((err)=>{
reject(err)
})
}else{
try{
callback(oEvent, ...args);
resolve(oEvent)
}catch(err){
reject(err)
}
}
}else{
resolve([oEvent, ..args])
}
}
binding.attachEventOnce("dataReceived", dataReceived);
//This is the action I missed
context.Refresh()
})
},
Given a button and it's click action set to be (in alpinejs)
<button x-on:click.prevent|debounce.3s="response = run();" type="button">Run</button>
when the run() function is defined like this:
function run() {
// captcha();
let data = input();
data = confirm(data);
...
data = request(url, data);
return data;
}
It is a synchronous function and it works nicely.
The recaptcha documentation (https://developers.google.com/recaptcha/docs/v3) states that if the button click is to be protected with recaptcha, it has to look like:
async function captcha() {
let resp;
grecaptcha.ready(function() {
grecaptcha.execute(recaptcha_site_key, {action: 'run'}).then(function(token) {
console.log(token);
resp = run();
});
});
return resp;
}
If the click action is set to be:
<button x-on:click.prevent|debounce.3s="response = await captcha();" type="button">Run</button>
It results to this error:
Can you please advise what I am doing wrong?
EDIT: added the return statement in the captcha function
for others that will be in the same situation as I am, here is the solution.
first step, I made the captcha function to return a promise
function captcha() {
return new Promise(function(resolve, reject) {
grecaptcha.ready(function() {
grecaptcha.execute(recaptcha_site_key, {action: 'run'}).then(function(token) {
resolve(token);
});
});
});
}
second step, async/await for the variable to become available
async function run() {
let g_recaptcha_response = await captcha();
document.forms['params'].elements['g_recaptcha_response'].value = g_recaptcha_response;
let data = input();
data = confirm(data);
if (data['error'] === true) {
return data;
}
let url = `${origin}/app/json`;
data = request(url, data);
// note: preparing for input / next request.
document.forms['params'].elements['token_id'].value = data['token_id'];
return data;
}
third step, create an IIFE at the top-level
(async function(){ let response = await run(); })();
I have a conditional statement in which I need to perform one of two operations, then continue after whichever operation has resolved. So my code currently looks as follows:
if (shoud_do_thing_a) { //should_do_thing_a is just a variable that determines which function to call. it is not a promise
do_thing_a()
} else {
do_thing_b()
}
// more code
The issue is that both do_thing_a and do_thing_b return promises, and I can't move on until whichever gets executed has resolved. The best way I've come up with to solve this is like this:
var more_code = function () {
// more code
}
if (shoud_do_thing_a) {
do_thing_a().then(more_code)
} else {
do_thing_b().then(more_code)
}
I don't like this structure. It's difficult to follow because you need to jump around to find where more_code is defined (imagine I have this type of control flow in several locations), rather than simply being able to continue reading.
Is there a better way to deal with this type of thing in javascript?
If you can use async/await
async function someFunc() {
var more_code = function () {
// more code
}
if (shoud_do_thing_a) {
await do_thing_a()
} else {
await do_thing_b()
}
more_code()
}
Or if you can't, use then():
var more_code = function () {
// more code
}
var do_thing;
if (shoud_do_thing_a) {
do_thing = do_thing_a()
} else {
do_thing = do_thing_b()
}
do_thing.then(more_code)
If you're stuck with raw Promises and can't use async/await (You usually should have no trouble, what with babel/typescript etc), the following is a bit more elegant than storing the promise in a variable:
function something() {
return Promise.resolve()
.then(() => {
if (should_do_thing_a) {
return do_thing_a();
}
else if (should_do_thing_b) {
return do_thing_b();
}
})
.then(some_more_code);
}
Note that when you start working with Promises, your functions should always return a Promise that other functions can work with. Leaving an asynchronous action without any way to handle it means bad things, especially when it comes to error handling.
In a more general sense, it means that when you use Promises, more of your code is "uplifted" into being executed and returned as Promises.
How I want to improve on other answers:
keep it clean and simple
no unneeded variables
return promise asap
in js we use camelCase
put it in a function and name that function to keep it readable
let then execute moreCode so it's called after the thing is done.
function doTheThing () {
if (shouldDoA) return doThingA()
else return doThingB()
}
doTheThing().then(moreCode)
Simple working example:
The scope it's defined in must be async.
const createUser = async (data) => {
if (await isUsernameTaken(username)) { return 'username-taken' }
}
The isUsernameTaken func:
const isUsernameTaken = async (username) => {
const request = await API.SomeRequest
return !request.isEmpty
}
Save the promise and add the then after the if statement:
var promise;
if (shoud_do_thing_a) {
promise = do_thing_a();
} else {
promise = do_thing_b();
}
promise.then(more_code);
var promise = shoud_do_thing_a? do_thing_a: do_thing_b
promise().then(function () {
// more code
})
Similar to other answers here, but you can self execute the async and clean up the condition a bit.
(async () => {
const should_do_thing_a = true
const do_thing_a = function() {
return new Promise(function(resolve, reject) {
resolve('a')
})
}
const do_thing_b = function() {
return new Promise(function(resolve, reject) {
resolve('b')
})
}
const result = (should_do_thing_a) ? await do_thing_a() : await do_thing_b()
console.log(result)
})()
The way I would do it would be to put the if check into another function that returns a promise. The promise gets resolved with the resolve of the other function calls in the if-else statement.
Example:
function performCheck(condition) {
var defer = $q.defer();
if (condition) {
doThingA().then(function(response) {
defer.resolve(response);
});
} else {
doThingB().then(function(response) {
defer.resolve(response)
});
}
return defer.promise;
}
performCheck(condition).then(function(response) {
//Do more code.
});
In my opinion, I would prefer this method because this function can now be used in multiple places where you have a check on the condition, reducing code duplication, and it is easier to follow.
You could reduce this down further with
function performCheck(condition) {
var defer = $q.defer();
var doThisThing = condition ? doThingA : doThingB;
doThisThing().then(function (response) {
defer.resolve(response);
});
return defer.promise;
}
performCheck(condition).then(function(response) {
//Do more code.
});
You can use async/await
async function fn() {
let p, result;
if (shoud_do_thing_a) {
p = await do_thing_a()
} else {
p = await do_thing_b()
}
if (p) {
result = more_code();
}
return result
}
more_code = miFunc() => return new Promise((resolve, reject) => ... });
Solution 1
const waitFor = should_do_thing_a ? do_thing_a() : do_thing_b();
waitFor.then(...).catch(...)
Solution 2
let waitFor = Promise.resolve();
if (do_thing_a) {
waitFor = do_thing_a();
} else {
waitFor = do_thing_b();
}
waitFor.then(...).catch(...);
When I run the script below from the command line node script.js, the result is:
success
Promise { <pending> }
The page never gets opened in console.log, can anyone explain why?
var phantom = require('phantom');
phantom.create().then(function (ph) {
ph.createPage().then(function (page) {
page.open('https://stackoverflow.com/').then(function (status) {
console.log(status);
var p = page.evaluate(function () {
return document.getElementsByTagName('html')[0].innerHTML
});
console.log(p);
});
});
});
as displayed, page.evaluate returns a promise.
try the folowing code :
var phantom = require('phantom');
phantom.create().then(function(ph) {
ph.createPage().then(function(page) {
page.open('https://stackoverflow.com/').then(function(status) {
console.log('test'+status);
var p = page.evaluate(function () {
return document.getElementsByTagName('html')[0].innerHTML
});
p.then(function(pagecontent) {
console.log(pagecontent);
process.exit()
});
});
});
});
That's because you're assigning Promise to a variable, and it's result is not returned immediately. You should rather:
page.evaluate(function () {
return document.getElementsByTagName('html')[0].innerHTML;
}).then(function (html) {
console.log(html);
});
Also, to not get lost in callback hell you might try to use async/await approach:
(async () => {
const instance = await phantom.create();
const page = await instance.createPage();
const status = await page.open('https://stackoverflow.com/');
const html = await page.evaluate(function () {
return document.getElementsByTagName('html')[0].innerHTML;
});
console.log(html);
})();
Which is kind of preferred way according to phantom maintainers guide.
I a promise in such fashion,
function getMode(){
var deferred = Promise.defer();
checkIf('A')
.then(function(bool){
if(bool){
deferred.resolve('A');
}else{
return checkIf('B');
}
}).then(function(bool){
if(bool){
deferred.resolve('B');
}else{
return checkIf('C');
}
}).then(function(bool){
if(bool){
deferred.resolve('C');
}else{
deferred.reject();
}
});
return deferred.promise;
}
checkIf returns a promise, and yes checkIf cannot be modified.
How do I break out of the chain at the first match? (any way other than explicitly throwing error?)
Any way other than explicitly throwing error?
You may need to throw something, but it does not have to be an error.
Most promise implementations have method catch accepting the first argument as error type (but not all, and not ES6 promise), it would be helpful under this situation:
function BreakSignal() { }
getPromise()
.then(function () {
throw new BreakSignal();
})
.then(function () {
// Something to skip.
})
.catch(BreakSignal, function () { })
.then(function () {
// Continue with other works.
});
I add the ability to break in the recent implementation of my own promise library. And if you were using ThenFail (as you would probably not), you can write something like this:
getPromise()
.then(function () {
Promise.break;
})
.then(function () {
// Something to skip.
})
.enclose()
.then(function () {
// Continue with other works.
});
You can use
return { then: function() {} };
.then(function(bool){
if(bool){
deferred.resolve('A');
return { then: function() {} }; // end/break the chain
}else{
return checkIf('B');
}
})
The return statement returns a "then-able", only that the then method does nothing.
When returned from a function in then(), the then() will try to get the result from the thenable.
The then-able's "then" takes a callback but that will never be called in this case. So the "then()" returns, and the callback for the rest of the chain does not happen.
I think you don't want a chain here. In a synchronous fashion, you'd have written
function getMode(){
if (checkIf('A')) {
return 'A';
} else {
if (checkIf('B')) {
return 'B';
} else {
if (checkIf('C')) {
return 'C';
} else {
throw new Error();
}
}
}
}
and this is how it should be translated to promises:
function getMode(){
checkIf('A').then(function(bool) {
if (bool)
return 'A';
return checkIf('B').then(function(bool) {
if (bool)
return 'B';
return checkIf('C').then(function(bool) {
if (bool)
return 'C';
throw new Error();
});
});
});
}
There is no if else-flattening in promises.
I would just use coroutines/spawns, this leads to much simpler code:
function* getMode(){
if(yield checkIf('A'))
return 'A';
if(yield checkIf('B'))
return 'B';
if(yield checkIf('C'))
return 'C';
throw undefined; // don't actually throw or reject with non `Error`s in production
}
If you don't have generators then there's always traceur or 6to5.
You could create a firstSucceeding function that would either return the value of the first succeeded operation or throw a NonSucceedingError.
I've used ES6 promises, but you can adapt the algorithm to support the promise interface of your choice.
function checkIf(val) {
console.log('checkIf called with', val);
return new Promise(function (resolve, reject) {
setTimeout(resolve.bind(null, [val, val === 'B']), 0);
});
}
var firstSucceeding = (function () {
return function (alternatives, succeeded) {
var failedPromise = Promise.reject(NoneSucceededError());
return (alternatives || []).reduce(function (promise, alternative) {
return promise.then(function (result) {
if (succeeded(result)) return result;
else return alternative();
}, alternative);
}, failedPromise).then(function (result) {
if (!succeeded(result)) throw NoneSucceededError();
return result;
});
}
function NoneSucceededError() {
var error = new Error('None succeeded');
error.name = 'NoneSucceededError';
return error;
}
})();
function getMode() {
return firstSucceeding([
checkIf.bind(null, 'A'),
checkIf.bind(null, 'B'),
checkIf.bind(null, 'C')
], function (result) {
return result[1] === true;
});
}
getMode().then(function (result) {
console.log('res', result);
}, function (err) { console.log('err', err); });
i like a lot of the answers posted so far that mitigate what the q readme calls the "pyramid of doom". for the sake of discussion, i'll add the pattern that i plunked out before searching around to see what other people are doing. i wrote a function like
var null_wrap = function (fn) {
return function () {
var i;
for (i = 0; i < arguments.length; i += 1) {
if (arguments[i] === null) {
return null;
}
}
return fn.apply(null, arguments);
};
};
and i did something totally analogous to #vilicvane's answer, except rather than throw new BreakSignal(), i'd written return null, and wrapped all subsequent .then callbacks in null_wrap like
then(null_wrap(function (res) { /* do things */ }))
i think this is a good answer b/c it avoids lots of indentation and b/c the OP specifically asked for a solution that doesn't throw. that said, i may go back and use something more like what #vilicvane did b/c some library's promises might return null to indicate something other than "break the chain", and that could be confusing.
this is more a call for more comments/answers than a "this is definitely the way to do it" answer.
Probably coming late the party here, but I recently posted an answer using generators and the co library that would answer this question (see solution 2):
https://stackoverflow.com/a/43166487/1337392
The code would be something like:
const requestHandler = function*() {
const survey = yield Survey.findOne({
_id: "bananasId"
});
if (survey !== null) {
console.log("use HTTP PUT instead!");
return;
}
try {
//saving empty object for demonstration purposes
yield(new Survey({}).save());
console.log("Saved Successfully !");
return;
}
catch (error) {
console.log(`Failed to save with error: ${error}`);
return;
}
};
co(requestHandler)
.then(() => {
console.log("finished!");
})
.catch(console.log);
You would pretty much write synchronous code that would be in reality asynchronous !
Hope it helps!
Try to use libs like thisone:
https://www.npmjs.com/package/promise-chain-break
db.getData()
.then(pb((data) => {
if (!data.someCheck()) {
tellSomeone();
// All other '.then' calls will be skiped
return pb.BREAK;
}
}))
.then(pb(() => {
}))
.then(pb(() => {
}))
.catch((error) => {
console.error(error);
});