Wix setAttribute in a forEach loop only firing once? - javascript

So I'm trying to send some data I fetched from my database to a Custom Element in Wix. To communicate, I used setAttribute and attributeChangedCallback of course.
var fetchedData = await fetchData();
fetchedData.forEach(function (item, index) {
console.log(item, index); //output is perfect: logs every single entry there is
$w('#customElement1').setAttribute('data', index); //This however seems to only fire once, "11" is the only value getting alerted (see below)"
})
and
attributeChangedCallback(name, oldValue, newValue) {
if(name === 'data') {
alert(newValue); //output: '11' expected output: '1, 2, 3, 4, 5, 6...'
}
}
static get observedAttributes() {
return ['data'];
}
As you can see, I am sending the index of the item via I am fetching setAttribute. However, my output ist just "11", though it should be 1, 2, 3, 4, 5, 6, 7... What is the problem here? Is the attributeChangedCallback too slow to react? Or ist the setAttribute only firing once? Everything else I'm putting in the loop is firing eleven times so the loop seems to work.

The reason this happens is because Javascript is single threaded. While your code runs, nothing else can run, including callbacks. So the only time the callback has a chance to run at all is when your code finished running, that is after the loop already ended.
There are many resources explaining how the event loop works in more details, for instance I would recommend this video: https://youtu.be/cCOL7MC4Pl0

Related

Angular 2, throttleTime on observable from subject

Im trying to make throttleTime take effect, but for some reason it does not kick in. I have the following:
// Class Properties
private calendarPeriodSubject: Subject<x> = new Subject<x>();
private calendarPeriodObservable$ = this.calendarPeriodSubject.asObservable();
// Throttling fails here (Inside constructor):
const calendarPeriodSubscription = this.calendarPeriodObservable$.pipe(throttleTime(750)).subscribe(async (calendar: x) => {
// Do http stuff here
}
});
The subject gets called like this:
this.calendarPeriodSubject.next(x);
I also tried with:
this.calendarPeriodSubject.pipe(throttleTime(1000)).subscribe({next: (x) => x});
I would like to process the FIRST time, and the following clicks should not have any effect before after ieg 750ms - To prevent the server from getting spammed basically.
Anyone has any idea?
Thanks!
The problem is that you are using the wrong operator for your use case. The way I understand your explanation you want to send through your first call and stop any further calls to your Server for some amount of ms. But what throttleTime(sec) does is simply put a timer on the action and execute it sec ms later. So you server will still be spammed, just a few ms later.
Your case screams debounceTime() for me. debounceTime docu
This disables any further data to be passed though the Observable for the specified time after a value has been emitted.
Therefore your code should be fine if you use something like:
const calendarPeriodSubscription =
this.calendarPeriodObservable$.pipe(debounceTime(750)).subscribe((calendar: x) => {
// Stuff with returned data
});

Run several small test within one 'it' in E2E test using Protractor

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.

Proper way to limit wait time on selenium element search

My nightwatch/selenium test code looks for elements in the page that may not exist using code such as
browser.elementIdElement(ELEMENT,'class name', 'myclass', function(r)
{ if (r.status == 0) console.log('found match')
else console.log('did not find match')
})
If the element is found, the callback is invoked quickly (< 50ms), but if no element matches, the callback takes much longer (>1000ms). I have to do this hundreds of times and there are only a few elements that match the search criteria, so it adds a significant amount of time to a test run.
I would like to limit the time selenium spends searching for elements. I tried using the selenium timeoutsImplicitWait() function, e.g.,
browser.timeoutsImplicitWait(250)
.elementIdElement(ELEMENT,'class name', 'myclass', function(r)
{...})
but it doesn't affect performance. What is the correct method for limiting element search time?
Perhaps I am misunderstanding your problem; both of these patterns works well for me:
client
.useXpath().waitForElementPresent(selector, this.timeout)
.useCss().waitForElementPresent(selector, this.timeout)
this.timeout is set in the prototype of the base test case.
util.inherits(MyExampleBaseClass, Base);
MyExampleBaseClass.prototype = {
before: function (client) {
// call super-before
Base.prototype.before.call(this, client);
this.timeout = 250;
},
after: function (client, callback) {
// call super-after
Base.prototype.after.call(this, client, callback);
},
// Note: This method will not be mistaken by nightwatch for a step because
// it is not enumerable (since it's on the prototype)
getSiteURL: function () {
return "http://www.urlundertest.com/";
}
};
The following code for checking the visibility and continue even if there is no match
browser.waitForElementVisible('selector',timeout,false);
or this for the existence :
browser.waitForElementPresent('selector',timeout,false);
According to nightwatch api,
By the default if the element is not found the test will fail. Set this to false if you wish for the test to continue even if the assertion fails.To set this globally you can define a property abortOnAssertionFailure in your globals.
For more detailed explanation, check here:
http://nightwatchjs.org/api/#waitForElementVisible

