Execute eval on dynamic expression - node.js

I have a dictionary that is params["actions"]["browser.evaluateJs"]. It has different expressions that need to be executed. I am trying to execute like this:
if("browser.evaluateJs" in params["actions"]){
for (let key of Object.keys(params["actions"]["browser.evaluateJs"])) {
console.log(params["actions"]["browser.evaluateJs"][key]);
ctx[key] = eval(await params["actions"]["browser.evaluateJs"][key]);
console.log(ctx[key]);
}
}
expression contained in that dictionary is page.waitForXPath('//div[#class='price-text']'). I am trying to scrape a website using pupeteer. And need to get this xpath. But it is giving this error:
UnhandledPromiseRejectionWarning: SyntaxError: missing ) after argument list
How do I fix it?
Edit:
I have used page.evaluate like this:
if("browser.evaluateJs" in params["actions"]){
for (let key of Object.keys(params["actions"]["browser.evaluateJs"])) {
console.log(params["actions"]["browser.evaluateJs"][key]);
ctx[key] = page.evaluate(params["actions"]["browser.evaluateJs"][key]);
console.log(ctx[key]);
}
}
and params["actions"]["browser.evaluateJs"][key] contains element => {return element.textContent;}, (await page.$x('//div[#class='price-text']'))[0]
but this gives Promise { }. How do I fix it?
Edit 2:
I put await like this:
ctx[key] = await page.evaluate(params["actions"]["browser.evaluateJs"][key]);
and it is throwing this error:
Error: Evaluation failed: SyntaxError: Unexpected identifier
Edit3:
Since Im reading it from yml file so I have put it like this:
mykey : "element => {return element.textContent;}, (await page.$x('//div[#class=\"price-text\"]'))[0]"
The error still persists

Related

Unable to add event listener to webNavigation.onCompleted

