Can't find item in cypress even though it's in DOM - css-selectors

No matter what selectors I use, I keep getting the response "Expected to find element: ... but never found it. The curious thing is that in the DOM I am able to find this element and the selector for it (I used this to find the element
Code I have tried
cy.get("#main-content [title='Main']").click({force:true})
But after running the test, Cypress can't find the item no matter what selector it uses. It's also strange that in the preview of the test I'm not able to select the item but it selects a much larger area of the whole page editor.
Anyone know any other way to approach the problem? I will be very grateful for any help
I tried various types of selectors and tools, including in Selenium Webdriver but the same problem occurred

I highly recommend cypress-iframe package to access your elements within the iframe.
Follow the instructions to install it, then use it in your test like so:
cy.iframe('#mgl-pageeditor')
.find('#main-content [title=Main]')
.click()

It looks like you're trying to reach an element that is inside of an iframe element. With most automation frameworks, you need to handle switching into the iframe. Cypress is no different.
Cypress is able to access elements inside iframes, but you have to be explicit when you do.
I normally grab the iframe element, cy.wrap() it's contents, then use the .within() function to execute commands inside the context of it.
// Basic example
cy.get('iframe').then(iframe => {
cy.wrap(iframe.contents()).within(() => {
cy.get("#main-content [title='Main']").click({force:true});
})
})
You can make it easier by turning it into a custom command that handles the cy.wrap() and .within() for you.
// Typescript example of custom .switchToIframe()
Cypress.Commands.add('switchToIframe', { prevSubject: true }, (prevSubject: JQuery<HTMLIFrameElement>, callback: () => void): void => {
cy.wrap(prevSubject.contents(), { log: false }).within(callback);
});
// Test file
it('iframe test', () => {
cy.get('iframe').switchToIframe(() => {
cy.get("#main-content [title='Main']").click({force:true});
})
})
Working with iframes in Cypress: https://www.cypress.io/blog/2020/02/12/working-with-iframes-in-cypress/

Related

Cypress: access custom defined css property

I am building an APP using React + chakra-ui and I am in the process of adding Cypress tests to it, but I am facing a blocker and I was wondering if anyone had faced a similar problem and that could help!
Problem:
The test I am trying to pass is to verify that my element contains a CSS property, however, the CSS was generated by Charkaui with their unique syntax (e.g --chakra-scale-x)
My test case is as follow
cy.get('MY_ELEMENT')
.should('be.visible')
.should('have.css', '--chakra-scale-y', 1);
This test gave me the error
expected '<div.css-o64oke>' to have CSS property '--chakra-scale-x'
even though I can see from inspect element that it does have the following property.
Does anyone know a solution or a workaround for this? Thanks in advance!
Edit:
--chakra-scale-y is a css variable, which will be applied (probably as a transform) by the browser css engine.
Take a look in the devtools Computed tab (unselect Show all to see just those applied). If a transform shows up, this is what you need to test for.
In the test use getComputedStyle to check the value identified above.
cy.get('MY_ELEMENT')
.then($el => {
const win = cy.state('window')
const styles = win.getComputedStyle($el[0])
const transform = styles.getPropertyValue('transform')
console.log(transform)
expect(transform).to.eq(...)
})
It looks like you need to check scaleY, this is from the chakra library
const transformTemplate = [
"rotate(var(--chakra-rotate, 0))",
"scaleX(var(--chakra-scale-x, 1))",
"scaleY(var(--chakra-scale-y, 1))",
"skewX(var(--chakra-skew-x, 0))",
"skewY(var(--chakra-skew-y, 0))",
]

How to horizontally scroll in a React Application (ag grid) with UI automation (WebdriverIO /JavaScript)?

