puppeteer evaluate functions not accepting JSHandles or ElementHandles as variables - javascript

I can pass native objects (strings, lists, numbers, etc) just fine, but whenever I try to pass in a JSHandle or ElementHandle, I get this error msg: "TypeError: Converting circular structure to JSON Are you passing a nested JSHandle? at JSON.stringify ()"
This is my code:
async getColumnsNames(id='dashboardGrid') {
await this.loadObjects();
const grid = this.table
const detectedColumns = await this.page.evaluate(({grid}) => {
//const grid = document.getElementById(id)
const header = grid.getElementsByClassName('ag-header-viewport')[0]
const labelRow = header.getElementsByClassName('ag-header-row')[0]
const cells = labelRow.getElementsByClassName('ag-header-cell-text')
var children = []
for(x = 0; x < cells.length; x++) {
cells[x].innerText.length > 0 ? children.push(cells[x].innerText) : null
}
return children
}, {grid});
console.log(detectedColumns)
}
So if I pass 'id' to obtain inside the 'grid' element, no problem (it's a CSS selector), but if I try to pass a handle (grid) directly, I get that error msg.
Said handle is exactly this:
this.table = await this.page.$(this.tableID);
Which totally works elsewhere for detecting the corresponding DOM element (this.tableID is '[id="dashboardGrid"]'), so I know the handle is NOT the problem.

The problem is that when you build your first argument like this {grid}, the resulting object is not an instance of JSHandle, so the evaluate won't treat that correctly.
This should work
const detectedColumns = await this.page.evaluate(grid => {
//Some code
}, grid);

Related

React State is Changing Without Using setState()

I'm developing a quiz/test website. I want to see different questions when I move to next question and don't want to see same answers at the same time.
I have a state array varaible which is calling allWords. This state
will keep all words.
And I have another state array variable which calls like
neverAskedWords. This state will keep words which never used
always.
I'm creating a new array variable and defining with allWords in a function. When I'm removing any record in the new array variable then that record is removing in allWords variable as well... Why?
I want to remove any record in that temporary array and want to save updated version to neverAskedWords state. In this way I could see different questions always. Here is my codes.
const [allWords, setAllWords] = useState([])
const [neverAskedWords, setNeverAskedWords] = useState([])
async function getAllData(){
axios
.get(`http://127.0.0.1:3000/api/improve-language`)
.then(res => {
setAllWords(res.data)//defining allWords
setNeverAskedWords(res.data)//defining neverAskedWords
firstQuestionAndAnswers(res.data)//sending all datas by parameter, bacause when I'm trying to get datas by using `allWords` state, it would be undefined. That's why sending all data by parameter for the first time to set first question and answers.
})
.catch(err =>{
console.log(err)
})
}
async function firstQuestionAndAnswers(wordsList){
let neverAskedList = await wordsList //creating and defining temporary variables
const allWordsList = await wordsList //creating and defining temporary variables
//some not necessary codes for this issue
const questionIndex = randomNumber(neverAskedList.length)
const firstQuestion = neverAskedList[questionIndex]
let firstAnswers = []
for (let i = 0; i < 4; i++) {
let answerIndex = randomNumber(allWordsList.length)
firstAnswers[i] = allWordsList[answerIndex]
allWordsList.splice(answerIndex, 1)//and here! I'm removing this record to prevent using it again next time, there will be different answers always
}
//some not necessary codes for this issue
firstAnswers.push(firstQuestion)
const randomisedAnswers = firstAnswers.sort(()=>Math.random() - 0.5)
//some not necessary codes for this issue
setQuestion(firstQuestion)
setAnswers(randomisedAnswers)
//and then here! I'm removing the used question in this time to prevent using it again, there will be different questions always and never see this question again
neverAskedList.splice(questionIndex, 1)
setNeverAskedWords(neverAskedList)
}
allWords should'nt change. But changing, because of why?
So the most obvious thing that I see in your code is that you are modifying the same object. What you should do instead is use the spread operator.
const [allWords, setAllWords] = useState([])
const [neverAskedWords, setNeverAskedWords] = useState([])
async function getAllData(){
axios
.get(`http://127.0.0.1:3000/api/improve-language`)
.then(res => {
setAllWords(res.data)//defining allWords
setNeverAskedWords(res.data)//defining neverAskedWords
firstQuestionAndAnswers(res.data)//sending all datas by parameter, bacause when I'm trying to get datas by using `allWords` state, it would be undefined. That's why sending all data by parameter for the first time to set first question and answers.
})
.catch(err =>{
console.log(err)
})
}
async function firstQuestionAndAnswers(wordsList){
// don't use await for js objects, should be used only with promises.
// use spread operator to make copy of the wordList array so you never actually modify the original object
let neverAskedList = [...wordsList]
const allWordsList = [...wordsList]
//some not necessary codes for this issue
const questionIndex = randomNumber(neverAskedList.length)
const firstQuestion = neverAskedList[questionIndex]
let firstAnswers = []
for (let i = 0; i < 4; i++) {
let answerIndex = randomNumber(allWordsList.length)
firstAnswers[i] = allWordsList[answerIndex]
allWordsList.splice(answerIndex, 1)//and here! I'm removing this record to prevent using it again next time, there will be different answers always
}
//some not necessary codes for this issue
firstAnswers.push(firstQuestion)
const randomisedAnswers = firstAnswers.sort(()=>Math.random() - 0.5)
//some not necessary codes for this issue
setQuestion(firstQuestion)
setAnswers(randomisedAnswers)
//and then here! I'm removing the used question in this time to prevent using it again, there will be different questions always and never see this question again
neverAskedList.splice(questionIndex, 1)
setNeverAskedWords(neverAskedList)
}
If you don't understand why it happened then here's a short explanation. In js when you do const a = { key: 'val' } you created a variable that references the memory block that is actually storing your object. And when you do const b = a you are creating another variable that references the same memory block. So updating 1 automatically changes the other one.

concatenating two tf.data.Dataset gives "this.lastRead.then is not a function" error in tensorflow.js

I am trying to append a new row in tf.data.Dataset in tensorflow.js, and after searching i figured that the way to do this is by turning the new row which is originally a json object into a dataset object then concatenate it with the pervious one, but i ended up facing this error
"this.lastRead.then is not a function"
I tried to debug it so i tried to concatenate the same dataset with it self and faced the same problem:
csvUrl = 'https://storage.googleapis.com/tfjs-examples/multivariate-linear-regression/data/boston-housing-train.csv';
const a = tf.data.csv(
csvUrl, {
columnConfigs: {
medv: {
isLabel: true
}
}
});
const b = a.concatenate(a);
await b.forEachAsync(e => console.log(e));
and got the same error message, can you help me out?
Currently there is a bug that prevents to concatenate datasets. Until it is deployed in a new version, the dataset iterators can be utilized to do a concatenation. Here is an example:
const csvUrl =
'https://storage.googleapis.com/tfjs-examples/multivariate-linear-regression/data/boston-housing-train.csv';
async function run() {
const csvDataset = tf.data.csv(
csvUrl, {
columnConfigs: {
medv: {
isLabel: true
}
}
});
const numOfFeatures = (await csvDataset.columnNames()).length - 1;
// Prepare the Dataset for training.
const flattenedDataset =
csvDataset
.map(({xs, ys}) =>
{
// Convert xs(features) and ys(labels) from object form (keyed by
// column name) to array form.
return {xs:Object.values(xs), ys:Object.values(ys)};
})
//.batch(10);
const it = await flattenedDataset.iterator()
const it2 = await flattenedDataset.iterator()
const xs = []
const ys = []
// read only the data for the first 5 rows
// all the data need not to be read once
// since it will consume a lot of memory
for (let i = 0; i < 5; i++) {
let e = await it.next()
let f = await it2.next()
xs.push(e.value.xs.concat(f.value.xs))
ys.push(e.value.ys.concat(f.value.ys))
}
const features = tf.tensor(xs)
const labels = tf.tensor(ys)
console.log(features.shape)
console.log(labels.shape)
}
await run();
The only thing to keep in mind is that the above will load all the tensors in memory which cannot be ideal depending on the size of the dataset. A data generator can be used to reduce the memory footprint. here is a very detailed answer to show how to do it
apparently this was a bug in the tensorflow.js library. The bug has been fixed in this pull request: https://github.com/tensorflow/tfjs/pull/5444
Thank you.
Edit
The pull request has been merged and now it is in the version 3.9.0

Properly append to an array when handling presses

Within my function, through interaction from the user, I aim slowly build up an array of responses which I then pass off to an API. However, different approaches to append to the array, simply return a single position array (overwrite).
My current code as follows:
const contribution: Array = [];
const handlePress = () => {
var col = {
response,
user: 1,
update: update.id,
question: q.id,
};
contribution = [...contribution, col];
}
My understanding is that contribution = [...contribution, col] is the correct way to add to the array.
What is the best practice approach for doing this inside a function called each time the user interacts?
Although it is not clear from the question, I suspect, this code is inside a component. If so, then a new contribution array is created on every render. You need to use useState to store this array so that a new array is not created on every render.
const [contribution, setContribution] = React.useState([]);
const handlePress = () => {
var col = {
response,
user: 1,
update: update.id,
question: q.id,
};
setContribution([...contribution, col]);
}

How to create and return an array of objects using protractor page object model design and promises

I'm creating a page object model and one of the properties is all the users from a table. The table has a few columns so I'd like to parse that table and create a user object for each row, then return that set to then be used in tests. So far, this is what that property of my page object looks like:
users: {
get: function() {
let userRecords = [];
var users = element.all(by.repeater('user in users')).each(function(tr, index) {
let user = {
name: tr.element(by.css('td:nth-child(2)')).getText().then(text => {return text;}),
role: tr.element(by.css('td:nth-child(3)')).getText().then(text => {expect(text).toBeTruthy();}),
status: tr.element(by.css('td:nth-child(4)')).getText().then(text => {expect(text).toBeTruthy();}),
//actionsButton: tr.element(by.css('btn btn-default'))
};
userRecords += user;
}).then(userRecords => {return userRecords});
return userRecords;
}
},
Through trial and error I encounter one of two outcomes when just trying to print to screen each element of userRecords:
each element prints as undefined or
userRecords is not defined.
Just to reiterate, I'm simply trying to build an array that holds each user as an object to then be able to iterate / filter on that set in my tests.
Given the approach I'm taking, what's the ideal way to construct this user array and resolve the promises?
Edit: it's worth noting that if I do a console.log() within each of the getText().then() statements, it does print the correct data from the table. So, I do know that it's reading the table correctly.
I'd go with a method that returns json, and would make it async
users: async function() {
let userRecords = [];
var users = element.all(by.repeater('user in users'));
for (let i = 0; i < await users.count(); i++) {
let tr = users.get(i);
let user = {
name: await tr.element(by.css('td:nth-child(2)')).getText(),
role: await tr.element(by.css('td:nth-child(3)')).getText(),
status: await tr.element(by.css('td:nth-child(4)')).getText()
};
userRecords.push()
}
return userRecords;
},
and then use:
console.log(
JSON.stringify(
await constructorName.users()
)
)
should be as simple as that. Note, I didn't test the code, but I did use the approach in my experience. So it may require some minor modifications
In general, try to avoid .then - async/await is easier to use, .each - go with for loop instead. Also userRecords += user; doesn't do what you think it does (though I may be wrong here)

Cannot read a public array item inside chained AXIO transactions in VUE

I have a chained AXIOS call that is being called from an array. I need to have the second call finish before the first one makes another API request and the process works fine. My only problem is that I can't reference anything from inside the response of the second chain. I need to update either the calling array(arr) a public array this.excelData.results or even a deep copy of the original array. Every time I get an error saying:
err = TypeError: Cannot read property 'ORCID' of undefined at eval (webpack-
internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-
loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-
loader/lib/index.js?!./src/views/examples/ExcelWorksheet.vue?vue&type=script&lang=js&:410:49) at
NodeList.forEach (<anonymous>) at eval (webpack-internal:///./node_modules/cache-
loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-
loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/examples/ExcelWorksheet.vue?
vue&type=script&lang=js&:381:28) at NodeList.forEach (<anonymous>) at eval (webpack-
internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-
loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-
loader/lib/index.js?!./src/views/examples/ExcelWorksheet.vue?vue&type=script&lang=js&:378:33)
I have tried adding this or self but nothing seems to help. As I debug in a browser I can see all of the elements of the array but I can't read them in code or change them. Is there something special that I need to do so that I can use the data from the second AXIOS call in the original array?
lookUpORCID(){
this.pubmedArticles=[];
this.makeRequestsFromArray(this.excelData.results);
},
makeRequestsFromArray(arr) {
var self = this
let index = 0;
function request() {
let authorName = arr[index]["Last_Name"]
let linkGetIdList = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?';
linkGetIdList += 'tool=biosketch&db=pubmed&retmode=json&retmax=200&';
linkGetIdList += 'term=' + authorName +'[AU] ';
return axios.get(linkGetIdList).then((res) => {
index++;
let idlist = res.data.esearchresult.idlist
const passID = idlist.join(',')
if (idlist.length > 0) {
var getXmlLink = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?';
getXmlLink += 'db=pubmed&retmode=xml&id=${passID}';
return axios.get(getXmlLink).then((response) => {
// parse document
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(response.data, 'text/xml') // Get ALL XML content
// Do Stuff
**This is the spot that I need to change value in array**
let xyz = arr[index]["ORCID"]
return request();
})
}
if (index >= arr.length) {
return 'done'
}
});
}
return request();
},
You are able to access the arr or the error would have been: cannot read property index of undefined.
You have incremented index after the first request & the arr has the value undefined at the incremented index, so the error says Cannot read property 'ORCID' of undefined.
You can try console.log(arr[index]) before let xyz = arr[index]["ORCID"] to verify above.

Categories

Resources