Using the mock function below along with the dev console:
This call will work:
chrome.webNavigation.onCompleted.addListener(processWebNavChange, filtera);
but when I actually pass in my real var filter it throws this error:
Uncaught TypeError: Could not add listener
My actual data looks like this:
{
url: [ {hostContains: ".im88rmbOwZ"} ]
}
function registerWebNavListener() {
var matchers = getUrlMatchers();
var filter = {
url: matchers
};
// test with mock data filtera that actually works
const filtera = {
url:
[
{hostContains: "example.com"},
]
}
if (matchers.length > 0) {
chrome.webNavigation.onCompleted.addListener(processWebNavChange, filtera);
}
}
async function processWebNavChange(data) {
}
Is there something wrong with my data structure that I'm actually using? I don't believe that the filter object I returned is incorrect
}
EDIT:
I added a new
const filterb = {
url: [ {hostContains: ".im88rmbOwZ"} ]
};
and it still fails with that. The single entry {hostContains: ".im88rmbOwZ"}, was the first item returned from getURLMatchers() which I used as an example of real data being returned.
The above comment on the upper-case letters was the cause of the issue. Converting everything to lowercase resolved the problem.
Although, I am not clear as to why that was a problem to begin with. (If there are any hints in the chromium source code event filter handlers, I'd appreciate it if it could be pointed out).

Fix for Typescript warnings for type ‘any’ with Cloud Functions for Firebase

I’m getting a number of warnings all relating to the use of ‘any’ as a return type for functions in my Typscript code. I am trying to write a node.js backend with Cloud Functions for Firebase, to manage Google Play Billing purchases and subscriptions.
I am following the examples given in the Classy Taxi Server example here:
https://github.com/android/play-billing-samples/tree/main/ClassyTaxiServer
For example, the following:
function purchaseToFirestoreObject(purchase: Purchase, skuType: SkuType): any {
const fObj: any = {};
Object.assign(fObj, purchase);
fObj.formOfPayment = GOOGLE_PLAY_FORM_OF_PAYMENT;
fObj.skuType = skuType;
return fObj;
}
Gives the warning
Unexpected any. Specify a different type. #typescript-eslint/no-explicit-any)
I have tried to change ‘any’ to ‘unknown’, but then I get an error
Property 'formOfPayment' does not exist on type 'unknown'.ts(2339)
and
Property 'skuType' does not exist on type 'unknown'.ts(2339)
In another function
export function mergePurchaseWithFirestorePurchaseRecord(purchase: Purchase, firestoreObject: any) {
// Copy all keys that exist in Firestore but not in Purchase object, to the Purchase object (ex. userID)
Object.keys(firestoreObject).map(key => {
// Skip the internal key-value pairs assigned by convertToFirestorePurchaseRecord()
if ((purchase[key] === undefined) && (FIRESTORE_OBJECT_INTERNAL_KEYS.indexOf(key) === -1)) {
purchase[key] = firestoreObject[key];
}
});
}
I get the following warnings
Missing return type on function. #typescript-eslint/explicit-module-boundary-types
Argument 'firestoreObject' should be typed with a non-any type. #typescript-eslint/explicit-module-boundary-types
Unexpected any. Specify a different type. #typescript-eslint/no-explicit-any
In this function, if I change ‘any’ to ‘unknown’, I still get a warning for
Missing return type on function.
In another example I get an error for the use of ‘any’ in this constructor:
export default class PurchaseManager {
constructor(private purchasesDbRef: CollectionReference, private playDeveloperApiClient: any) { }
And again the warning is
Argument 'playDeveloperApiClient' should be typed with a non-any type. #typescript-eslint/explicit-module-boundary-types
In this case, if I follow the suggestion to use ‘unknown’ instead of ‘any’, then I get an error for ‘purchases’ in the following function:
const apiResponse = await new Promise((resolve, reject) => {
this.playDeveloperApiClient.purchases.products.get({
packageName: packageName,
productId: sku,
token: purchaseToken,
}, (err, result) => {
if (err) {
reject(this.convertPlayAPIErrorToLibraryError(err));
} else {
resolve(result.data);
}
})
});
The error created by changing ‘any’ to ‘unknown’ in the constructor is:
Property 'purchases' does not exist on type 'unknown'.ts(2339)
If I understand correctly, I could prevent all of these (and other similar) warnings without creating errors, by disabling explicit-module-boundary-types, and/or no-explicit-any for the entire file, but I am not sure if this is bad practice?
Is there another (better) way to specify the return types to avoid using ‘any’?
Or is it fine to just go ahead and disable explicit-module-boundary-types or no-explicit-any?
If you do not want to use any, you will have to declare an interface for your type and set that as the return type of the function.
You can do something like:
interface MyType {
formOfPayment: string;
skyType: SkyType;
foo: //<any other pre-existing type>;
}
Then you can use this as the return type of any other function that requires you to return an object with above properties. e.g.
function purchaseToFirestoreObject(purchase: Purchase, skuType: SkuType): MyType {
const fObj: any = {};
Object.assign(fObj, purchase);
fObj.formOfPayment = GOOGLE_PLAY_FORM_OF_PAYMENT;
fObj.skuType = skuType;
return fObj;
}

Using find{ } on a map where the whole map is evaluated not each element

I created some mixin methods. Code and example below:
URL.metaClass.withCreds = { u, p ->
delegate.openConnection().tap {
setRequestProperty('Authorization', "Basic ${(u + ':' + p).bytes.encodeBase64()}")
}
}
URLConnection.metaClass.fetchJson = {
delegate.setRequestProperty('Accept', 'application/json')
delegate.connect()
def code = delegate.responseCode
def result = new JsonSlurper().parse(code >= 400 ? delegate.errorStream : delegate.inputStream as InputStream)
[
ok : code in (200..299),
body: result,
code: code
]
}
example usage:
new URL("$baseUrl/projects/$name").withCreds(u, p).fetchJson().find {
it.ok
}?.tap{
it.repos = getRepos(it.key).collectEntries { [(it.slug): it] }
}
}
When I dont use find(), my object is, as expected, a map with those 3 elements. When I use find it is a Map.Entry with key ok and value true
which produces this error:
groovy.lang.MissingPropertyException: No such property: ok for class: java.util.LinkedHashMap$Entry
Possible solutions: key
It occured to me when I wrote this post that it was treated the map as an iterable and thus looking at every entry which I have subsequently verified. How do I find on the whole map? I want it.ok because if it's true, I need to carry it forward
There is no such method in Groovy SDK. Map.find() runs over an entry set of the map you call method on. Based on expectation you have defined I'm guessing you are looking for a function that tests map with a given predicate and returns the map if it matches the predicate. You may add a function that does to through Map.metaClass (since you already add methods to URL and URLConnection classes). Consider following example:
Map.metaClass.continueIf = { Closure<Boolean> predicate ->
predicate(delegate) ? delegate : null
}
def map = [
ok : true,
body: '{"message": "ok"}',
code: 200
]
map.continueIf { it.ok }?.tap {
it.repos = "something"
}
println map
In this example we introduced a new method Map.continueIf(predicate) that tests if map matches given predicate and returns a null otherwise. Running above example produces following output:
[ok:true, body:{"message": "ok"}, code:200, repos:something]
If predicate is not met, map does not get modified.
Alternatively, for more strict design, you could make fetchJson() method returning an object with corresponding onSuccess() and onError() methods so you can express more clearly that you add repos when you get a successful response and optionally you create an error response otherwise.
I hope it helps.

Office JS issue with recognising ListItems

I'm trying to add a paragraph at the end of the document and escape the possibility of the newly added paragraph to be added inside a list (if the document is ending with a list).
I have the following code:
let paragraph = paragraphs.items[paragraphs.items.length - 1]
let p = paragraph.insertParagraph('', window.Word.InsertLocation.after)
if (paragraph.listItemOrNullObject) {
p.detachFromList()
p.leftIndent = 0
}
The following happens: if there is a ListItem, the code works. If not, it breaks inside the if condition, like I wrote paragraph.listItem.
Shouldn't this be used like this?
EDIT - error thrown:
name:"OfficeExtension.Error"
code:"GeneralException"
message:"GeneralException"
traceMessages:[] 0 items
innerError:null
▶debugInfo:{} 4 keys
code:"GeneralException"
message:"GeneralException"
toString:function (){return JSON.stringify(this)}
errorLocation:"Paragraph.detachFromList"
the issue here is that the *.isNullObject methods/properties does not return a regular js 'null' object, but a NullObject (a special framework type of null).
check out this code i rewrote it i think in a more efficient way. excuse my js, you can port it to ts.
hope this helps.
Word.run(function (context) {
var listI = context.document.body.paragraphs.getLast().listItemOrNullObject;
context.load(listI);
return context.sync()
.then(function () {
if (listI.isNullObject) { // check out how i am validating if its null.
console.log("there is no list at the end")
}
else {
context.document.body.paragraphs.getLast().detachFromList();
context.document.body.paragraphs.getLast().leftIndent = 0;
return context.sync();
}
})
})
listItemOrNullObject will return a null object if it isn't a ListItem. Conceptually you're if is asking "if this is a list item or it isn't a list item" which effectively will also return true.
It is failing here you are attempting to detach from a non-existent list. I would take a look at isListItem. This will tell you specifically if the paragraph is a ListItem so you only execute p.detachFromList() when in fact it is part of a list.

chai-smoothie: to.eventually.be.displayed on a function doesn't work?

First-off, thanks for the chai-smoothie!
I've been trying to polish some of the test code and use chai-smoothie more, but I have ran into some trouble:
This is what it looked like before:
return expect(Promise.all([
userMenuPage.userMenuItemIcon.isDisplayed(),
userMenuPage.userMenuItemText().isDisplayed()
])).to.eventually.be.eql([true, true])
and this is how I expected it to work in chai smoothie
expect(userMenuPage.userMenuItemIcon).to.eventually.be.displayed
expect(userMenuPage.userMenuItemText()).to.eventually.be.displayed
when running the new code, I receive the following error:
Step Definition: steps/userMenuSteps.js:23
Message:
TypeError: assertion._obj.locator is not a function
at <repo>/tests/node_modules/chai-smoothie/lib/src/chai-smoothie.ts:42:65
at <repo>/tests/node_modules/protractor/built/element.js:798:32
at ManagedPromise.invokeCallback_ (<repo>/tests/node_modules/selenium-webdriver/lib/promise.js:1379:14)
at TaskQueue.execute_ (<repo>/tests/node_modules/selenium-webdriver/lib/promise.js:2913:14)
at TaskQueue.executeNext_ (<repo>/tests/node_modules/selenium-webdriver/lib/promise.js:2896:21)
at asyncRun (<repo>/tests/node_modules/selenium-webdriver/lib/promise.js:2775:27)
at <repo>/tests/node_modules/selenium-webdriver/lib/promise.js:639:7
at process._tickCallback (internal/process/next_tick.js:103:7)
Page model definition looks like this:
userMenuItemText: () => $('#desktop-menu #desktop-menu-classic span').getText(),
userMenuItemIcon: $('#desktop-menu #desktop-menu-classic .fa-fjunk'),
The issue is with the second row "userMenuPage.userMenuItemText().isDisplayed()". If I just use "userMenuPage.userMenuItemIcon.isDisplayed()", then I get no problem, and if I just use "userMenuPage.userMenuItemText().isDisplayed()", then I get the failure.
Do you have any recommendationts on how to overcome this and still use chai smoothie?
The problem is caused by the assertion to.eventually.be.displayed applied to the text of the element, rather than the element itself.
Update: as of version 0.2.1, chai-smoothie can work with either.
Hope this helps!
Jan
Nevertheless, it might be cleaner to assert on the element to verify its visibility, and on its text to verify the contents:
So instead of:
const userMenuPage = {
userMenuItemText: () => $('#desktop-menu #desktop-menu-classic span').getText(),
userMenuItemIcon: $('#desktop-menu #desktop-menu-classic .fa-fjunk')
}
maybe try:
const userMenuPage = {
userMenuItem: $('#desktop-menu #desktop-menu-classic span'),
userMenuItemIcon: $('#desktop-menu #desktop-menu-classic .fa-fjunk')
}
and then:
expect(userMenuPage.userMenuItem).to.eventually.be.displayed
expect(userMenuPage.userMenuItem.getText()).to.eventually.equal('expected text')
With Serenity/JS you could also define the page object as follows:
class UserMenuPage {
static Item = Target.the('Menu item').located(by.css('#desktop-menu #desktop-menu-classic span');
static Item_Text = Text.of(UserMenuPage.Item);
static Item_Icon = Target.the('Menu icon').located(by.css('#desktop-menu #desktop-menu-classic .fa-fjunk');
}
which allows you to define "questions" such as UserMenuPage.Item_Text in the same place as targets and avoid having to call .getText() in the scenario.

Resources