We have a grid application exactly similar to this demo application:
https://www.ag-grid.com/example.php
I want to horizontally/vertically scroll to any element in this demo site.
I have tried below methods:
browser.moveTo() : is not working and throwing below error :
stale element reference: element is not attached to the page document
browser.scroll(1665, 147.12345) : Is not doing anything. Pass the step.
browser or element.scrollIntoView() :shows not an existing method. Following error appears:
Property 'scrollIntoView' does not exist on type 'Client> & RawResult'.t
and
Property 'scrollIntoView' does not exist on type 'Client> & RawResult'.t
I am using webdriverio and typescript for UI automation.
wdio version:
"webdriverio": "^4.14.4"
Though none of the existing methods worked, but I solved it by using the "Tab" key. I followed below steps:
I click/select the last visible element in the current screen.
I press "Tab" key (Single or multiple as needed)
This scrolled the page horizontally(which solved my purpose for now).
Try to use scroll with javascript:
browser.execute(function() {
document.querySelector('yourElementLocator').scrollIntoView()
})
Keep in mind that code inside browser.execute does not have access to the outer context. So if you want to pass your element inside you will have to specify this argument after executable code:
browser.execute(function(){}, selector)
https://webdriver.io/docs/api/browser/execute.html
browser.waitUntil(
async () => {
browser.keys(["\ue00F"]);
return await countryEle.isDisplayedInViewport();
},
15000,
undefined,
500
);

Dojo Tooltip Attach to Multiple Nodes: Element Selector Works, but not Class Selector

I'm trying to use dojo Tooltips on a series of SVG elements that are tool buttons in my header. I'm using the method in the docs of attaching tooltips to multiple nodes like this:
new Tooltip({
connectId: query('.header'),
selector: 'svg',
position: ['below'],
getContent: function (e) {
return e.getAttribute('data-tooltiptext');
}
});
And that works, but if I use a selector of '.tool' (every SVG has a class of tool on it) my getContent function never gets called. 'svg.tool' doesn't work as a selector either. The docs an several examples around the net claim class selectors will work, but I've only been able to get element selectors to work.
I'm requiring 'dojo/query' and I've tried using 'dojo/query!css3' but that doesn't seem to make a difference. I don't know if it makes a difference, but I'm using dojo included with ESRI's ArcGIS JS API library, which reports a dojo version of 1.14.2.
I've experienced the same issue when using the selector attribute while creating a Menu. Within an SVG element, element selectors (even comma-concatenated ones) work, but class selectors do not. Outside of the SVG element, they worked just fine. You can play around with this by using dojo.query in your browser's console to see which elements get selected.
I was able to solve the issue by changing the selectorEngine in my dojo config. Using any of css3, css2.1, and css2 worked, so I think the issue may be in the acme engine. If you don't already have a dojo config, you can add it via a script tag:
<script>
var dojoConfig = {
selectorEngine: 'css3',
};
</script>

Protractor + chrome driver: Element is not clickable at point

