nodeId to Element handle - puppeteer

Is there some elegant way how to get an element handle from nodeId? I get a list of all nodes (and nodeIds) by
const nodes = await page._client.send("DOM.querySelectorAll", {
nodeId: doc.root.nodeId,
selector: "*"
});
where nodes.nodeIds is a list of integers. And somehow I would like to run
const html = await page.$eval(nodeId, node => node.offsetParent === null);
To check if the element is visible. The problem is that page.$eval expects string (selector) as a first argument.
I could add a custom attribute through DOM.setAttributeValue and then query that through page.$eval but that seems hacky. So is there some more direct way how to go from devtool-protocol nodeIds to Puppeteer's ElementHandle?

So there is a way how to go from Puppeteer's ElementHandle to nodeId.
const element = page.$('a');
const node = await page._client.send("DOM.requestNode", {
objectId: element._remoteObject.objectId
});
// <- { nodeId: 1 }
My original question asks for the other direction (nodeId -> ElementHandle) but I can work with this. It's also more practical to use Puppeteer by default whenever possible.

Related

Conditionally make a page read-only using react

I want to create a React webpage that has both editable and read-only versions, the whole page not just a few elements on the page. A version is displayed to the user based on user id and other conditions. How do I do it?
The only straight forward way I know is to create 2 pages one editable and one read-only and based on the condition show the appropriate version (html page) to the user.
Is there a better and smarter way to do this? Like can I create just one page for both versions and toggle the mode based on the condition to the users?
Your question should have provided an example of some code you had tried but based on the description, very rough example below of one of many possible solutions.
Suppose EditView component is your page and you are able to pass a value for permission based on whatever credential you need to apply.
Then you have a component, ExampleField that takes the permission and displays either an input or static text. A collection of multiple of these fields is mapped from a theoretical array of data that you'll have to fetch from somewhere and the fields are returned by the main component.
const EditView = ({permission}) => {
const [editable, setEditable] = useState();
const [values, setValues] = useState([]);
useEffect(() => {
setEditable(permission);
}, [permission]);
useEffect(() => {
//maybe fetch your data from a back end or whatever and assign it to `values`
//on page load
}, [])
const ExampleField = ({permission, val, index}) => {
const handleChange = (e) => {
let vals = [...values];
vals[index] = val;
setValues(vals);
}
return(
<>
{permission
? <input name="example" type="text" defaultValue={val}
onChange={handleChange} />
: <span>{val}</span>}
</>
)
}
const fields = values.map((value, i) => {
return <ExampleField permission={permission} val={value} index={i}/>
})
return(
<>
{fields}
</>
)
}
Most likely, you'll want to break out various field components into their own file and, instead of using useState, you would probably want to explore useContext or useStore type functionality to lift up your state and do all the react things.
*Haven't tested or even compiled this code - for illustration purposes only.

Testcafe: How to grab the text not from html code (selector) but in field on UI

I need to extract parts of string from the text which was written in the field (input) on UI (This text is not in HTML code).
I am trying sth like this (but it does not work).
const textInput = await model.inputtTittle.textContent;
console.log(textInput)
Nothing return probably textContent take text from the selector, I was trying with .innerText but it also returned nothing.
And then I would like to write sth like this:
if (textInput.length > 32)
await t.typeText(model.inputTittle, textInput.substr(0, 30));
I hope that it will be work if I have the content of the field inputTittle.
Additional question:
This answer is hidden. This answer was deleted via review 16 hours ago by Jason Aller, Mark Rotteveel, Nico Haase, Botje.
This code works:
const textTittle = await model.inputTittle.value;
const textlength = textTittle.length
if (textlength>32)
{
console.log(textTittle.substr(0,30));
}
why i can not to writte shorter:
if (await model.inputTittle.value.length >32)
{ console.log(await model.inputTittle.value.substr(0,30));}
You can obtain the entire DOM Node Snapshot with all properties in one object to check what properties you need. It is likely you need the value property.

How to define a variable that's gonna be retrieved from localstorage (Chrome extension)?

