I am working with a database app and I sometimes I have to click on tens or even hundreds of same buttons on a web page so I thought I save some time and run a script in the Inspect -> Console window of the Crome browser to do the job for me. I found a script which does the job just fine, however the database app hangs up after the first 10-20 clicks so I spoke with our developers and they advised that there should be a small delay between the clicks. They were not sure how much though so I need to experiment with it. So, by now I am trying to run the following script:
javascript:var inputs = document.getElementsByClassName('BUTTON CLASS HERE');
$(function theLoop (i) {
setTimeout(function () {
inputs[i].click();
if (--i) {
theLoop(i);
}
}, 100);
})(inputs.length);
It does nothing, but gives me the following error message:
gE02JAse0g9.js:60 ErrorUtils caught an error: "Cannot read property 'click' of undefined". Subsequent errors won't be logged
There seems to be some problem with calling the inputs[i].click() within the SetTimeout function, because it works just fine if I run it in a simple for loop like this:
javascript:var inputs = document.getElementsByClassName('BUTTON CLASS HERE');
for(var i=0; i<inputs.length;i++) {
inputs[i].click();
}
What am I doing wrong?
Thanks.
inputs[inputs.length] (on your first iteration, i === inputs.length) will always be undefined - array-like objects are zero-indexed in Javascript, so when inputs[i].click(); is encountered, an error is thrown.
Either initialize i to inputs.length - 1 instead, or you might find it easier to use your original code and simply await a Promise that resolves after 100ms or so:
const resolveAfter100MS = () => new Promise(res => setTimeout(res, 100));
(async () => {
var inputs = document.getElementsByClassName('_42ft _4jy0');
for(var i=0; i<inputs.length;i++) {
inputs[i].click();
await resolveAfter100MS();
}
})();
Related
I am working on a E2E test for a single-page web application in Angular2.
There are lots of clickable tags (not redirected to other pages but has some css effect when clicking) on the page, with some logic between them. What I am trying to do is,
randomly click a tag,
check to see the the response from the page is correct or not (need to grab many components from the web to do this),
then unclick it.
I set two const as totalRound and ITER, which I would load the webpage totalRound times, then within each loading page, I would randomly choose and click button ITER times.
My code structure is like:
let totalRound: number = 10;
let ITER: number = 100;
describe('XX Test', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
describe('Simulate User\'s Click & Unclick',() => {
for(let round = 0; round < totalRound; round++){
it('Click Simulation Round ' + round, () =>{
page.navigateTo('');
let allTagFinder = element.all(by.css('someCSS'));
allTagFinder.getText().then(function(tags){
let isMatched: boolean = True;
let innerTurn = 0;
for(let i = 0; i < ITER; i++){
/* Randomly select a button from allTagFinder,
using async func. eg. getText() to get more info
about the page, then check if the logic is correct or not.
If not correct, set isMatchTemp, a local variable to False*/
isMatched = isMatched && isMatchTemp;
innerTurn += 1;
if(innerTurn == ITER - 1){
expect(isMatched).toEqual(true);
}
}
});
});
}
});
});
I want to get a result after every ITER button checks from a loading page. Inside the for loop, the code is nested for async functions like getText(), etc..
In most time, the code performs correctly (looks the button checkings are in sequential). But still sometimes, it seems 2 iterations' information were conflicted. I guess there is some problem with my code structure for the async.
I thought JS is single-thread. (didn't take OS, correct me if wrong) So in the for loop, after all async. function finish initialization, all nested async. function (one for each loop) still has to run one by one, as what I wish? So in the most, the code still perform as what I hope?
I tried to add a lock in the for loop,
like:
while(i > innerTurn){
;
}
I wish this could force the loop to be run sequentially. So for the async. func from index 1 to ITER-1, it has to wait the first async. finish its work and increment the innerTurn by 1. But it just cannot even get the first async. (i=0) back...
Finally I used promise to solve the problem.
Basically, I put every small sync/async function into separate promises then use chaining to make sure the later function will only be called after the previous was resolved.
For the ITER for loop problem, I used a recursion plus promise approach:
var clickTest = function(prefix, numLeft, ITER, tagList, tagGsLen){
if(numLeft == 0){
return Promise.resolve();
}
return singleClickTest(prefix, numLeft, ITER, tagList, tagGsLen).then(function(){
clickTest(prefix, numLeft - 1, ITER, tagList, tagGsLen);
}).catch((hasError) => { expect(hasError).toEqual(false); });
}
So, each single clicking test will return a resolve signal when finished. Only then, the next round will be run, and the numLeft will decrease by 1. The whole test will end when numLeft gets to 0.
Also, I tried to use Python to rewrite the whole program. It seems the code can run in sequential easily. I didn't met the problems in Protractor and everything works for my first try. The application I need to test has a relatively simple logic so native Selenium seemed to be a better choice for me since it does not require to run with Frond-end code(just visit the webapp url and grab data and do process) and I am more confident with Python.
I am doing a Web Scraping Project with Youtube. I need to automatically scroll to the bottom via Javascript. I am inputting the command in Google Chrome Inspect Console Terminal.
This link: https://stackoverflow.com/a/29971996/3326078 showed me to use this:
window.scrollTo(0, document.body.scrollHeight ||document.documentElement.scrollHeight);
Above works as expected.
However when I do,
for (var i = 0; i < 5; i++) {
setTimeout(window.scrollTo(0, document.body.scrollHeight
|| document.documentElement.scrollHeight),5)}
it doesn't repeat more than once. Can someone explain to me whether what I am trying to do is possible. Again, I am inputting this command in the Google Chrome Inspector Console.
Thanks!
A for loop isn't going to wait for setTimeout to complete before it continues running. If you want the scrolling to wait for setTimeout to finish and run a certain amount of times, you'll need a function that calls the timeout and the calling function again inside of it.
Many ways to do this, but here is my implementation:
var scrollToBottomWithTimeout = function (param_iterations, param_wait_time) {
setTimeout(function () {
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
if (param_iterations > 0) {
param_iterations = param_iterations - 1;
scrollToBottomWithTimeout(param_iterations, param_wait_time);
}
}, param_wait_time);
};
Once declared, this function can be called like so:
scrollToBottomWithTimeout(5, 1000);
The param_wait_time in milliseconds (thousandths of a second).
I've been created a demo for RxJS scan() method but unfortunately my timer doesn't work properly and I get this error: Timer 'myTimer' does not exist
console.time('myTimer');
let source = Rx.Observable
.interval(100) // interval starts from 0
.take(4)
.scan((acc, val) => acc + val);
source.subscribe((value) => {
console.timeEnd('myTimer');
console.log('in next. Value: ', value);
});
Here is a demo in JSBin.
Here is a source that I Copy code from that.
How can fix that issue?
Once you've stopped the timer with console.timeEnd("name") it no longer exists when using chrome.
console.time("myTimer");
for(var i=0;i<10000;i++){
}
console.timeEnd("myTimer"); // works
console.timeEnd("myTimer"); // displays an error (in chrome only)
Which is pretty much what your code is doing. The first time subscribe is called your timer outputs the amount of time since it ws started. On the 3 subsequent calls it does not work.
This behaviour is specific to Chrome, it works how you expect in both IE & Firefox.
I'm trying to make a javascript script running from the urlbar which for each row on a specific page clicks a button (allowing me to change a certain value), then edit that value, then click that button again to confirm. I'm running into a lot of trouble with simultaneity, as it seems to wait to do the first click 5000 ms even tho it's not being told to pause. didn't do the same when i tested the structure replacing things in between pauses with rando alerts
The reason for the pauses is bc there's a short loading animation during which I can't execute code. Anything glaringly wrong in this as-is? I know there's funky stuff w/ closure, but I thought by busywaiting i could get past it but its not.
function pause(milliseconds) {
var dt = new Date();
while ((new Date()) - dt <= milliseconds) { /* Do nothing */ }
}
var q=document.getElementById("MainContent_gvProducts").children[0];
var l=q.children.length;
for(i=1;i<l;i++){
q.children[i].children[0].children[0].click();
pause(5000);
var x=q.children[i].children[7].children[0].value;
pause(1);
x=math.round(x);
pause(5000);
q.children[i].children[0].children[0].click();
pause(5000);
}
Your pause function seems very hacky.
You should use setTimeout() instead, or something similar.
If you can afford to use the latest JS tech, I'd recommand creating a promise with a timeout and using async/await, with something like this :
async function pause(t = 1000) {
return new Promise(resolve => {
setTimeout(() => {
resolve(true)
}, t)
})
}
for(i=1;i<l;i++){
q.children[i].children[0].children[0].click();
await pause(1000)
var x=q.children[i].children[7].children[0].value;
pause(1);
x=math.round(x);
await pause(5000);
q.children[i].children[0].children[0].click();
await pause(5000);
}
Note : your for loop must be placed in an async function.
I've successfully navigated to the corresponding page where I want to select multiple elements and click a button to confirm. The selection of the elements work, I've confirmed this with a screenshot, but clicking the button in nightmare does not work. When I run the segment in the console, everything works fine. The button has a randomly defined ID, and everything else except the innerHTML of the button is not unique, so I iterate over all buttons to match it based on content.
It's this snippet that's relevant.
.evaluate(function(){
//Select all the "elements" for room.
var elemArr = document.getElementById("L210").getElementsByTagName("td");
document.getElementById("resRoom").innerHTML = "L210";
document.getElementById("resStartTime").innerHTML = "08:00";
document.getElementById("resEndTime").innerHTML = "19:00";
for(var i = 0; i < elemArr.length; i++){
elemArr[i].className += " selected"
}
//Here select and click the button
var bTags = document.getElementsByTagName("button");
var searchText = "Confirm";
for (var i = 0; i < bTags.length; i++) {
if (bTags[i].innerHTML == searchText) {
bTags[i].click();
break;
}
}
})
Without seeing your full code I can't say for sure, but the most likely answer is that in the context of evaluate your nightmare code ( the .click() ) won't run because it doesn't have access to the original nightmare function. You would either need to use nightmare.click("bTags[i]") or use
.then(function(result){
if(result === "Confirm"){
nightmare.click("bTags[i])"
}
On top of all this - you're using a for loop to call a nightmare action! This is going to cause some issues. Nightmare issues promises before executing. This means you're attempting to run multiple electron instances at the same time, because the promises execute concurrently with the for loop. Rather than queue up - they fight for dominance, causing a crash. You should probably use a generator to manage async code such as vo or co.
Resources:
Common nightmare.js pitfalls