I have a React web app which uses Blockly that I'm currently trying to write automated tests for using the Cypress framework.
Cypress works pretty well for the basic process of signing on, but starts behaving inconsistently once Blockly is supposed to load.
About half the time, the entire Blockly portion of the app doesn't show up at all in the Cypress viewport. Sometimes this shows up, sometimes it doesn't, and I'm unsure what causes it or how to really reproduce it, it seems to be random.
Here is how it looks when it loads properly
Here is how it looks when it doesn't load properly
At first, I thought that the reason it didn't work is because the resources for Blockly hadn't loaded, and Cypress was trying to access resources that didn't exist.
To work around this, I added a delay, using cy.wait(). I tried anywhere from 1s-10s, but the delay doesn't seem to affect anything, no matter how long the delay is, it doesn't seem to impact if the Blockly portion of the app loads properly or not.
Here is the code for the portion of the Cypress test file used:
it('Sign in with created profile', () => {
cy.visit('localhost:3000/');
cy.get('input[name="email"]')
.type('test123#test.com').should('have.value', 'test123#test.com');
cy.get('input[name="password"]')
.type('testtest').should('have.value', 'testtest');
cy.get('button[type="submit"]').click();
});
it('Open created project', () => {
cy.get('div[class="project-container"]').contains('test project').click();
});
it('Drop 1+1 block into grid', () => { //2s delay
cy.wait(2000).then((prom) => {
cy.get('div[class="blocklyTreeRow"]').contains('Math').click({ force: true });
});
});
It works perfectly, until just after the 'Open created project' part of the test is run, then it's a hit or miss if the Blockly part of the app shows up. Refer to the images above for possible scenarios that happen.
Please check you are not trying to automate iframe because cypress do not support iframe
Iframe opened Question :
https://github.com/cypress-io/cypress/issues/136
Else :
Try to use Aliased.
// Wait for the route aliased as 'getAccount' to respond
// without changing or stubbing its response
cy.server()
cy.route('/accounts/*').as('getAccount')
cy.visit('/accounts/123')
cy.wait('#getAccount').then((xhr) => {
// we can now access the low level xhr
// that contains the request body,
// response body, status, etc
})
More documentation available here :
https://docs.cypress.io/api/commands/wait.html#Alias
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
The app was built in a way that you can't go straight to a specified url. So I can't do cy.visit(#specifiedURL)... So I need to do this:
cy.get('btn')
.click()
.then(() => {
cy.wait(#alias);
});
So when clicked onto that button it redirects me to a new page of the app but I need the data to load before rendering the page...
You could wait for an element on the next page to be visible to ensure the page has loaded properly if you want.
Cypress waits for any command(click() or type() etc...) for 4 secs by default and click() method will be invoked irrespective of page is loaded or not after 4 secs.
So In order to wait for page to fully load, you need to tell cypress to wait how long in cypress.json file and you can specify with "pageLoadTimeout": 10000 in cypress.json
{
"integrationFolder": "cypress/integration",
"defaultCommandTimeout": 10000,
"pageLoadTimeout": 10000
}
Change to something like above and then, first cypress get method will not invoke until 10 secs of pageLoadTimeout and then cypress makes query for get method. If you have specific requirement of timeouts, just read the documentation here https://docs.cypress.io/guides/references/configuration.html#Timeouts
I'm using SignalR (2.0.0 then 2.0.1) to make some real-time web application. Every 5 seconds it receives some data... Also process user events. Anything works, but i have problems with IE8 (same with any version if IE + compatibility Mode).
var worker = $.connection.mainHub;
...
worker.client.test= function () { ... };
var connection = ((navigator.userAgent.match(/iPad/i) != null) ?
$.connection.hub.start({ transport: ['webSockets', 'longPolling'] }) :
$.connection.hub.start())
.done(function () { console.log($.connection.hub.id);});
1) The first one: after ~1-2minutes of working - everything became slower(user events like pressing buttons, simple css hover action, etc.), IE seems to be dieing.
I thought the reason is memory leak.. it's a large amount of JavaScript (I'm using knockoutjs to build my html). But now i'm not sure
2) I need to have some clarity, how should I reload the page.
I'm using "location.href = location.href;" and everything fine, but in IE it seems to cause an error.
Not always, but often there is an error "$.connection.hub.id is undefined". I check $.connection.hub.transport, it's undefined too. Have no idea what i'm doing wrong...
it seems there was redirecting to other page at the moment of 'done' occured. jquery.signalR stayed on the other page.. I guess there is some specific of brouser cash
I'm converting a standard browser based app that's working fine to a chrome-app.
Once the page loads up, it has already hit an error - Uncaught TypeError: Cannot call method 'appendChild' of null. This occurs after several hundred lines of JS have done their job but its the first time the code makes a reference to the document object, specifically document.getElementById('mainDiv').appendChild(...).
I can clearly see the div with the id="mainDiv" in the debuggers elements tab. Yet, document.getElementById('mainDiv') must be returning a null. Any attempt at putting in breakpoints fails as they are ignored. I've added them to the line that fails as well as to lines that lead up to it and breakpoints are never triggered. I've read some of the threads on SO and I'm certain the breakpoints issue is just a bug in the debugger, but not recognizing an id when I can clearly see it and the code when run in the browser works fine leaves me wondering what's going on. Is document in the browser different from document in the app version?
Any ideas?
If I choose "inspect background page", the breakpoints work but it still fails but in a different way. The elements tab does NOT show my html page, but the pseudo generated background one and I can't get the debugger to show my page at all.
Any enlightenment would be appreciated. I've searched and read what I could find, but much of the docs are clearly out of date.
You seem to be accessing the document object of the background page, instead of that of your POS.html file.
Try this:
chrome.app.window.create('POS.html',{
'bounds': {
'width': screen.availWidth,
'height': screen.availHeight
}
}, function(appWin) {
var pageWindow = appWin.contentWindow;
var pageDocument = pageWindow.document;
pageWindow.addEventListener('load',function() {
// now use
pageDocument.getElementById('yourid');
// instead of
document.getElementById('yourid');
},false);
});
Also to inspect elements in your page right-click anywhere in the app window and select Inspect Element (this works only when the app was loaded as an 'unpacked extension')
Alternatively you can navigate to chrome://extensions and click the page link next to your app entry.
As lostsource mentioned, you're probably accessing the wrong DOM's document. You should think about the javascript in your app running in different global contexts, one for each page. There is (at a minimum) a page for the background page, and a page for each window.
Each of these pages runs in its own global context. This means global variables like document and window are different.
In the background page will be scripts which you load via the background manifest tag. When you open a window, it can also load its own script via script tags (make sure you do not use inline or block script tags, but use script src="foo.js". See http://developer.chrome.com/apps/contentSecurityPolicy.html).
The code that runs in the callback to chrome.app.window.create runs in the background page's context, so its document variable is for the background page's DOM, which is usually empty. Instead you can make it refer to the window's DOM using win.contentWindow as lostsource suggested, or add a page.js file with the script in it, and include it from the page via a script src='page.js' tag.
Is your call occurring after the load event, e.g. the JS called in a function set on window.onload?
I am trying to fetch data from a site by simulating events using CasperJS with phantomJS 1.7.0.
I am able to simulate normal click events and select events. But my code fails in following scenario:
When I click on button / anchor etc on remote page, the click on remote page initiates an AJAX call / JS call(depending on how that page is implemented by programmer.).
In case of JS call, my code works and I get changed data. But for clicks where is AJAX call is initiated, I do not get updated data.
For debugging, I tried to get the page source of the element container(before and after), but I see no change in code.
I tried to set wait time from 10 sec to 1 ms range, but that to does not reflect any changes in behavior.
Below is my piece of code for clicking. I am using an array of CSS Paths, which represents which element(s) to click.
/*Click on array of clickable elements using CSS Paths.*/
fn_click = function(){
casper.each(G_TAGS,function(casper, cssPath, count1)
{
casper.then ( function() {
casper.click(cssPath);
this.echo('DEBUG AFTER CLICKING -START HTML ');
//this.echo(this.getHTML("CONTAINER WHERE DETAILS CHANGE"));
this.echo('DEBUG AFTER CLICKING -START HTML');
casper.wait(5000, function()
{
casper.then(fn_getData);
}
);
});
});
};
UPDATE:
I tried to use remote-debug option from phantomJS, to debug above script.
It is not working. I am on windows. I will try to run remote debugging on Ubuntu as well.
Please help me. I would appreciate any help on this.
UPDATE:
Please have a look at following code as a sample.
https://gist.github.com/4441570
Content before click and after click are same.
I am clicking on sorting options provided under tag (votes / activity etc.).
I had the same problem today. I found this post, which put me in the direction of jQuery.
After some testing I found out that there was already a jQuery loaded on that webpage. (A pretty old version though)
Loading another jQuery on top of that broke any js calls made, so also the link that does an Ajax call.
To solve this I found http://api.jquery.com/jQuery.noConflict/
and I added the following to my code:
this.evaluate(function () { jq = $.noConflict(true) } );
Anything that was formerly assigned to $ will be restored that way. And the jQuery that you injected is now available under 'jq'.
Hope this helps you guys.