I am using the "Simple custom control to add infinite scrolling to repeat or views" available as snippet on OpenNTF.
This works but incorrect, this is what happens:
When you reach the bottom of screen a pager control gets triggered and an AJAX call is send.
However you are still at the bottom of the screen so again the pager control gets triggered and a second ajax call is send. (check the XHR requests in your browser with firefox or something).
So you requested for 1 additional set of values and get 2 returned.
Sometimes the second ajax request is finished earlier than the first and then the sorting in the repeat control is messed up.
I first tried to set a timeout on the JS call e.g.
$(window).scroll(function() {
if ($(window).scrollTop() == $(document).height() - $(window).height()) {
setTimeout(function() {
$(".infiniteScroll ul li a").click();
}, 4000);
}
});
but I have to set the timeout to a large amount of milliseconds and sometimes 4000 is not sufficient.
I wonder if there is an option to chain the ajax calls for the pager control or disable temporarily the trigger?
I found out the problem lies with the way the infinite scroll is triggered: when reaching the bottom of the window.
Then, no state is set (e.g. loading) so before the results of the first trigger are returned a second or third trigger could be fired.
Related
So I'm trying to write some cypress code and the documentation imo isn't really clear.
I have two scenarios.
A page is loaded with no loading spinner.
A page is loaded with a loading spinner.
I would like to write code that would satisfy both scenarios and let the test just continue.
If the page does not have a loading spinner element: Continue with the tests as usual.
If the page does have a loading spinner element: Wait until the element disappears and then continue
Cypress has a lot of fancy functions but the documentation on how to use them just isn't clear enough.
I tried with the following piece of code:
try {
cy.getByTestId('loader-spinner')
.should('exist')
.then(el => {
el.should('not.exist');
});
} catch (error) {
cy.getByTestId('loader-spinner').should('not.exist');
}
Because of the timing aspect it can be tricky to get this test right.
Controlling Triggers
You really need to know what controls the spinner - usually it's a call to API. You can then delay that call (or rather it's response) to "force" the spinner to appear.
To do that, use an intercept
cy.intercept(url-for-api-call,
(req) => {
req.on('response', (res) => res.delay(100)) // enough delay so that spinner appears
}
)
// whatever action triggers the spinner, e.g click a button
cy.getByTestId('loader-spinner') // existence is implied in this command
// if the spinner does not appear
// the test will fail here
cy.getByTestId('loader-spinner').should('not.exist') // gone after delay finishes
Two scenarios
First off, I don't think your two scenario idea is going to help you write the test correctly.
You are trying to conditionally test using try..catch (nice idea, but does not work). The trouble is conditional testing is flaky because of the timing aspect, you get the test working in a fast environment then it starts to break in a slower one (e.g CI).
Better to control the conditions (like delay above) then test page behaviour under that condition.
To test that the spinner isn't appearing, return a stub in the intercept It should be fast enough to prevent the spinner showing.
cy.intercept(url-for-api-call, {stubbed-response-object})
// whatever action triggers the spinner, e.g click a button
cy.getByTestId('loader-spinner').should('not.exist') // never appears
Take a look at When Can The Test Blink?
You should be able to just use a should('not.exist') assertion, causing Cypress to wait for the element to not exist. Remember, Cypress automatically retries up until the timeout, so if you haven't changed the global timeout, then the following will try for up to 4 seconds.
cy.getByTestId('loader-spinner')
.should('not.exist');
If you find the test failing because the element still exists, you can bump the timeout. Below, I've defined a 10s (10000ms) timeout for the should() command.
cy.getByTestId('loader-spinner')
.should('not.exist', { timeout: 10000 });
Additionally, you may find that the element does still exist, but is not visible. In that case, change not.exist to not.be.visible
From https://docs.ckeditor.com/ckeditor4/docs/#!/api/CKEDITOR.editor-event-change
If it is important not to get the change event fired too often, you should compare the previous and the current editor content inside the event listener. It is not recommended to do that on every change event.
I don't understand the above ... is there possibly a typo?
Is it trying to say that a call such as checkDirty on every change event could result in poor performance?
Would using an interval timer be a good approach to limit the number of checkDirty calls made.
For example, do not call checkDirty unless at least 1 second had passed from its last invocation?
The use case I have is enabling or disabling a save button based on the editor content differing from its last saved state.
I deal with this situation by always having the save button enabled and:
1) Every time a user presses the save button, I alert him that the contents have been saved and I clear the dirty flag:
CKEDITOR.instances.editor1.resetDirty();
2) I register an onbeforeunload event handler to alert him if he tries to exit the webpage having unsaved data:
window.onbeforeunload = function() {
if (CKEDITOR.instances.editor1.checkDirty()) {
return 'You will lose the changes made in the editor if you don't save them.';
}
}
So I have an ajax call to bring down several dozen chunks of data all several megabytes in size, afterward storing the data locally via the html5 filesystem api.
I wanted to prevent the user from navigating away from the page before the ajax calls were done. I decided to explore the onbeforeunload event, to have it notify that the user should stay on the page until the ajax calls are complete. I set the following before the AJAX call and at the end/success of the AJAX call I reset the window.onbeforeunload.
window.onbeforeunload = function(){
return "Information is still downloading, navigating away from or closing "+
"this page will stop the download of data";
}
When I attempted to close the page or navigate away from the page, the pop-up message comes up as expected informing the user to stay. However, once I confirm that I want to stay on the page, the ajax calls do not resume where they left off. Is there a way to prevent the ajax calls from pausing/stopping or to continue on/restart with their executions?
I'm open to any ideas to implement desired functionality described in the title of this post.
Pretty sure you can create a global in the .js file like...
var request;
Then assign your ajax call to this variable.
request = $.ajax{
//Ajax
//Stuff
//Goes
//Here
}
Now inside your window.unbeforeunload function, add this conditional statement.
window.onbeforeunload = function(){
if(!request){
return "Request not initiated";
}else{
//Request is in progress...
//You can use request.abort() if you need
}
}
EDIT: To elaborate on on some of the methods you can use on the request object, check out this page. (for example, .done or .always may suit your circumstances)
In our application we are making an ajax call on the final submit button and displaying a modal dialog with progress bar in it. After successful processing the dialog will be updated with the response. Now my requirement is to change the modal dialog to non dialog and allow the user to navigate to another page while the ajax call gets finished.
What are the implications if i change the modal window to non modal window. Will the ajax call still continues or will it get aborted. if it continues will it have reference to the dialog to update it with the response.
Please let me know your views.
Whatever you do while your AJAX call is being made in terms of maniuplating the view, will not impact the AJAX call itself. As long as you let the AJAX call complete and show the changes on the callback, you will be okay.
Needless to say, you cannot change the actual page your on but if I understand you right, your not doing that anyway.
A simple experiment to try the idea
I've written a simple example where I have a HTML page with a link to another page. When I click that link, it sends an AJAX request to a PHP script that simulates a long process (wait 10 seconds) and then changes a variable in the session. The target page then echoes that variable.
When I click that link, the page starts a long AJAX request but it also navigates to another page. The result is that the AJAX request is cancelled and the other page prints "nothing".
Here's the server.php:
session_start();
$x = isset($_SESSION['x']) ? $_SESSION['x'] : 0;
sleep(10); /* simulates long process */
$_SESSION['x'] = ++$x;
echo $x;
index.html (the significant portion):
Click here
The AJAX request in index.html (inside a <script> tag):
document.querySelector('a').onclick = function(e) {
var xhr = new XMLHttpRequest();
xhr.onload = function(e) {
console.log(JSON.parse(xhr.responseText));
}
xhr.open('POST', 'server.php');
xhr.send();
}
Possible solution
If your request is time consuming on the server only, create an endpoint that returns the progress of that process, then add a javascript code in all your pages to consume this endpoint and show the progress to your user.
I've been working on Chrome Extension for a website for the past couple of days. It's coming along really nicely but I've encountered a problem that you might be able to help with.
Here's an outline of what the extension does (this functionality is complete):
A user can enter their username and password into the extensions popup - and verify their user account for the particular website
When a user browses http://twitter.com a content script is dynamically included that manipulates the DOM to include an extra button next to each tweet displayed.
When a user clicks this button they are presented with a dialog box
I've made a lot of progress but here is my problem:
When a user visits Twitter the content script is activated and all tweets on the page get my new button - but if the user then clicks 'More...' and dynamically loads the next 20 tweets... these new additions to the page DOM do not get affected by the content script (because it is already loaded).
I could add an event listener to the 'More...' button so it then triggers the original content script again (and adds the new button) but i would have to predict the length of twitter's ajax request response.
I can't tap into their Ajax request that pulls in more tweets and call my addCurateButton() function once the request is complete.
What do you think is the best solution? (if there is one)
What you want to do is to re-execute your content-script every time the DOM is changed. Luckily there is an event for that. Have a look at the mutation event called DOMNodeInserted.
Rewrite your content script so that it attaches an event listener to the body of the DOM for the DOMNodeInserted event. See the example below:
var isActive = false;
/* Your function that injects your buttons */
var inject = function() {
if (isActive) {
console.log('INFO: Injection already active');
return;
}
try {
isActive = true;
//inject your buttons here
//for the sake of the example I just put an alert here.
alert("Hello. The DOM just changed.");
} catch(e) {
console.error("ERROR: " + e.toString());
} finally {
isActive = false;
}
};
document.body.addEventListener("DOMNodeInserted", inject, false);
The last line will add the event listener. When a page loads the event is triggered quite often so you should define a boolean (e.g. var isActive), that you initialize to false. Whenever the inject function is run check whether isActive == true and then abort the injection to not execute it too often at the same time.
Interacting with Ajax is probably the hardest thing to coax a content script to do, but I think you’re on the right track. There are a couple different approaches I’ve taken to solving this problem. In your case, though, I think a combination of the two approaches (which I’ll explain last) would be best.
Attach event listeners to the DOM to detect relevant changes. This solution is what you’ve suggested and introduces the race condition.
Continuously inspect the DOM for changes from inside a loop (preferably one executed with setInterval). This solution would be effective, but relatively inefficient.
The best-of-both-worlds approach would be to initiate the inspection loop only after the more button is pressed. This solution would both avoid the timing issue and be efficient.
You can attach an event-handler on the button, or link that is used for fetching more results. Then attach a function to it such that whenever the button is clicked, your extension removes all the buttons from DOM and starts over inserting them, or check weather your button exists in that particular class of DOM element or not and attach a button if it doesn't.