I have a variable defined like this (not sure if it should be with let or var in the first place):
let activated = false;
The first thing that the extension should do is check the value of activated. I think this is the correct syntax:
chrome.storage.local.get(['activated'], function(result) {
activated = result.activated
alert ("activated: " + result.activated)
});
After some logic, I want to change activetedto true, with this syntax:
chrome.storage.local.set({activated: true}, function() {
console.log("activated changed to true: " + activated)
});
However, when I close and open the browser again, activatedis set to false again.
How should I structure this in order to achieve the desired result?
The way to acess a localstorage variable isn't by defining as I was doing in let activated = false;.
The way to add the variable retrieved from localstorage to the program's control flow should be done this way:
chrome.storage.local.get(['activated'], function(result) {
if (result.activated == value) { // Do something }
});

Read long text in Angular 2

I have a very long document - 40000 words - I would like to display in a styled manner, like html.
I need to display it with headers, paragraphs and bold styling.
I am building an Angular app. I tried loading the converted document as a local html, but it takes a very long time.
For instance, I tried this:
var html = this.http.get("../data.html").map(ref => {
console.log(html);
} );
Are there any other ways I can load this text? Maybe break it up into smaller chunks somehow?
Based on what you've provided with no other context:
You need to subscribe to the Observable otherwise, nothing will ever happen since Observable execution is lazy:
var html = this.http.get("../data.html")
.map(ref => {
console.log(html);
return ref;
})
.subscribe(ref => ...);
Also, you're using console.log(html) in your map, but html does not exist in the context of map so you would need to do something like:
var html = this.http.get("../data.html")
.map(ref => {
console.log(ref); // Does this log appear and what does it contain?
return ref;
})
.subscribe(ref => ...);
Finally, var html is an Observable not HTML so I'd probably rename this to something a bit more descriptive if you're passing it around and subscribing to the response:
const data$ = this.http.get("../data.html")
.map(ref => {
console.log(ref);
return ref;
});
// ... do other stuff
data$.subscribe(ref => ...);
Or if not passed chain it and subscribe which indicates the Observeable has completed:
this.http.get("../data.html")
.map(ref => {
console.log(ref);
return ref;
}).subscribe(ref => ...);
If this doesn't help answer the question it's because you haven't provided enough information, and I'd suggest answering:
In the second example what does console.log(ref) output?
Include more code that provides more context like do you use subscribe already and what does the data you're using look like?
Make an example in StackBlitz that replicates the issue. Just click Angular and you get a pre-made Angular application you can drop your code into and then people can hack directly on the issue. Takes tops 5 seconds to setup

Firebase on('child_added' is adding more unwanted childs / .once returns only the last one in the array

Working on a node script to automatically call Google's pagespeed api upon giving the sites listed in a json file.
{
"1" : "https://bitbucket.org",
"2" : "https://www.catswhocode.com/blog/"
}
The aim is to add the results for the api call to firebase json database along with a top-level url node to store the firebase key for the sitestats entered. Here is example firebase data structure I need.
sites
-KrehhWxld7XlKCuFSHRY
stats { ... }
url: "https://bitbucket.org"
-KrehhXAWlYdjAOA9sd95
stats { ... }
url: "https://stackoverflow.com"
urls :
393957be871e209a76e0dc5df1f526ec : -KrehhWxld7XlKCuFSHRY
7f4c919540be6ec81cd37d9e61da6c37 : -KrehhXAWlYdjAOA9sd95
Within a promise I am adding a reference for the nodes in firebase json database.
Promise.all(allRes).then( function( values ) {
var ref = db.ref();
var sitesRef = ref.child("sites");
var urlsRef = ref.child("urls");
var psiresults = {};
SitesArray.map(function (sites, index) {
psiresults[sites] = values[index]
desktopStats = values[index].desktopStats;
mobileStats = values[index].mobileStats;
sitesRef.push({
url: sites,
stats: metricsResponse
});
sitesRef.once('child_added', function(snapshot) {
console.log(snapshot.key) //returns the last key only
});
});
When using once('child_added', ... it adds to the url top level node only for the very last item. However, if on('child_added', ... multiple copies of the same data and other childs are added. Not sure how to add the exact child key to the urls top level node when a child is added to sites node each time.
If you want to process all URLs once, use once('value' with snapshot.forEach():
sitesRef.once('value', function(snapshot) {
snapshot.forEach(function(child) {
console.log(child.key);
});
});
Finally, I had to do it using..
sitesRef.on('value', function(snapshot) {
...
});
This returns all the children and then I am able to filter the result based on my SitesArray values. I don't really find this an appropriate solution, however, I was able to make it work.