How would I go about using window.find() activate on that text only once

I use hack.chat a bit, and I saw that they have a bot, but the bot program wasn't working for me so I decided to make my own.
var finderBinder;
var searchFor = function(command){
finderBinder = window.find(command, true, true);
if(finderBinder){
if(command === "/hello"){
ws.send(JSON.stringify({cmd: "chat", text: "hello!"}));
}
else if(command === "/cry"){
ws.send(JSON.stringify({cmd: "chat", text: "wah waha wahhh"}));
}
else
{
console.log("it was found but it was not a command.")
}
}
else
{
console.log("Did not find the command");
}
}
var loopdeloop = 0;
while(loopdeloop === 0){
searchFor("/hello");
searchFor("/cry");
}
Now, the first part works if I just run that by itself on the page, and enter searchFor("/hello"); that would work, but if I wanted it to just automatically do that whenever a message popped up, I attempted the loop,(In a empty chatroom so it wouldn't spam a used room if it did) and it crashed my browser. I know why it did that. because it just checked forever, and it saw it forever so it kept on trying to do the code forever..
But how would I make it only run the searchFor when a new text showed up so it would run the text in it and if it was a command it would do the command? Or is there a better way to do this?
The simplest way to stop your function from looping to infinity (and beyond!) would be to call it once every X seconds/minutes/hours/lightyears.
Using setInterval(searchFor, 1000); where the second parameter is the time interval in milliseconds.
To pass a parameter to your searchFor function, you must create an anonymous function so it doesn't get called right away.
setInterval( function() { searchFor ("/hello"); }, 1000 );
This will call your function every ~1 second, although keep in mind there is some overhead to javascript and there will be a slight delay. Also be careful of looping your function too often, as it will be expensive, and browsers have a built in delay, for example, you will not be able to setInterval to 2 ms and have it function normally cross browser.
Edit: The more elegant solution of binding an event to a change in the textbox is also possible, depending on how the page is setup and your access to it, hard to answer without that structure known.

Meteor Method Endlessly Runs

I have this code in a Meteor.methods definition:
update_field: function(collection,document_id,field,value) {
obj = {};
obj[field] = value;
console.log(obj);
if (collection == 'clients') {
var Collection = Clients;
} else if(collection = 'sites') {
var Collection = Sites;
}
Collection.update(
{
_id: document_id
}, {
$set: obj
}, function(error,id) {
console.log(error,id);
return(error,id);
}
);
}
This method is called from several client-side helpers events, and updates the field as needed. But whenever it runs once, it never stops running. Sometimes it runs infinitely even when all the Meteor.call('update_field')s have been commented out. I have tried including a 'caller' parameter and adding that to all the possible calls to figure out why it keeps getting called to no avail. Any ideas why this is looping?
Edit: this runs 2,000/minute
Edit2: this is called in one of two ways: on a keyup code==13 (enter) in an appropriate field or a field blur. However, event when these calls are commented out, the issue persists.
Especially your second comment worries me:
However, even when these calls are commented out, the issue persists.
Then who is calling it? The behaviour you're describing points to some helper executing the method. The method changes some data, which re-executes the helper (reactivity) and we end up with a classic endless loop.
Check your entire source code for references to this method:
$ grep -r "update_field" *
Maybe you set a variable somehow and then use the variable to call the method. Also: Have you declared the method inside a Meteor.methods({ ... }) block?
I think the issue was that one of my methods blurred the input field but preventDefaulted. Then the blur handler was called and caused the loop from there. This is my first Meteor project, so I'm chalking this one up to not quite understanding the system sufficiently. I still find it strange that the method was getting called when the callers were commented out, but I'll figure that one out another day.

Categories

Resources