Cypress Assertions, verify class exists for certain text - cypress

Afternoon Stack community.
Pretty new to cypress, what I'm looking to do is identify some text on a page and chain that there should be a particular class with that text. Able to retrieve the text fine but not the class.
From my spec file:
cy.contains('some text').should('have.css', 'fa-pencil')
Page source
<h2 class="module__header big-right" style="text-transform: none;">
some text
<span class="push-right">
<a target="_new" class="fa fa-question-circle-o" href="{env details}" aria-hidden="true"></a>

If the page source shown is accurate, "some text" is outside the element that has the class you seek.
It's on the first child of <h2>, so adding .children() to the test will allow you to test it with have.class
cy.contains('some text') // this will give the parent <h2> element
.children().eq(0)
.should('have.class', 'fa-pencil')
When Cypress runs cy.contains('some text') it finds the closest containing element going upwards in the hierarchy from the text.
In this case it's the <h2> since is closed off before the text occurs, so that can't be the containing parent.
You can still verify the class of <a.fa.fa-pencil> by using a navigational command children() to go inside the <h2>.
You could also specify the child with that particular class like this
cy.contains('some text') // this will give the parent <h2> element
.children('.fa-pencil') // child with the class
.should('have.class', 'fa-pencil') // not really needed, gets tested in line above

In case of an exact match with the text:
cy.get('selector')
.should('have.text', 'some text')
.and('have.class', 'fa fa-pencil')
In case of partial match with the text:
cy.get('selector')
.should('contain', 'some text')
.and('have.class', 'fa fa-pencil')

Related

Cypress assert item in table related to it's title

On my page I have multiple tables with this structure:
<div class="box"><h4>Service 1</h4></div>
<div class="container"><ag-grid-table>items in the table</ag-grid-table><div>
<div class="box"><h4>Service 2</h4>
<div class="container"><ag-grid-table>items in the table</ag-grid-table><div>
</div>
<div class="scope-header"><h5>Service 3</h5></div>
<div class="scope-grid"><ag-grid-table>items in the table</ag-grid-table><div>
As you can see, all sections have different structure, and there are even more on the page.
What I want to achieve is to assert that the Service 3 table contains the correct item.
I was thinking about using cy.get('div.scope-header').contains('Service 3') and then using next() or sibling() but the ag-grid-table is neither directly next or sibling.
The only idea I have is to check ag-grid-table:nth(n), this should work, but is there a solution that selects the table according the header title text?
I mean select the table e.g. belonging to Service 3 only.
If you want to continue from cy.get('div.scope-header')
cy.contains('div.scope-header', 'Service 3') // this returns div.scope-header
// not <h5>Service 3</h5> which
// cy.get().contains() would do
.next() // next() moves you to <div class="scope-grid">
.find('ag-grid-table') // table is within that
You can do something like this with .sibling()
cy.contains('div.scope-header', 'Service 3')
.sibling('div.scope-grid') // move to specified sibling
.find('ag-grid-table') // within that element
.should(...)
From what I understood from your question, You can do something like this:
cy.contains('div.scope-header', 'Service 3')
.next() //goes to .scope-grid
.within(() => {
// scopes search within .scope-grid
cy.contains('ag-grid-table', 'some text').should('be.visible')
//OR
cy.get('ag-grid-table').find('item selector').should('be.visible')
//OR
cy.get('ag-grid-table')
.find('item selector')
.should('have.text', 'some text')
})
Something like this, you can do use Traversal commands.
Adding selectors to next() and sibling() is more robust when page layout changes.
cy.contains('h5', 'Service 3')
.parent('div.scope-header')
.next('div.scope-grid')
.children('ag-grid-table')
.find('div.ag-row').eq(2)
...

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
}
})

How to capture current set of classes used on an element

I have two elements on the page: button1 and button2. One of them has class active set on an initial load.
I want to grab button1 and check if active is set. If set, then for my tests I want to use button2, if not I stick with button1.
For my test, I need to test the switch action between two buttons, hence why I need to use the button which is not an active one.
Each button does have a text, i.e. <div class="active" data-cy="button1">Button 1</div>, so I was also thinking that maybe a different option would be to grab a button1 with class active set and check if it exists? Not sure if that is possible...
How can the above be achieved?
You want to avoid conditional processing in the test, try to use a selector that targets what you want.
Given
<button class="active">Button 1</button>
<button>Button 2</button>
use a :not() selector
cy.get('button:not(.active)')
.should('have.text', 'Button 2')
If there's other button, also not active but not wanted,
<button class="active">Button 1</button>
<button>Button 2</button>
<button>Ignore me</button>
you can add a filter
cy.get('button:not(.active)')
.filter((index, button) => ['Button 1', 'Button 2'].includes(button.innerText))
.should('have.text', 'Button 2')
If you really meant the active attribute,
<button active>Button 1</button>
<button>Button 2</button>
cy.get('button:not([active])')
.should('have.text', 'Button 2')