Hi I am having some trouble getting a basic protractor test to work.
My setup:
I use requirejs so I init angular using angular.bootstrap(), not the ng-app attr. According to protractor docs this is not supported out of the box, but seems to work fine for tests that don' involve clicking.
Protractor conf.json:
"use strict";
exports.config = {
specs: '../E2ETests/**/*.js',
chromeOnly: true,
getPageTimeout: 30000,
allScriptsTimeout: 30000
}
I use some third party jquery plugs which I wrap in directives, I suspect these might be part of the issue.
The test:
"use strict";
describe('When clicking should add stuff', function () {
var ptor;
beforeEach(function () {
browser.get('https://localhost/myApp');
ptor = protractor.getInstance();
});
it('add stuff', function () {
// If I comment this, the test pass.
element(by.id('add-stuff-button')).click();
// This does not matter fails on the line above..
expect(browser.getTitle()).toBeDefined();
});
});
The error:
UnknownError: unknown error: Element is not clickable at point (720, 881). Other element would receive the click: <div class="col-md-5 col-md-offset-5">...</div>
(Session info: chrome=37.0.2062.124)
(Driver info: chromedriver=2.10.267521,platform=Windows NT 6.1 SP1 x86_64)
Thoughts
The chromedriver do find the button, because if I change the id it complains that no element is found. So I think the problem is that the button moves from its initial position. As the element(***) function should wait for angular to be done, I suspect that its the third party plugins that might interfere as they might not use angular api's fetching data etc. So angular think its done but then the third party plug populates and moves stuff around.
Any ideas what to do?
If the third party plugs is the problem, can I somehow tell angular that third party stuff is going on and then later tell it when its done?
Thx
Br
Twd
You should set window size in your config file
onPrepare: function() {
browser.manage().window().setSize(1600, 1000);
}
Following worked fine for me:
browser.actions().mouseMove(element).click();
Edit: If above does not work try chaining perform() method too(I got this as an edit suggestion, I have not tested it but somebody could verify it and comment)
browser.actions().mouseMove(element).click().perform();
This happens if the chrome window is too small, try to add inside the beforeEach
browser.driver.manage().window().setSize(1280, 1024);
Or simply use the Actions class:
browser.actions().mouseMove(elem).click().perform();
Had the same issue but was not related to the window size but had to wait for ngAnimation to end.
So I had to wait until the element was clickable with.
const msg = 'Waiting for animation timeout after 1s';
const EC = new protractor.ProtractorExpectedConditions();
await browser.wait(EC.elementToBeClickable(model.elements.button.checkCompliance), 1000, `${msg} panel`);
await model.elements.button.checkCompliance.click();
#note - I am using async/await node 8 feature, you could just as well convert this to regular Promises.
Also using ProtractorExpectedConditions instead of ExpectedConditions see documentation
Maybe It is not applicable in your case, but I've encountered the same problem and based on Milena's answer I've been looking for another element obscuring my button (in my case, a dropdown menu in the top right of my screen).
It appears to be the Connected to Browser Sync notification message sent by browsersync, launched by Gulp. The message vanished after a short time, but after my onClick() call.
To remove the notification, in my gulpfile, I've added the notify: false param when initializing browsersync:
browserSync.init(files, {
server: {
baseDir: "dist",
index: "index.html"
},
notify: false
});
I fix this problem by using browser time sleep.
browser.driver.sleep(3000)
before giving click button
You can define the desired screen resolution through your protractor configuration file (e.g. protractor.conf.js or config.js) for consistent test behavior.
For example with Chrome browser:
exports.config = {
specs: [
// ...
],
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: [
'--window-size=1600,900',
'--headless'
]
}
}
// ...
}
Explanations
window-size argument will launch Chrome with a 1600 by 900 window.
headless will launch headless Chrome, allowing you to have your tests run with the specified window size (1600 by 900) even if your screen resolution is lower than that.
You may want to have two configurations, one for developers (without headless mode) who always have a high resolution screen and one for build servers (headless mode) where screen resolution is sometimes a mystery and could be lower than what your application / test is designed for. Protractor configuration file are javascript and can be extended to avoid code duplication.
I had the same error and purely adjusting the screen size did not fix it for me.
Upon further inspection it looked as though another element was obscuring the button, hence the Selenium test failed because the button was not (and could not be) clicked. Perhaps that's why adjusting the screen size fixes it for some?
What fixed mine was removing the other element (and later adjusting the positioning of it).
This works better than specifying the window size, in case you test need to run on multiple displays.
browser.manage().window().maximize();
Other way, you can try this:
this.setScrollPage = function (element) {
function execScroll() {
return browser.executeScript('arguments[0].scrollIntoView()',
element.getWebElement())
}
browser.wait(execScroll, 5000);
element.click();
};
You could also try turning off any debug tools you might be using. I was using Laravel and debugbar and had to set APP_DEBUG to false.
From Gal Malgarit's answer,
You should set window size in your config file
onPrepare: function() {
browser.manage().window().setSize(1600, 800);
}
If it still doesn't work you should scroll to the element's location
browser.executeScript('window.scrollTo(720, 881);');
element(by.id('add-stuff-button')).click();
Note that this was sometime caused by a top navigation bar or bottom navigation bar / cookie warning bar covering the element. With angular 2, when clicking it scrolls until the element is only just on page. That means that when scrolling down to click something, if there is a bottom navigation, then this will obstruct the click. Similarly, when scrolling up it can be covered by the top navigation.
For now, to get around the scrolling up, I am using the following:
browser.refresh();
browser.driver.sleep(3000);
I made sure that I removed the bottom bar by clicking to close it before the test started.
That means the element is not within the visible area. There are several ways to handle this:
Force click the element regardless visibility
await browser.executeScript('arguments[0].click();', $element.getWebElement());
Scroll to the element and then click
await browser.executeScript(`arguments[0].scrollIntoView({block: "center"});`, $element.getWebElement());
await $element.click()
Maximize the working area of browser's window before tests
beforeAll(async () => await browser.driver
.manage()
.window()
.setSize(1920, 1080)
);

