Hi I am new to Cypress and trying to automate this scenario -
I have a dropdown like this having duplicate values:
<select name="cars" id="cars">
<option value="volvo">Volvo</option>
<option value="volvo">Volvo</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>
</select>
Now I want to select the second Volvo that appears on the list. Things I have tried:
cy.get('select#cars').select('Volvo') //This selects the first Volvo
Edit: Now Based on the selection above, a new element is displayed on the webpage and I am checking the inner text of that element -
cy.get('selector', {timeout: 8000}).should('have.text', some text)
Looking at the source, the .select() command dispatches input and change events to the option chosen.
You can do the same, "manually" selecting
cy.get('option').eq(1).then($option => {
const input = new Event('input', { bubbles: true, cancelable: false })
$option.get(0).dispatchEvent(input)
const change = document.createEvent('HTMLEvents')
change.initEvent('change', true, false)
$options.get(0).dispatchEvent(change)
})
Ref: /driver/src/cy/commands/actions/select.js
Also this works
cy.get('option').eq(1)
.trigger('input', { force: true })
.trigger('change', { force: true })
// force because select is not open so option isn't visible
While either way triggers events, it does not select the option.
This will do that,
cy.get('option').eq(1)
.trigger('input', { force: true })
.trigger('change', { force: true })
.then($option => {
cy.get('select').then($select => $select.val($option.val()))
})
Related
I want to disabled a button on a lightning datatable based on a boolean field, I came to this solution:
const columns = [
{ label: 'Client', type:'button',
typeAttributes: {
label: 'Client',
disabled: {fieldName: 'Client__c' }
} }
];
The problem is, I need to make visible when is true, but it actually doing the opposite, i search to a enabled property or trying something like this:
Client__c == true ? false : true;
but it doens't work..
I also try this solution here
this.tableData = result.data.map((value) => ({ ...value, clientDisabled: ('Client__c' == true) ? false : true }));
And the column:
const columns = [
{ label: 'Client', type:'button',
typeAttributes: {
label: 'Client',
disabled: {fieldName: 'clientDisabled' }
} }
];
Also, not work, all buttons became disabled.
Also, I would like to put a - when is disabled (and the field = false), like this:
'Client__c' == true is always false; you're comparing two literal values that will never be equal. You'll want to use the data from the record instead:
this.tableData = result.data.map((record) => ({ ...record, clientDisabled: !record.Client__c }));
I have an array of data which is gotten from an api and is displayed using Vuejs like this
<div class="col-sm-9">
<Select class="form-control" name="building" v-model="allBuild.id" #change="showBuilding()">
<option v-for="b in allBuilding" :key="b.id" :value="b.id">
{{ b.description }}
</option>
</Select>
</div>
data(){
return {
allBuilding: [],
allBuild:{
id: null
},
}
}
async created(){
let self = this;
await axios.get( self.$apiAdress + '/api/electricity-building?token=' + localStorage.getItem("api_token") )
.then(response => {
self.allBuilding = response.data
// for(index in Object.keys(self.allBuilding)){
// self.allBuild = self.allBuilding[0].fields.id
// }
if(response.data.length){
this.allBuild = response.data.id
}
})
}
showBuilding()
{
console.log(this.allBuild)
},
My Question is that how do i get the ID of each data when the onchange is fired. if i click the building from the list of all building displayed i get the id of the first element in the array which isn't what i want.
What i want is that any building i click i should get an id of each.
For example click building 1 get id 1 click building 2 get id 2 click building 3 get id 3
etc
try this:
showBuilding(evt){
console.log(evt.target.value)
}
I'm dealing with something that seems to be a bit beyond my ken here ...
It's about select value binding. Here below, it's a simple peace of code that is working perfectly in a classic svelte SPA.
<script>
let countrySelected = {
code: 'BE',
name: 'Belgium',
};
const countries = [
{
code: 'FR',
name: 'France',
},
{
code: 'BE',
name: 'Belgium',
},
{
code: 'GA',
name: 'Gabon',
},
];
</script>
<select bind:value={countrySelected}>
{#each countries as country}
<option value={country} selected={country.code === countrySelected.code}>
{country.name}
</option>
{/each}
</select>
But for whatever reason, it doesn't work anymore when it's working in a sveltekit app which uses the svelte-i18n npm package.
/src/routes/__layout.svelte
<script>
import { setupI18n, isLocaleLoaded } from '$lib/services/i18n.js';
$: if (!$isLocaleLoaded) {
setupI18n({ withLocale: 'fr-FR' });
}
</script>
{#if !$isLocaleLoaded}
Please wait...
{:else}
<main>
<slot />
</main>
{/if}
/src/lib/services/i18n.js
The code of /src/lib/services/i18n.js comes from:
https://phrase.com/blog/posts/how-to-localize-a-svelte-app-with-svelte-i18n/
&
https://lokalise.com/blog/svelte-i18n/
import { derived } from 'svelte/store';
import { dictionary, locale, _, date, time, number } from 'svelte-i18n';
const MESSAGE_FILE_URL_TEMPLATE = 'http://localhost:3000/lang/{locale}.json';
let cachedLocale;
async function setupI18n({ withLocale: _locale } = { withLocale: 'en-GB' }) {
const messsagesFileUrl = MESSAGE_FILE_URL_TEMPLATE.replace(
'{locale}',
_locale
);
const res = await fetch(messsagesFileUrl);
const messages = await res.json();
dictionary.set({ [_locale]: messages });
cachedLocale = _locale;
locale.set(_locale);
}
// Before any locale is set, svelte-i18n will give locale an object type.
// Once it is correctly set, the libray will set locale
// to the code of the active locale, e.g. "en", a string type.
// We check for this in our devired store, and make sure that isLocaleLoadedās value
// is true only after i18n initialization is successful.
const isLocaleLoaded = derived(locale, $locale => typeof $locale === 'string');
export { _, locale, setupI18n, isLocaleLoaded, date, time, number };
Problem:
The <select> element in the index.svelte file should show "Belgium" because:
countrySelected = { code: 'BE', name: 'Belgium', }
The problem is that in __layout.svelte, svelte-i18n makes a kind of refresh just after the item has been selected so it looks nothing is selected.
It's probably because of the way I've integrated svelte-i18n in my project as I only understood the broad strokes but once again ... the devil is in the detail :D
Thank you so much for your help. You can clone this repos, it gonna be easier to understand:
git clone https://github.com/BigBoulard/sveltekit-sveltei18n
npm i
npm run dev
With your example, if you inspect the select element in a browser, the selected attribute is not set on any option.
Svelte has a very easy to use and straight forward way to set the initial selected value.
It automatically handles the required attribute on the option element when binding a value to the select element.
The issue in your example is, that countrySelected looks the same as the object/dictionary inside your countries array, but is actually a new object and therefore Svelte can not select it.
This example should work:
<script>
const countries = [
{
code: 'FR',
name: 'France',
},
{
code: 'BE',
name: 'Belgium',
},
{
code: 'GA',
name: 'Gabon',
},
];
let countrySelected = countries.find(x => x.code === 'BE');
</script>
<select bind:value={countrySelected}>
{#each countries as country}
<option value={country}>
{country.name}
</option>
{/each}
</select>
Trying to click a button based on another element with enabled/disabled status. For some reason, my disabled check code is not working and it always ends in another statement ('No existing routes found') even though the UI has a select button enabled.
cy.get('voyage-suggested-routes')
.find('button.selectButton')
.then(($routes) => {
if ($routes.is(":disabled")) {
cy.log("No existing routes found...")
} else {
cy.log("Deleting......")
cy.get('.delete-button').click({ force: true, multiple: true })
}
});
This is the DOM: (There are 3 elements by default and a delete option will be there for each Select button if it is not disabled.)
<button class="selectButton" disabled route="1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="..."></path></svg>
SELECT
</button>
Tried the jquery method as well but the same result.
var btnStatus = Cypress.$('.selectButton')
.is(":disabled");
if (btnStatus == true) {
cy.log("Deleting......")
cy.get('.delete-button').click({ force: true, multiple: true })
} else {
cy.log("No existing routes found...")
}
What am I missing?
Update 1:
After Electron's input, my new code is:
cy.get('voyage-suggested-routes')
.find('button.selectButton')
.then(($routes) => {
if ($routes.is(":disabled").length === 0) {
cy.log("No existing routes found...")
} else {
cy.log("Deleting......")
cy.get('.delete-button').click({ force: true, multiple: true })
}
});
From the docs jQuery .is()
Description: Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
So if only one route is disabled, the delete will not go ahead.
Try using a filter to see if any are disabled.
cy.get('voyage-suggested-routes')
.find('button.selectButton')
.then(($routes) => {
const disabled = $routes.filter(":disabled")
if ($routes.length === disabled.length) {
cy.log("No existing routes found...")
} else {
cy.log("Deleting......")
cy.get('.delete-button').click({ force: true, multiple: true })
}
})
It's because you need each instead of then, like this:
.each(($routes) => {
in order to perform as many actions as there are button elements.
Edit: as Electron stated in the comments, the suggestion below will fail a test if all buttons are disabled, so take care if you use it.
And to better optimize your code, your can set the selector as .find('button.selectButton:not(:disabled)') then you don't need if block at all, just the delete statement.
Here's a custom command which conditionally runs a callback, depending on the result of filtering by given selector.
Not a lot of difference to .then(($routes) => { const disabled = $routes.filter(":disabled") pattern. Unfortunately ending part of a chain is quite difficult, as the whole test is considered one chain.
Cypress.Commands.add('maybe', {prevSubject:true}, (subject, selector, callback) => {
const result = subject.filter(selector)
if (result.length > 0) {
cy.log(`Maybe: Found ${result.length} "${selector}"`)
cy.wrap(result).then(callback)
return
}
cy.log(`Maybe: Not found: "${selector}"`)
})
cy.get('button.selectButton')
.maybe(':not(:disabled)', ($result) => {
// can use result of filter here
console.log('result is', $result))
// or conditionally run some other commands
cy.log(`Deleting...`)
cy.get('.delete-button').click({ force: true, multiple: true })
})
// runs either way
cy.wrap('something')
.then(x => console.log('next', x))
Is there any method or way to get the 'value' of <select><option> tag?
I have a scenario where i need to get the value of <select><option> tag, and store it in a variable because the value is dynamic, changing on every execution.
I cannot hard code that value like this, because it is changing every time:
cy.get(' ').select('b5c12d3-2085-4ed8-bd57-8a93f6ae1e64')
so i want to do something like this after getting that value:
cy.get(' ').select(value)
and by using text value, it is not selecting
cy.get(' ').select('related new) ....it is not working
You can use: cy.get('select option[value]').then($el => <logic to store values in Cypress.env>);
Markup:
<select>
<option>Select</option>
<option value="b5c12d3-2085-4ed8-bd57-8a93f6ae1e64">Some value</option>
<option value="more-such-dynamic-value">More value</option>
</select>
Test:
cy.get('select option[value]').then($options => {
return new Cypress.Promise((resolve, reject) => {
const values = [];
for (let idx = 0; idx < $options.length; idx++) {
values.push($options[idx].value);
}
if (values) {
resolve(values);
} else {
reject(null); // handle reject;
}
});
}).then((options) => {
Cypress.env('selectValues', options);
});
cy.log(`selectValues: ${Cypress.env('selectValues')}`);
cy.get('select').select('Some value').invoke('val').should('eq', Cypress.env('selectValues')[0]);
Cypress.env('selectValues', undefined); // clear
cy.log(`After reset, selectValues: ${Cypress.env('selectValues')}`);
Test Screenshot
You can use Cypress's invoke() for this, like:
cy.get('select option').each(($option) => {
cy.wrap($option).invoke('attr', 'value').then(($val) => {
console.log($val);
});
});
You could use the nth-child() selector to only grab one of the options:
cy.get('select option:nth-child(2)').invoke('attr', 'value').then(($val) => {
console.log($val);
});