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)
...
Related
I'm new to Alpine and struggling to wrap my head around how to make a scenario like this work:
Let's say I have a serverside built page, that contains some buttons, that represent newsletters, the user can sign up to.
The user might have signed up to some, and we need to indicate that as well, by adding a css-class, .i.e is-signed-up.
The initial serverside markup could be something like this:
<button id='newsletter-1' class='newsletter-signup'>Newsletter 1</button>
<div>some content here...</div>
<button id='newsletter-2' class='newsletter-signup'>Newsletter 2</button>
<div>more content here...</div>
<button id='newsletter-3' class='newsletter-signup'>Newsletter 3</button>
<div>and here...</div>
<button id='newsletter-4' class='newsletter-signup'>Newsletter 4</button>
(When all has loaded, the <button>'s should later allow the user to subscribe or unsubscribe to a newsletter directly, by clicking on one of the buttons, which should toggle the is-signed-up css-class accordingly.)
Anyway, then I fetch some json from an endpoint, that could look like this:
{"newsletters":[
{"newsletter":"newsletter-1"},
{"newsletter":"newsletter-2"},
{"newsletter":"newsletter-4"}
]}
I guess it could look something like this also:
{"newsletters":["newsletter-1", "newsletter-2", "newsletter-4"]}
Or some other structure, but the situation would be, that the user have signed up to newsletter 1, 2 and 4, but not newsletter 3, and we don't know that, until we get the JSON from the endpoint.
(But maybe the first variation is easier to map to a model, I guess...)
Anyway, I would like to do three things:
Make Alpine get the relation between the model and the dom elements with the specific newsletter id (i.e. 'newsletter-2') - even if that exact id doesn't exist in the model.
If the user has signed up to a newsletter, add the is-signed-up css-class to the corresponding <button> to show its status to the user.
Bind to each newsletter-button, so all of them – not just the ones, the user has signed up to – listens for a 'click' and update the model accordingly.
I have a notion, that I might need to 'prepare' each newsletter-button beforehand with some Alpine-attributes, like 'x-model='newsletter-2', but I'm still unsure how to bind them together when Alpine has initialising, and I have the data from the endpoint,
How do I go about something like this?
Many thanks in advance! 😊
So our basic task here is to add/remove a specific item to/from a list on a button click. Here I defined two component: the newsletter component using Alpine.data() creates the data (subs array), provides the toggling method (toggle_subscription(which)) and the checking method (is_subscribed(which)) that we can use to set the correct CSS class to a button. It also handles the data fetching in the init() method that executes automatically after the component is initialized. I have also created a save method that we can use to send the subscription list back to the backend.
The second component, subButton with Alpine.bind() is just to make the HTML code more compact and readable. (We can put each attribute from this directly to the buttons.) So on click event it calls the toggle_subscription with the current newsletter's key as the argument to add/remove it. Additionally it binds the bg-red CSS class to the button if the current newsletter is in the list. For that we use the is_subscribed method defined in our main component.
.bg-red {
background-color: Tomato;
}
<script src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js" defer></script>
<div x-data="newsletter">
<button x-bind="subButton('newsletter-1')">Newsletter 1</button>
<button x-bind="subButton('newsletter-2')">Newsletter 2</button>
<button x-bind="subButton('newsletter-3')">Newsletter 3</button>
<button x-bind="subButton('newsletter-4')">Newsletter 4</button>
<div>
<button #click="save">Save</button>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('newsletter', () => ({
subs: [],
init() {
// Fetch list of subscribed newsletters from backend
this.subs = ['newsletter-1', 'newsletter-2', 'newsletter-4']
},
toggle_subscription(which) {
if (this.subs.includes(which)) {
this.subs = this.subs.filter(item => item !== which)
}
else {
this.subs.push(which)
}
},
is_subscribed(which) {
return this.subs.includes(which)
},
save() {
// Send this.sub to the backend to save active state.
}
}))
Alpine.bind('subButton', (key) => ({
'#click'() {
this.toggle_subscription(key)
},
':class'() {
return this.is_subscribed(key) && 'bg-red'
}
}))
})
</script>
I am trying to obtain the ID of some element in Cypress. Because it's ID randomly changes (yes it is strange but it is angular).
E.g. I have a <button id="dropdown-1" class="someclass"><span>Click here</button>
As I can't rely on that ID, because after I do some actions on the page it becomes to dropdown-2 etc., I want to know, what is the current ID name.
I tried something like:
cy.get('button > span').contains('Click here').find("id");
but seems that is not the correct way.
I also tried:
cy.get('button > span').contains('Click here')
.invoke('attr', 'id')
this works not as well.
You can use a combination of text and a selector which has the non-changing part of your id value. Something like:
cy.contains('button[id*="dropdown-"]', 'Click here').invoke('attr', 'id')
Now you can use use the id value in two ways.
Directly after extraction:
cy.contains('button[id*="dropdown-"]', 'Click here')
.invoke('attr', 'id')
.then((id) => {
cy.log(id) //prints id
})
Save it in alias .as and use it later:
cy.contains('button[id*="dropdown-"]', 'Click here')
.invoke('attr', 'id')
.as('idValue')
cy.get('#idValue').then((idValue) => {
cy.log(idvalue) //prints id
})
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')
Cypress how to get all children element from the parent element but not grand children
getting those two button directly from the body but not the sub/grand child(ren) from the form
For example
<body>
<button></button>
.
.
.
<button></button>
<form>
<button></button>
.
.
.
</form>
</body>
It should be as simple as specifying the direct descendants only with parent > child selector
Please try this
cy.get('body > button')
.its('length')
.should('eq', 2)
You need to be specific about the children you are looking for,
cy.get('body').children('button')
for instance,
<body>
<button>C1</button>
<button>C2</button>
<div>D</div>
<form>
<button>GC</button>
</form>
</body>
cy.get('body').children('button')
.invoke('text')
.should('eq', 'C1C2')
Checking immediate parent
I was also looking for a way to specify the parent is <body>.
There's no native Cypress way, but you can add a jQuery expression
Cypress.$.expr[":"].parentIs = function(el, idx, selector) {
return Cypress.$(el).parent().is(selector[selector.length - 1]);
}
cy.get('button')
.filter(':parentIs(body)')
You can apply a combination of children() and not to get the two children instead of three.
cy.get('body').children('button').not('form')
In case you want to get specific children element you can use the .eq() command:
cy.get('body').children('button').eq(0) //yields first button
cy.get('body').children('button').eq(1) //yields second button
You can also use filter() if your buttons have unique texts. This in my opinion is a much cleaner way-
cy.get('body').find('button').filter(':contains("buttontText")')
i'm new to nightwatch and was wondering if there's any good way to select the inner element of a current element and then get the text? Assuming i have the following..and i'm trying to retrieve the text inside (a) tags of each (li).
so i would like to get 'text to retrieve' and 'text to retrieve 2'.
...
<div class="mywrapperhere">
<ul>
<li>
<a>.....
<div>text to retrieve</div>
</a>
</li>
<li>
<a>.....
<div>text to retrieve 2</div>
</a>
</li>
<li>...
...
</div>
I'm thinking along these lines..
module.exports = {
'Demo test 1' : function (browser) {
....
//some sort of selector then gets from the anchor list
...'.mywrapperhere li a') : {
..
//for each element of the anchor..
{
//is there anyway to get it through something like
element.('div').innerHTML eg..
//or am i forced to use browser.execute( ...getElementsByTag method
//to achieve this?
}
}
browser.end();
}
};
Looking at the nightwatch api, i couldn't find anything allows me to do that. I'm particularly looking at the 'Element State' examples that doesn't seem to have a way for me to select the current element state's child element :
http://nightwatchjs.org/api/elementIdAttribute.html
The reason why i had to loop through the anchor tag level is because i'll need to retrieve a few more data besides the one from div tag, thanks!
You can use elementIdElement and elementIdText to get text from a child element. First you can get all the li elements by using .elements(). Then you use elementIdElement to get a child element. Then you can use elementIdText to get the text of this child element. Here is an example that will allow you to get the text of both list items in your snippet and log the values to the console.
browser.elements('css selector', 'li', function(listItems) {
listItems.value.forEach(function(listItem) {
browser.elementIdElement(listItem.ELEMENT, 'css selector', 'a', function(anchor) {
browser.elementIdText(anchor.ELEMENT, function(text) {
console.log(text.value);
});
});
}, browser); //have to pass in browser for scoping
});