Cypress if else conditional test - cypress

I am trying to include the below condition into my test but I can't seem to get it to work and receive the below error, any ideas why?
Essentially, I want to test that a input is / is not empty:
cy.get(`[class^='input-module_field']`).eq(0).then(($input) => {
if ($input.should('have.value', '')) {
cy.get(`[class^='input-module_field']`).eq(0).should('be.visible').type(foo)
cy.get(`[class^='input-module_field']`).eq(1).should('be.visible').type(bar)
cy.get(`[class^='input-module_field']`).eq(2).should('be.visible').type(foo-bar)
cy.get(`[class^='input-module_field']`).eq(3).should('be.visible').type(foo-bar-foo)
} else {
cy.get(`[class^='input-module_field']`).eq(1).should('be.visible').type(bar)
cy.get(`[class^='input-module_field']`).eq(2).should('be.visible').type(foo-bar)
cy.get(`[class^='input-module_field']`).eq(3).should('be.visible').type(foo-bar-foo)
}
})
The error i get:
$input.should is not a function

When yielded from a .then(), the $input variable is just a JQuery element, and can't use Cypress commands. In this case, even using a Cypress command, such as .should() wouldn't work, because that does not yield a boolean value for the if/else.
Instead, we'll want to use JQuery's .val() function.
...
if ($input.val()) { // if $input.val() returns an empty string, this evaluates to false
// code to run if the $input element has a value
} else {
// code to run if the $input element does not have a value.
}
...
Note: I reversed the order of what you had, with the $input.val() === '' being the else, instead of the if.

The check can be on the value itself
cy.get(`[class^='input-module_field']`).eq(0)
.then(($input) => {
const field0Val = $input.val() || 'foo' // if empty use "foo"
cy.get(`[class^='input-module_field']`).eq(0).type(field0Val)
cy.get(`[class^='input-module_field']`).eq(1).type('bar')
cy.get(`[class^='input-module_field']`).eq(2).type('foo-bar')
cy.get(`[class^='input-module_field']`).eq(3).type('foo-bar-foo')
})

Related

How to override 'data-testid' in the 'findByTestId function from Cypress Testing Library

Most of my existing codebase uses a 'id' only in few places 'data-testId' attribute present.
tried this code
import { configure } from '#testing-library/cypress';
configure({ testIdAttribute: ['data-testId','id'] });
But, still its not working.
Is there any way to use 'id' value in any of the testing-library functions.
My HTML code is something like:
<div class="some random class name" id="userprofile-open" role="button">SB</div>
I want click that element with this code:
cy.findByTestId("userprofile-open", { timeout: 120000 }).click();
I don't think you can configure testing-library with an array of ids, ref API configuration,
import { configure } from '#testing-library/cypress'
configure({ testIdAttribute: 'id' })
But even this fails. Instead you have to use the Cypress command to change the attribute name (only one name is allowed).
cy.configureCypressTestingLibrary({ testIdAttribute: 'id' })
To use either/or attribute name you can change the attribute name on the fly, wrapping it in a custom command (based on Custom Queries)
Cypress.Commands.add('findByTestIdOrId', (idToFind) => {
let result;
const { queryHelpers } = require('#testing-library/dom');
let queryAllByTestId = queryHelpers.queryAllByAttribute.bind(null, 'data-testId');
result = queryAllByTestId(Cypress.$('body')[0], idToFind)
if (result.length) return result;
queryAllByTestId = queryHelpers.queryAllByAttribute.bind(null, 'id');
result = queryAllByTestId(Cypress.$('body')[0], idToFind);
if (result.length) return result;
throw `Unable to find an element by: [data-test-id="${idToFind}"] or [id="${idToFind}"]`
})
cy.findByTestIdOrId('my-id')
.should('have.attr', 'id', 'my-id')
// passes and logs "expected <div#my-id> to have attribute id with the value my-id"
Note this custom command works only for synchronous DOM.
If you need to have Cypress retry and search for either/or attribute, don't use testing-library in the custom command.
Instead use Cypress .should() to enable retry
Cypress.Commands.add('findByTestIdOrId', (selector, idToFind) => {
cy.get(selector)
.should('satisfy', $els => {
const attrs = [...$els].reduce((acc, el) => {
const id = el.id || el.getAttribute('data-test-id') // either/or attribute
if (id) {
acc.push(id)
}
return acc
}, [])
return attrs.some(attr => attr === idToFind); // retries when false
})
.first(); // may be more than one
})
cy.findByTestIdOrId('div', 'my-id')
.should('have.attr', 'id', 'my-id')
// passes and logs "expected <div#my-id> to have attribute id with the value my-id"
The usual cypress way - which has an inherent check on the element visibility and existence as well as included retries for a period of time is using cy.get()
If you want to select element using property like data-id you need this sintax: cy.get('[propertyName="propertyValue"]')
If you want select an element by CSS selector you just pass CSS selector like this:
cy.get('#id')

