Cypress: child command subject seems not to be an element - javascript

I have a fillInput() function that takes a selector and a value as parameters, then:
clear the the input via cy.get(selector).clear()
then fills the input value via cy.get(selector).type(value)
As far as I know, this is really some sort of anti-pattern and a cypress command should be the way to go.
So, reading about child commands, I came out with this command that should do the same as my fillInput() utility function:
Cypress.Commands.add('fillInput', {prevSubject: 'element'}, (subject, value) => {
subject.clear();
subject.type(value);
});
However, when I try this in a spec via:
cy.get('#my-selector').fillInput('my-value');
I get this error in the cypress browser console:
TypeError: subject.clear is not a function
In the docs, cy.get() is said to yield a DOM Element, and the {prevSubject: 'element'} should make subject to be the same type (as far as I understand this).
However, subject seems to be a different type and methods that work on elements like type() or ' clear()or ' should() does not work on child command subjects.
How can I get the subject of my child command to act as a Dom Element?

While investigating to post the question, I came up with a simple solution to this problem. The subject is an object which has a selector, so you can use that selector with cy.get(subject.selector) to get the Dom Element:
Cypress.Commands.add('fillInput', {prevSubject: 'element'}, (subject, value) => {
cy.get(subject.selector).clear().type(value);
});
I think this is not a lot of clear and more people may have this issue, so I leave my solution here.

Try to use
cy.wrap(subject)
For me that was the trick.

Related

Cypress unable to find selector - React/CSS Modules/Cypress

I am writing e-2-e tests for our frontend using Cypress.
When I try to target an element, I am using .button-module_btnLabel__30kQb, but as I understand this makes my test brittle as the classname hash may change.
However, when I try to add a selector, Cypress can't find it. An example below:
import { Button } from '...ui'
<Button.Primary
data-cy="submit"
onClick={startLogin}
>
Log In
</Button.Primary>
cy.get('data-cy="submit"')
.should('be.visible')
.should('contain.text','Log In')
.click()
AssertionError: Timed out retrying after 4000ms: Expected to find element: `[data-cy=submit]`, but never found it.
If you are selecting on an attribute, you need enclosing square brackets.
cy.get('[data-cy="submit"]')
Note React does not pass on all attributes, but data prefix should be ok.

how can i use find() in using xpath

i am trying a drag and drop in an iframe and to do that i need to pass xpath in find since i cant find a unique element to pass in cy.get()
currently i am trying
cy.xpath('//div[#class="unlayer-editor"]//iframe[#src]')
.should("be.visible")
.find('//div[#class = "blopockbder-coent-tols43 col-sm-12"]//div[#aria-describedby="t8ppy-tooltip-9"]')
but this isnt working
i am using cypress for automation
Not an expert on xpath, but I think .find() can't be mixed with an xpath selector.
Two things to try
// chain 2nd xpath in place of .find()
cy.get('div[class="unlayer-editor"] iframe[id="my-iframes-id"]')
.should("be.visible")
.xpath('//div[#class = "blopockbder-coent-tols43 col-sm-12"]//div[#aria-describedby="t8ppy-tooltip-9"]')
or
// use .within() instead of .find() (roughly equivalent)
cy.get('div[class="unlayer-editor"] iframe[id="my-iframes-id"]')
.should("be.visible")
.within(() => {
cy.xpath('//div[#class = "blopockbder-coent-tols43 col-sm-12"]//div[#aria-describedby="t8ppy-tooltip-9"]')
})
Other things that might need adjusting
The iframe selection generally needs a follow-up command to get it's document body (ref Working with iframes)
// get the iframe document body any select within it
cy.get('div[class="unlayer-editor"] iframe[id="my-iframes-id"]')
.its('0.contentDocument.body', { log: false }).should('not.be.empty')
.within(() => {
cy.xpath('//div[#class = "blopockbder-coent-tols43 col-sm-12"]//div[#aria-describedby="t8ppy-tooltip-9"]')
})
Some of those classes in the path like col-sm-12 are purely display-oriented and may be different if you test at different devices. Once the test works, try removing them to make the test more robust.

Query.swap Backwards compatibility