UI testing an ExtJS webapp using CasperJS/PhantomJS

I'm working on UI testing an ExtJS web-app, and I'm a beginner.
I am trying to test the ExtJS widgets by using CasperJS/PhantomJS tool.
Also, I generate the required CasperJs script using Resurrectio and by making necessary changes to it.
Since ExtJs generates unique ids dynamically for the DOM elements that it creates, I want to know how to provide those ids in CasperJs script for testing.
For example, The following Casper Script was generated by Resurrectio:
casper.waitForSelector("#ext-gen1142 .x-tree-icon.x-tree-icon-parent",
function success() {
test.assertExists("#ext-gen1142 .x-tree-icon.x-tree-icon-parent");
this.click("#ext-gen1142 .x-tree-icon.x-tree-icon-parent");
},
function fail() {
test.assertExists("#ext-gen1142 .x-tree-icon.x-tree-icon-parent");
});
casper.waitForSelector("#gridview-1038",
function success() {
test.assertExists("#gridview-1038");
this.click("#gridview-1038");
},
function fail() {
test.assertExists("#gridview-1038");
});
Here #ext-gen1142 and #gridview-1038 are the ids dynamically created. How should one provide data in the tests? Is there any stub or mocking tools which works with ExtJs in the code to provide these ids at runtime during tests?
I came across SinonJS. Can it be used or Do I need to used CSS or XPath Locators as mentioned in this answer? How reliable it is to use CSS or Xpath Locators?
Thanks in advance!
Not so easy to answer this, but here a few thoughts...
Don't rely on generated IDs. Never. They'll change in moments you won't like and if you have luck very much earlier.
Your best friends will probably be pseudo CSS classes you attach to your components. You could also use IDs, but this is only reasonable when you have elements which occur only once in your page. If that is the case, they are very good anchors to start with selections/queries.
XPath with ExtJS is possible, but you have to carefully choose the elements. ExtJS is so verbose in generating little things so your paths can be quite complicated. And when Sencha drops support for problematic browsers (IE < 8) maybe they change their templates and your XPath doesn't find anything.
SinonJS is great. But it won't help you much in DOM problems. But sure you can use it in your tests. I suppose it will payoff most in testing parts of your controllers or non-trivial models.
Model your test components after your real UI components and screen sections. Don't just record a script. Test code should be engineered like production code. If you create reusable components of test code and logic, you don't have to fear changes. In the best case the changes in one component will only touch the testing code of that particular component.
I know you have ExtJS. But take some time to look at AngularJS and see how easy it can be to test all parts of a JavaScript web application. I'm not saying you should switch to AngularJS, but you can learn a lot. Have a look at Deft JS as it has many concepts which enhance testability of ExtJS applications.
I use Siesta for my ExtJs testing. It works amazingly good for all JavaScript (jQuery based and others), but is specifically designed for ExtJS/Sencha Touch.
It has the feature to combine CSSquery and ComponentQuery to select your elements I think that will fix a lot of problems for you.
In the paid version there is even a test recorder to record scenario's and use them for your tests.
Here's a demo
Here's some sample code:
StartTest(function(t) {
t.chain(
{ waitFor : 'CQ', args : 'gridpanel' },
function(next, grids) {
var userGrid = grids[0];
t.willFireNTimes(userGrid.store, 'write', 1);
next();
},
{ waitFor : 'rowsVisible', args : 'gridpanel' },
{ action : 'doubleclick', target : 'gridpanel => .x-grid-cell' },
// waiting for popup window to appear
{ waitFor : 'CQ', args : 'useredit' },
// When using target, >> specifies a Component Query
{ action : 'click', target : '>>field[name=firstname]'},
function(next) {
// Manually clear text field
t.cq1('field[name=firstname]').setValue();
next();
},
{ action : 'type', target : '>>field[name=firstname]', text : 'foo' },
{ action : 'click', target : '>>useredit button[text=Save]'},
function(next) {
t.matchGridCellContent(t.cq1('gridpanel'), 0, 0, 'foo Spencer', 'Updated name found in grid');
}
);
})

Resources