Cypress picks up the wrong element? - cypress

I am trying to find and click this element using Cypress:
<input class="form-control btn btn-primary" type="button" value="Log out">
I have tried several variations, but the closest is this:
cy.get("input[type='button']").filter('.btn-primary').should('have.value','Log out').click()
So when I run this, I get the following response:
expected [ <input.form-control.btn.btn-primary>, 1 more... ] to have value Log out, but the value was Edit.
Sure, there is a button there called Edit, but it is not the one I want. I have specified what I want with the should('have.value','Log out') clause.
So - why does it insist on trying to use the wrong element & failing?
Update: I finally got this working.
This is the solution I went with in the end:
cy.get("input[type='button']").filter('.btn-primary').eq(1)
.should('have.value','Log out').then(($btn) => {
$btn.click()
})

can you try
cy.get('input').find("[value='Log out']").click()
or
cy.get("[value='Log out']").click()
Obviously you need your developers to add ids, but I know your situation.
You could also try if there is only one btn-primary
cy.get('.btn-primary').click()

This was the solution that worked for me:
cy.get("input[type='button']").filter('.btn-primary').eq(1)
.should('have.value','Log out').then(($btn) => {
$btn.click()
})

How about to set an unique testID for that button and get it as simple as it can be.
Something like:
data-test-id= "log-out-button" //put that in your button code
and then in Cypress:
cy.get('[data-test-id="log-out-button"]')
.click()
even though you can set a function to get those testID's more effectively:
Command.Cypress.add('getTestID', (testID) => {
cy.get(`[data-test-id="${testId}"]`)
})
and now everything you do for getting that button (or every element with testID is: cy.getTestID('log-out-button')

Related

Laravel 9 - Submitting a form to a route leads to /null and doesn't do anything

I'm using soft deletes and trying to make a function to restore the deleted row. Really everything to do with submitting the form doesn't work, even deleting...
My tags are within a forelse loop, maybe that's the cause??
Route is (this is above my resource route):
Route::post('/post/restore/{id}', [PostController::class, 'restore'])
->name('post.restore');
Controller is:
public function restore($id)
{
dd($id);
}
Form/view is:
<form action="{{route('post.restore', $post->id)}}" method="POST">
#csrf
<button type="submit" class="dropdown-item popup" data-confirm="Would you like to restore?">Restore</button>
</form>
After submitting, it just takes me to:
domainURL/post/null
and gives a 404 error
Any advice?? I also tried it without the {id} at the end of the route, same results
I think you are using the wrong syntax for route function, instead of this:
route('post.restore', $post->id)
you should use it like this:
route('post.restore', ['id' => $post->id])
you can read more here as well.
I figured it out... my "would you like to restore?" javascript function was breaking the form. I was using javascript to popup a confirmation message before submitting and during that process it lost the ID
I got rid of that and it works fine
Thank you!

Test passing locally but not in CI - cypress

I have this code:
<div class="input-group" data-cy="k-item">
<input type="text" placeholder="my_placeholder" value="my_value">
<div data-cy="delete-field" role="button" tabindex="-1" class="r_field">
<i class="close"></i>
</div>
</div>
I have my cypress code for clicking on "close" and check that the item when submit is deleted in the table:
cy.getBySel('k-item').within(() => {
cy.getBySel('delete-field').click().click()
})
cy.getBySel('register').click()
cy.intercept('GET', 'route_intercepted').as('getRoute')
cy.wait('#getRoute')
cy.get('table').should('not.contain', 'my_item')
})
The test is passing in my local and even in headless mode but when in pipeline it is failing saying:
AssertionError: Timed out retrying after 4000ms: Expected not to find content: 'my_item' within the selector: 'table' but continuously found it.
I think the item is not getting deleted after the submit. I am experiencing the same issue with force:true.
Have anyone experienced this issue please and also is there any other way to make the same tests more robust.
Add another assertion for an element on the page that is not delayed in rendering. This could be a header, spinner, etc. but must be something immediately rendered, not something async as this list of items appears to be.
cy.wait('#getRoute')
// add another assertion here
cy.get('table').should('not.contain', 'my_item')
Try moving the intercept to the top of the test. It's a "listener" so it should be set up before the delete event is triggered.
cy.intercept('GET', 'route_intercepted').as('getRoute')
cy.getBySel('k-item').within(() => {
cy.getBySel('delete-field').click()
cy.getBySel('register').click()
cy.wait('#getRoute')
cy.get('table').should('not.contain', 'my_item')
})
Also the alias wait needs an #, but I presume that was a typo.
How about you increase the timeout to 7 seconds and try. Mostly in CI systems since the resources are shared so the execution speed might vary, If the line below works locally then my best guess is increasing the timeout should do the job.
cy.get('table', {timeout: 7000}).should('not.contain', 'my_item')
You have to wait the response on the wait call and just then you can check your element:
cy.getBySel('k-item').within(() => {
cy.getBySel('delete-field').click().click();
})
cy.getBySel('register').click();
cy.intercept('GET', 'route_intercepted').as('getRoute');
cy.wait('#getRoute').then(()=>{ //wait the response of your GET, then check
cy.get('table').should('not.contain', 'my_item');
})
})

Cypress: Check if an element contains a specific other element