I'm trying to use a datepicker known as flexcal on my site. It is important for me to use it accurately, to allow selection from a Jewish calendar. My site is based on jQuery v3.3.1, but flexcal was designed for jQuery v2.1.3. I thought it shouldn't cause any problems, but I came across the following error:
Uncaught TypeError: $.swap is not a function
After searching, I found here that this is a method that was intended to be private and was never documented, Anyway at the moment I'm having trouble embedding a widget on my site. Reviewing the source code of the widget reveals that the use of the method looks like this:
return $.swap(
parent,
{display:'inline-block'}, // make it visible but shrink to contents
swapper.bind(this, elem, parent.parentNode)
);
Does anyone know what the purpose of the method is, does it have a parallel alternative, or some other troubleshooting advice?
If you look at the source of jQuery.swap (https://github.com/jquery/jquery/blob/3.4.1/src/css/var/swap.js), you'll see that all it does is temporarily change some CSS attributes of the first argument (parent, in your case), run a calculation, and the restore the original attribute values. You can implement that yourself. It's particularly easy in your case, since the only CSS attribute we're temporarily changing is display:
var old_display = parent.style['display'];
parent.style['display'] = 'inline-block';
var ret = swapper.bind(this, elem, parent.parentNode).apply(parent, []);
parent.style['display'] = old_display;
return ret;

Protractor - Jasmine . Perform some action only when a specific element is present.

I have a window-box with two buttons 'add' and 'close'. I need to test below scenario:
When clicked on 'add' button it throws error and the window remains open. I need to click on 'close' button to proceed.
I used below code:
if(element(by.xpath("xpath_of_error_box")).isEnabled())
{
element(by.xpath("xpath_of_close_button")).click();
}
But it throws below error:
No element found using locator: By(xpath, xpath_of_error_box)
Is there any way to handle this?
According to the error, it seems that your xpath locator didn't match any element. And according to the additional clarification in the question you could try:
element(by.xpath("xpath_of_error_box")).isDisplayed().then(isDisplayed => {
if (isDisplayed) {
// do what you need when it is visible
} else {
// if not then proceed
}
});
As it was pointed out, isEnabled might not be the proper method you should use in this case. If it seems that the element you try to find is always present in the dom, you might better try to check for its visibility using isDisplay instead.
An advice. It's not a good idea to use xpath locators in your tests, because this ties them to the html DOM structire of the web page you are observing. As we know, the UI happens to change often, which would make your tests to brake often as well. Although this is of cource a personal preference, it is such until you end up with tons of brocken tests after a single small change in the html.
If you need to check if an element is present you can use the following code:
if (element(by.xpath("yourXpath")).isPresent())
But your problem is not on your if code, your problem is that the xpath your are searching doesn't exist.
isEnabled() and isPresent() returns promise (not boolean) and have to be resolved.
isEnabled() could be only used for <button>.
You can use XPath all the time, don't listen to anyone.
PS. Of course it would be glad to see your HTML to check the correctness of your XPath.

A way to "try" to click an Element without fail, if nonexistent (fire and forget)

I need a way to try to click on an element, but if it is not there, the test should not fail.
Kind of an "fire and forget" click.
I want to do that directly in Selenese (or js, as for user-extensions.js) , and not in PHPUnit/JUnit.
I thought maybe of a "user-extensions"-function to do this, so i searched the openqa Site but i didn't find anything useful.
Update: My own first try: (not working)
Selenium.prototype.doClickAndForget= function(locator) {
this.page().click(locator));
return true;
};
Any Ideas on how to make it work?
May be you can create a wrapper for click(String) method in DefaultSelenium like
public void clickWithoutFail(String locator){
try{
selenium.click(locator);
}catch(SeleniumException ex){
}
}
Above wrapper method may solve your problem. It will ignore any errors occured during click on non-existent element.
You can avoid having try/catch block by checking the presence of the locator using isVisible() method.
Hope this helps.
I did it with "storeEval()" and pure js.
Either i got a valid link or i returned a mandatory element to "fake klick" on, so sel will not throw any errors.
Its a hack but i had "unpredefined" content on my site, so i needed a way to handle the possibilities.

Categories