I'm having a very frustrating problem with testing a React app using CodeceptJS and puppeteer - it only finds elements with a custom locator only when in paused mode.
My custom locator is data-test-id. I'm targeting elements using I.seeElement("$id-of-element-here") and I.seeNumberOfElements("$test-id", X).
This works perfectly when my tests are paused and I'm manually moving onto each step of the test, but does not work when the tests are executing from start to finish - targeted elements are simply not found.
Sometimes I can counter this with I.wait(X) or I.refreshPage() but I'm now running into a case where none of this helps.
I do see the data-test-id attributes in the HTML using both Chrome's and Chromium's dev tools. There are no typos either.
There's not much point in showing examples of targeted elements, as the problem seems to happen at random, I haven't been able to see any pattern of where/when this happens.
These are the settings of the custom locator plugin, in codecept.conf.js:
plugins{
...
customLocator: {
enabled: true,
attribute: 'data-test-id',
},
...
}
Any help will be appreciated! :)
May waitForNavigation is a way to go?
https://codecept.io/helpers/Puppeteer/#configuration
https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagewaitfornavigationoptions
Solution:
I was running the assertions in question in a rather big within block (link), so I was ending up looking for something within an element that was no longer on the page.
It appears that during a pause(), you can find and operate on elements that are visible at the moment and the within blocks don't matter.
That's the only way I can explain why, in this situation, an element can be detected during a pause but not during the actual execution of the test from start to finish.
As soon as I moved the assertions out of the within block, everything was normal and behaved as expected.
Apologies for the confusion - learning every day... :)
Related
I have given a wait time for 60 sec, and i am using wait until element to be clickable/visible but script is waiting for whole 30 secs even though the element is visible on UI and also clickable?
I tried using latest selenium version, tried using different waits also using different locators. but it did not work
The reasons could be in:
The element belongs to iframe so you need to switch to the iframe prior to attempting locating anything inside it
The element belongs to Shadow DOM so you need to locate ShadowRoot object, cast it to the WebElement and find the sub-element you want to click
Your locator is not correct, try getting the page source and saving it to a file. Once done use your favourite browser developer tools to locate the elemen
The syntax of your Explicit Wait is not correct. Check out Explicit Waits and How to use Selenium to test web applications using AJAX technology for code examples
Going forward consider adding page source and your code to your question as the chance you will get the comprehensive answer will be much higher, otherwise we have to go for "blind shots"
I know the basics of optimizing Robot Framework for speed on normal applications, but this is not a normal application. It's not a question of going as fast as possible, because if the code executes too fast on an Angular application, it'll try to click an element that isn't enabled or visible, or an element that doesn't exist yet. Timing issues abound, and the result is that I'm using a keyword (below) to slow down my program universally. The problem is that it's hard-coded, and I'm looking for a more "programatic" (programatical? I don't know the exact term) solution that will wait for an element to be clickable and then click it as soon as it is.
This is the keyword I use after every single click (${SLOW_TIME} is a global variable set to 0.5s):
Slow Down
# EXAMPLE USAGE
# Slow Down ${SLOW_TIME}
[Arguments] ${SLOW_TIME}
Sleep ${SLOW_TIME}
This is my current solution, which was written to verify that an element is ready to be clicked for test verification purposes, not speed. It's not complete (needs "Is Clickable") and occasionally causes the program to wait longer than it has to:
Verify Element Is Ready
# EXAMPLE USAGE
# Verify Element Is Ready id=myElementId
# Click Element id=myElementId
[Arguments] ${element}
Variable should exist ${element}
Wait until element is visible ${element}
Wait until element is enabled ${element}
I'm aware that Robot Framework isn't built for speed, but for long tests I'm tired of doing nothing for 10 minutes waiting for it to finish, only to see that I have an incorrect [Fail]. If the solution involves Python, Javascript, or Java, I can work that in.
EDIT: I'm currently using ExtendedSelenium2Library, but its implicit waits don't always work, so I wanted a second layer of waiting, but only as long as necessary.
First solution to explore would be to use libraries specifically designed for Angular based web applications, such as AngularJsLibrary or ExtendedSelenium2Library. As far as I know, ExtendedSelenium2Library is the one that works best (but perhaps not without any issues, I think it does have a few issues)
Next thing to know is, given that your element indeed is visible, it doesn't necessarily mean that it's ready to be clicked. There are quite a few ways to get around this kind of issues.
One way is to put a sleep in your test setup, to give the page some time to fully initialize. I'm personally not a fan of this solution. This solution also doesn't work well for pages that load new content dynamically after the initial document was initialized.
Another way is to wrap your click element in a wait, either by writing your own in Python or, using something like Wait Until Keyword Succeeds
Basically i have these two lines of code written right after each other.:
console.log(typeof (noAdsCallback));
document.write('<sc' + 'ript type="text/javascript">console.log(typeof(noAdsCallback));</scr' + 'ipt>');
The first one logs function, the second logs undefined.
Of course it's a bit trickier than that. So here is the set-up in a nutshell:
I have a so called waterfall of ad-providers. That means, I try to load some Ads, by writing (using document.write) some special tags (given to me by my ad-provider).
If the provider doesn't find an ad for me, they send back a javascript-snippet which looks like this:
if (typeof(window.noAdsCallback) === "function") noAdsCallback();
This function essentially writes the tags of the next provider, which does the same as the first one until I reach the end of the list.
This system actually works fine, doing exactly what I want it to do. Both lines given in the beginning log function.
Except if I use Google as an ad-provider. There is one thing Google does differently, which seems to mess everything up.
In Google, I cannot define a fallback-JavaScript-snippet. All I can do is provide a fallback-url. So this fallback-url (since it's loaded inside an iframe inside an iframe inside...) sends a postMessage to the top, which then calls the same noAdsCallback() method. And this too, works just fine. The message is received and the right method executed. However, already the two lines already give different results, i.e. function and undefined respectively
The next provider then fails to find the noAdsCallback() Method, when it returns, because it uses document.write to try to execute it. Somehow, the context was lost.
First hint: It works fine (i.e. both lines log function) in Chrome, but it doesn't work in FF or IE.
Second hint: It works fine, as long as context never switches, but if communication runs at any point through messaging, it get's confused.
Third hint: Using the fantastic postscribe library as mentioned below, actually solves the problem, but introduces new ones somewhere else.
Fourth hint: Debugging the window.name, before using document.write, gives the correct name, so I'm not in a random iFrame.
Finishing thoughts. I know, i know: DON'T USE DOCUMENT WRITE!! I know that. But since Adproviders use it all the time, I am forced to use it to, otherwise I get this:
Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.
In Fact, right now I'm using postscribe (https://github.com/krux/postscribe) and it works like a charm, except for one lousey provider. And the workauround solution would be, to use document.write only for this lousy provider and postscribe for all the others. But i would really like to find out what the root of the problem is.
Any Ideas, much appreciated.
I think I understood it now. Long story short: DON'T USE DOCUMENT.WRITE :)
Try postscribe, if you have to.
So in hindsight it is quite obvious, because really, anywhere you read about document.write() it says, that write() clears the whole document. And I just didn't get it, because I never saw it happening and every ad is using it, like the whole time. Plus, it seemed to work fine on Chrome. So what's going on??
Well here is what happens. As long as the document is open, which basically means while it is being written, document.write() just appends to the stream, and doesn't clear the document. And as long as I used document.write(), to append foreign ad-scripts (which may and will contain document.write()), the page does not close, hence the document stays open.
This is the reason, why adding Google to my waterfall, posed a problem: Google puts everything in iframes. So the page containing the waterfall model just sees the iframe and says: "well as far as I'm concerned, I'm done" and closes the document, while in fact, Google is still at it.
Afterwards, Google didn't find an ad, sends a postMessage to the main page, causing the next provider to be used. Who then uses document.write() and clears everything.
Everything? Not everything. Remember, it still used to work when I used Chrome? The reason for that is, Chrome just clears the HTML but leaves the Javascript intact. So on Chrome, my Javascript-waterfall worked fine, because all the JS-objects where still in place. All other browsers cleared it.
So that's it. Probably noone's gonna read it, but if you do, USE POSTSCRIBE! Now that I finally really understood document.write() and document.open() and document.close() I'm a big fan.
I am trying to run a simple test on multiple browsers, here is a mock up of the code I've got:
String url = "http://www.anyURL.com";
WebDriver[] drivers = { new FireFoxDriver(), new InternetExplorerDriver,
newChromDriver() };
#Test
public void testTitle() {
for (int i = 0; i < drivers.length; i++) {
// navigate to the desired url
drivers[i].get(url);
// assert that the page title starts with foo
assertTrue(drivers[i].getTitle().startsWith("foo"));
// close current browser session
drivers[i].quit();
}// end for
}// end test
For some reason this code is opening multiple browsers seemingly before the first iteration of loop is completed.
What is actually happening here? and what is a good/better way to do this?
Please understand that I am by no means a professional programmer, and I am also brand new to using Selenium, so if what I am attempting is generally bad practice please let me know, but please don't be rude about it. I will respect your opinion much more if you are respectful in your answers.
No it's not.
In fact, most of the test frameworks have convenient ways to handle sequential/parallel executions of test. You can parametrize test class to run the same tests on multiple browsers. There is an attribute in TestNG called Parameters which can be used with setting.xml for cross browser testing without duplicating the code. An example shown here
I would no do that.
Most of the time it is pointless to immediately run your test against multiple browsers. Most of the problems you run into as you are developing new code or changing old code is not due to browser incompatibilities. Sure, these happens, but most of the time a test will fail because, well, your logic is wrong, and it will not just fail on one browser but on all of them. What do you gain from getting told X times rather than just once that your code is buggy? You've just wasted your time. I typically get the code working on Chrome and then run it against the other browsers.
(By the way, I run my tests against about 10 different combinations of OS, browser and browser version. 3 combinations is definitely not good enough for good coverage. IE 11 does not behave the same as IE 10, for instance. I know from experience.)
Moreover, the interleaving of tests from multiple browsers just seems generally confusing to me. I like one test report to cover only one configuration (OS, browser, browser version) so that I know if there are any problems exactly which configuration is problematic without having to untangle what failed on which browser.
Motivation
When testing directives; sometimes assertions fail in non-obvious ways, stack traces are not helpful and inspecting local variables while paused on a breakpoint with Chrome Developer Tools hides the forest behind the tree.
This is when I wish I was able to pause a unit test (possibly with iit or ddescribe) and poke around the UI of the directive to narrow down the faulty behavior.
So far
I have been able to achieve this with KnockoutJS before and found it to be an exceedingly useful technique in complicated scenarios. Now I would like to do the same for AngularJS.
I have found that I can attach the element I $compiled with Angular to the html body, and that makes it visible, however I cannot interact with it, as if all the bindings were turned off.
Upon closer inspection I found that while running a test case (an it block) everything seems to be in order, as soon as the test case finishes all the event listeners vanish from the corresponding DOM elements — like click listener on a <button ng-click="...">.
After some digging I found that deep inside angular-mocks.js:1965 there is an afterEach block that calls .off() to turn off DOM event listeners at two places:
injector.get('$rootElement').off(); and
handle && angular.element(handle.elem).off(); (via angular.mock.clearDataCache();)
When I comment out the two .off() lines, voilà, the appended directives jump to life in the DOM!
Note: While the version of angular-mocks I link to (v1.2.10-build.2148+sha.fced1c0) is not bleeding-edge, newer ones only seem to differ in line numbers being shifted when it comes to this part.
Question
Is there a better (more supported, less ad-hoc, and/or less hackish) way to achieve what I am trying to do? If so, how?
Edit: versions
Karma 0.10.9
Jasmine 1.3.1
Angular Mocks v1.2.10-build.2148+sha.fced1c0
AngularJS 1.2.10-build.2148+sha.fced1c0