I need to check a div to see if it contains an 'a' (link). I've been looking at similar questions here, but none seem to do exactly what I'm trying.
Example markup:
<div if='description>
blablablabla
blablablabla
blablablabla
</div>
<div id='description'>
blablablabla
The link
blablablabla
</div>
<div id='description>
blablablabla
blablablabla
blablablabla
</div>
The ids are duplicated, and there's nothing I can do about that. However, using .eq() in these cases seems to work fine.
What I need to do, is to check, for instance, cy.get('#description').eq(0) to see if there is an 'a' within it, and click it if there is. If there is no 'a', do nothing.
I've tried things like this, without success:
if (cy.get('#description').eq(0).contains('a')) {
cy.get('#description').eq(0).within( () => {
cy.get('a').click();
});
Probably incorrect code anyway, with regards to the use of within.
This, too:
if (cy.get('#description').eq(0).children().contains('a')) {
cy.get('#description').eq(0).within( () => {
cy.get('a').click();
});
All help and/or hints are, as allways, appreciated. (Man, I miss Selenium sometimes, where I could use Java or Kotlin code with near full freedom.)
You can use children for this as mentioned by #lbsn and then add click().
cy.get('#description').eq(0).children('a').click()
You can also look into this, like when you get the a then click on it and exit each() and if there is no child element, the test continues and doesn't fail.
cy.get('div#description').each(($ele) => {
if ($ele.children('a').length > 0) {
cy.wrap($ele).children('a').click()
return false //Remove if you want to continue the loop even after finding the child element
}
})

Error trying to get attribute from element in Cypress

I have this HTML element:
<input id="" type="text" name="last_name" value="Userc7bff2d0-7faf-11e8-9884-8fe4c5df7f77-Updated" class="medium" maxlength="2000" autocomplete="off" tabindex="" data-reactid=".0.2.0.1.0.2.1.0.1.0.0.1:0.1.0.1.2:$/=10">
I want to get it's value property to assert that it has been updated by my test.
I have tried using its():
cy
.get(selector)
.its("value")
.should("contain", "-Updated");
But get the error:
CypressError: Timed out retrying: cy.its() errored because the property: 'value' does not exist on your subject.
I have also tried invoke:
cy
.get(selector)
.invoke("value")
.should("contain", "-Updated");
But get a similar error:
CypressError: Timed out retrying: cy.invoke() errored because the property: 'value' does not exist on your subject.
In both cases, the Cypress console output of the get() command shows the element with its value property successfully:
Yielded: input id="" type="text" name="first_name" value="Fake-Updated"
class="medium" maxlength="2000" autocomplete="off" tabindex="" data-
reactid=".0.2.0.1.0.2.1.0.1.0.0.1:0.1.0.0.2:$/=10"
I'm kind of stumped on this one. Please let me know if you want more info or have an idea what's going on.
invoke() calls a jquery function on the element. To get the value of an input, use the function val():
cy.get('input').invoke('val').should('contain', 'mytext')
This is not the same as getting the value attribute which will not update with user input, it only presets the value when the element renders. To get an attribute, you can use the jquery function attr():
cy.get('input').invoke('attr', 'placeholder').should('contain', 'username')
you can use this
cy.get('a') // yields the element
.should('have.attr', 'href') // yields the "href" attribute
.and('equal', '/home') // checks the "href" value
or
cy.get('a').should('have.attr', 'href', '/home')
for more details check this: https://docs.cypress.io/api/commands/should#Method-and-Value
If above answers doesn't work try this,
cy.get('input[placeholder*="Name"]')
Find the input with a placeholder attribute containing the word "Name".
Now there is a plugin for your need.
https://github.com/Lakitna/cypress-commands/blob/develop/docs/attribute.md
With this, you'll be able to do :
cy.get('input').attribute('placeholder').should('contain', 'username');
Aprt from above suggestions, you can also get the value using prop()
The benefit of using prop() over attr() is that:
The prop() will always give you the current value but the attr can sometimes give you the default value no matter how many times you updated it.
cy
.get(selector)
.invoke("prop","value")
.should("contain", "-Updated");

Angular 2 - Using pipe in (click) event

My question is probably simple but just can't find the way to use pipe within an event like (click) for example. Something like this:
<button (click)="quizAnswers(answer?.texte | translate | async)"></button>
I always get an error. I tried to wrap it with () or {} or []...
There are some workaround like putting the content in an attribute and then get it on the event with this.attribute but I'm sure there is a proper way !
Thanks in advance for your help
A workaround would be to call your pipes in the click handler function instead:
function quizAnswers(answer)
{
let translatePipe= new TranslatePipe();
...
return translatePipe.transform(answer?.texte);
}
I just got through this same issue. Action expressions can't contain the async pipe. However, you can use a hidden <input> element to hold the latest value of the promise/observable stream, and then access that value anywhere.
<input #dummy style="{display: none}" type="text" value="{{ myAsyncSource | async }}">
<a (click)="myFunction(dummy.value)">{{ dummy.value }}</a>
For your case of <button> there's actually a one-line solution that eliminates the need for the dummy <input>, posted in this solution:
<button type="button" #item value="{{i$ | async}}" (click)="buttonClicked(item.value)">BUTTON</button>

Resources