cypress: How can manage the application flow, if the element xpath is not present

I have the following scenario:
if the element is present, i have to do one activity and if not present will do another activity.
cy.xpath("//div[text()= 'button').its('length').then(res=> {
if (res > 0) {
return 1;
}
else {
cy.log ("Element is not present")
}
}
)} '''
if element is present = Code is working fine,
if the element xpath is not present = it try to search the element xpath (//div[text()= 'button') and throwing the error as 'Timed out retrying: Expected to find element: undefined, but never found it.'
if element is not present, Is there any way, i can handle the code ,
When using xpath you can (sort of) make it conditional by wrapping the xpath selector with count().
cy.xpath("count(//div[text()= 'button'])") // ok with async content
.then(count => {
if (count) {
//return 1; // not useful, returns a "chainer"
// ...but you can perform the required test here, e.g
cy.xpath("//div[text()= 'button']").click()
} else {
cy.log('not found')
}
})
The shorter syntax using built-in jQuery might be
const exists = !!Cypress.$("div:contains('button')").length
if (exists) {
cy.xpath("//div[text()= 'button']").click()
} else {
cy.log('not found')
}
Note that this is a partial match to 'button', where-as the xpath syntax is an exact match.
Also note - using Cypress.$ by-passes retry-ability, so it should not be used where the text is asynchronous.
From docs
This is a great way to synchronously query for elements when debugging from Developer Tools.
The implication is that it's more for debugging after the page has loaded.
The best practice is to try to construct the test and the app's data in such a way that you know that the button is present.
You can do something like this. With Cypress.$, you can validate the presence of the element with the help of the length attribute and then do further actions.
cy.get('body').then($body => {
const ele = $body.find('selector');
if (Cypress.$(ele).length == 0) {
//Do Something element is not present
}
else if (Cypress.$(ele).length > 0) {
//Do Something when element is present
}
})

Golang mock for all arguments except

mock.on("FunctionName", "someStringArgument").Return(...)
Suppose if someStringArgument is "hello" then I want to return "1". But if someStringArgument is any other string I want to return "2".
How is this achieve able with GoMock?
What you want to do is write a custom function which will return your desired output.
Here is a simple example of what I do.
Define a custom response function
func FunctionNameResponse(arg String) string{
if arg == "hellp" {
// I used quotes because you mentioned "1" and not 1
return "1"
}
return "2"
}
Call custom function anywhere needed.
mock.on("FunctionName", mock.Anything).Return(FunctionNameResponse("someStringArgument"))

Use another rule if callback returns false

I'm using the following rule for my input:
callback_validate_host
I need to make the following condition:
if callback_validate_host is FALSE afterwards it should use the valid_ip validation rule.
So if validation of both: callback_validate_host and valid_ip on one input if FALSE then is should throw an error message.
How can I do that?
How about using your existing validate_host() method in conjunction with the Input class' $this->input->valid_ip($ip) method to create a single callback? Something like this:
public function your_custom_rule($input) {
if (! $this->validate_host($input) && ! $this->input->valid_ip($input)) {
// validate_host() returned FALSE *and* it's not a valid IP
$this->form_validation->set_message('your_custom_rule', 'Error msg');
return FALSE;
} else {
return TRUE;
}
}

Validate other field without causing infinite loop

I have a situation where I am creating an unobtrusive validator that must validate that another field is required only if the validated field is not empty (and vice versa). The problem is that there are some edge cases where the other field does not re-validate, and I would like to force it to revalidate itself without causing an infinite loop.
My validation method looks like this:
$.validator.addMethod("jqiprequired", function (value, element, params) {
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
return true;
}
return false;
});
params is my other field (both are textboxes). If both are empty, it passes, if both have values, it passes. It only fails if only one has a value.
This works fine, except that if one field is empty, and another has a value, then you delete the value from the field with a value, the empty field is not revalidated (because it's value has not changed).
I tried doing this:
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
$('form').validate().element(params);
return true;
}
But this causes an infinite loop because each time it passes, it calls the other.
How can I cause the other field to validate, without itself calling the original field?
Instead of adding an attribute to each field, try adding a variable jqip_validating in the script where you are adding this validation method. Then, change your validation as follows:
var jqip_calledFromOtherValidator = false;
if (jqip_validating) {
jqip_validating = false;
jqip_calledFromOtherValidator = true;
}
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
if (!jqip_validating && !jqip_calledFromOtherValidator) {
jqip_validating = true;
$('form').validate().element(params);
}
return true;
}
In order for the other validator to be called, both conditions must be satisfied, and they can only be satisfied when the first validator invokes the second validator.
You can add a is_validating attribute to each fields so that, if it's on you skip the validation and if not, you set it to true, do your validation and then clear it.

Resources