SharepointFramework - how to set the actual webpart code as initial value in PropertyFieldCodeEditor

Hello i am using this custom property pane control called PropertyFieldCodeEditor. what i want is to display the actual webpart code as the initial value of the code editor, then after i click save, the changes will be reflected on the webpart..
this is the code of PropertyFieldCodeEditor
PropertyFieldCodeEditor('htmlCode', {
label: 'Edit Code',
panelTitle: 'Edit Code',
initialValue: "",
onPropertyChange: this.onPropertyPaneFieldChanged,
properties: this.properties,
disabled: false,
key: 'codeEditorFieldId',
language: PropertyFieldCodeEditorLanguages.HTML
})
i tried to put this.domElement on initialvalue but it only accept string, also i cand find a way to convert this.domelement to string..
also what should i put inside
protected onPropertyPaneFieldChanged(path: string, newValue: string) {}
For initialValue, you should be able to use this.domElement.innerHTML or this.domElement.outerHTML. Both are strings representing the contents of domElement (note, domElement is really just an HTMLElement).
outerHTML will include everything, including one extra div layer on the outside:
<div style="width: 100%;">
<div class="helloWorld_d3285de8">
...
</div>
</div>
innerHTML is only the inside contents of that div:
<div class="helloWorld_d3285de8">
...
</div>
You'll probably want innerHTML, since that's what's initially used in the render method.
Once you set the initialValue, you would have accomplished copying your web part code into the PropertyFieldCodeEditor. Now you would need to get the PropertyFieldCodeEditor contents (which is stored in your property htmlCode) assigned back into this.domElement.innerHTML.
Unfortunately, in onPropertyPaneFieldChanged, this points to the PropertyFieldCodeEditor, not to the web part class anymore. You may or may not be able to do it here - I didn't look too deeply into it.
The easiest solution I figured was, in render, to assign this.domElement.innerHTML like so:
public render(): void {
this.domElement.innerHTML = this.properties.htmlCode || `
<div class="${styles.helloWorld}">
...
</div>`;
}
This way, the web part will initially render whatever comes after the ||. But as soon as you save a change to the PropertyFieldCodeEditor, it will start rendering the htmlCode instead. This only works because initially htmlCode is undefined. (Note it won't work exactly like this if you assign something truthy to it via your web part's preconfiguredEntries - you would have to write some further checks. The principle is the same, though.)

Why does this Web2Py ajax call fail to return the value of a variable?

Here is the relevant snippet from my Web2Py view:
{{for candidate in rows:}}
<div class="well col-sm-12">
<button type="button" name="up_button" onclick="ajax('{{=URL('default', 'arrow_button_callback')}}', ['name'], 'target')" class="fa fa-caret-up arrow-up fa-4x"></button>
<span>{{=candidate.votes}}</span>
<button type="button" name="down_button" onclick="ajax('{{=URL('default', 'arrow_button_callback')}}', ['name'], 'target')" class="fa fa-caret-down arrow-down fa-4x"></button>
{{=IMG(_src=URL('photos',candidate.path_to_photo), _alt="Photo of Candidate")}}
{{=candidate.name}}
<div id="target"></div>
</div>
{{pass}}
And the relevant snippet from my Web2Py controller:
def arrow_button_callback():
response.flash = str(request.post_vars.name)
return request.post_vars.name
So why do I see the string "None" in my target div (and in my flash)?
Thank you for your help. I read chapter 11 of the Web2Py book and I'm still confused.
I really want to be able to pass candidate.id (depending on which row's button was pressed) and the button direction to controller variables. Please let me know if there's a better way to do this.
--Benjamin
From the web2py documentation (emphasis added):
ajax(url, [name1, name2, ...], target)
It asynchronously calls the url (first argument), passes the values of the field inputs with the name equal to one of the names in the list (second argument)...
In your code, your second argument is ['name']. However, there is no input field with the name "name" anywhere, so no "name" variable gets posted to web2py (therefore, request.post_vars.name is None, which is the default value returned whenever you attempt to get a variable that does not exist in request.post_vars).
In this case, because you are not placing any data in form input fields, you can simply ignore the second argument to ajax(). Instead, pass the relevant data via the URL itself:
{{url = URL('default', 'arrow_button_callback',
vars=dict(id=candidate.id, direction='up'))}}
<button type="button" name="up_button" onclick="ajax('{{=url}}', [], 'target')"
class="fa fa-caret-up arrow-up fa-4x"></button>
Then in the arrow_button_callback controller, you can access the candidate id via request.get_vars.id (or just request.vars.id). And you can access the arrow direction via request.vars.direction. Create a new URL for the "down" button, with direction='down'.

Resources