how to get value of select<> & option<> tag in cypress - cypress

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

Related

select value binding and svelte-i18n integration problem

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>

Cypress command that return values from DOM

In my DOM i have an input and a div, and i want to get the value of both in one command.
Here is an HTML exemple
<div id="myDiv">Content of the div</div>
<input id="myInput" value="2000" />
Here is what i tried for my command
Cypress.Commands.add("getDomValues", () => {
var divValue = cy.get('#myDiv').invoke('text')
var inputValue = cy.get('#myInput').invoke('val')
return cy.wrap({
divValue:divValue,
inputValue:inputValue
});
});
If there is no cy.wrap around my returned object i get this error
Unhandled rejection CypressError: Cypress detected that you invoked
one or more cy commands in a custom command but returned a different
value.
And then in my test for now i use it like that
cy.getDomValues().then((values)=>{
console.log(values)
})
In my console inside the returned object i have something like that for both values
$Chainer {userInvocationStack: " at Context.eval (http://localhost:8888/__cypress/tests?p=cypress/support/index.js:181:24)", specWindow: Window, chainerId: "chainer4419", firstCall: false, useInitialStack: false}
Do you have any idea how i could have a result like that ?
{
divValue:"Content of the div",
inputValue:"2000"
}
You need to access the values with .then()
Cypress.Commands.add("getDomValues", () => {
cy.get('#myDiv').invoke('text').then(divValue => {
cy.get('#myInput').invoke('val').then(inputValue => {
// no need to wrap, Cypress does it for you
return {
divValue, // short form if attribute name === variable name
inputValue
}
});
The error you received was because you were returning Chainers instead of values.
You can use .as() to assign an alias for later use.
cy.get('#myDiv').invoke('text').as('divValue')
cy.get('##myInput').invoke('val').as('inputValue')
Then use these values later separately like -
cy.get('#divValue').then(divValue => {
//Do something with div value
})
cy.get('#inputValue').then(inputValue => {
//Do something with input value
})
OR, use these values later together like -
cy.get('#divValue').then(divValue => {
cy.get('#inputValue').then(inputValue => {
//Do something with div value
//Do something with input value
})
})

vue-18n - how to force reload in computed function when changing language

I am using vue-i18n but I also have some content which is stored in database. I would like my text to be updated when the user changes the language.
I am using laravel and vuejs2.
Thanks in advance, I am not super familiar with vuejs yet. I hope it's clear enough.
in ContenuComponent.vue
<template>
<div>
{{$i18n.locale}} <== this changes well
<div v-html="textcontent"></div>
<div v-html="textcontent($i18n.locale)"></div> <== this won't work, I am wondering how to put params here (more like a general quetsion)
</div>
</template>
<script>
export default {
name:'contenu',
props: {
content: {
type: String,
default: '<div></div>'
}
},
computed: {
textcontent: function () {
console.log(navigator.language); <== this gives me the language as well, so i could use it if I can make it reload
var parsed = JSON.parse(this.content);
parsed.forEach(element => {
if(navigator.language == element['lang']){
return element['text'];
}
});
}
}
}
</script>
in ContentController
public function getcontent(){
$content = DB::connection('mysql')->select( DB::connection('mysql')->raw("
SELECT text, lang from content
"));
return view('myvue', ['content' => json_encode($content)]);
}
in content.blade.php
<div id="app">
<contenu content="{{ $content }}"></contenu>
</div>
You SHOULD NOT pass parameters to computed props! They are not methods and you should create method instead:
methods: {
textcontent () {
var parsed = JSON.parse(this.content)
parsed.forEach(element => {
if (navigator.language == element['lang']){
return element['text']
}
})
}
}
Also you should consider ES6 syntax:
methods: {
textcontent () {
var parsed = JSON.parse(this.content)
const content = parsed.find(element => navigator.language == element['lang'])
return content['text']
}
}
Much cleaner!
Please make sure to read about computed props and how they are different than methods or watchers: docs

Vuejs 2 using filterBy and orderBy in the same computed property

I am having a hard time trying to joing a filterBy with orderBy, on vuejs 2.0, with all research I have found about this subject, as of link on the bottom of my question.
This is my filter, which is working:
// computed() {...
filteredResults() {
var self = this
return self.results
.filter(result => result.name.indexOf(self.filterName) !== -1)
}
A method called in the component:
// methods() {...
customFilter(ev, property, value) {
ev.preventDefault()
this.filterBook = value
}
In the component:
// Inside my component
Name..
And another filter, which works as well:
// computed() {...
orderByResults: function() {
return _.orderBy(this.results, this.sortProperty, this.sortDirection)
}
To comply with my orderBy I have this method:
// methods() {...
sort(ev, property) {
ev.preventDefault()
if (this.sortDirection == 'asc' && this.sortProperty == property ) {
this.sortDirection = 'desc'
} else {
this.sortDirection = 'asc'
}
this.sortProperty = property
}
And to call it I have the following:
// Inside my component
Name..
I have found in the docs how we use this OrderBy, and in this very long conversation how to use filter joint with sort, but I could really not implement it...
Which should be some like this:
filteredThings () {
return this.things
.filter(item => item.title.indexOf('foo') > -1)
.sort((a, b) => a.bar > b.bar ? 1 : -1)
.slice(0, 5)
}
I could not make this work...
I tried in many forms as of:
.sort((self.sortProperty, self.sortDirection) => this.sortDirection == 'asc' && this.sortProperty == property ? this.sortDirection = 'desc' : this.sortDirection = 'asc' )
But still, or it does not compile or it comes with errors, such as:
property not defined (which is defines such as I am using it in the other method)
method of funcion not found (is happens when comment my method sort.. maybe here is what I am missing something)
Thanks for any help!
The ideas of your approach seem valid, but without a full example it's hard to tell what might actually be wrong.
Here's a simple example of sorting and filtering combined. The code can easily be extended e.g. to work with arbitrary fields in the test data. The filtering and sorting is done in the same computed property, based on the parameters set from the outside. Here's a working JSFiddle.
<div id="app">
<div>{{filteredAndSortedData}}</div>
<div>
<input type="text" v-model="filterValue" placeholder="Filter">
<button #click="invertSort()">Sort asc/desc</button>
</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
testData: [{name:'foo'}, {name:'bar'}, {name:'foobar'}, {name:'test'}],
filterValue: '',
sortAsc: true
};
},
computed: {
filteredAndSortedData() {
// Apply filter first
let result = this.testData;
if (this.filterValue) {
result = result.filter(item => item.name.includes(this.filterValue));
}
// Sort the remaining values
let ascDesc = this.sortAsc ? 1 : -1;
return result.sort((a, b) => ascDesc * a.name.localeCompare(b.name));
}
},
methods: {
invertSort() {
this.sortAsc = !this.sortAsc;
}
}
});
</script>

How to pass AJAX arguments to the extbase action?

Now that I managed to get values from the database, I want to specify more what I want to be passed.
From a select box that reacts to the event function below, I want to read out a value (uid of a record) and pass it to my ajaxAction:
var uid;
$('#mySelectBox').change(function() {
arguments = $(this).attr('value');
var uri = '<f:uri.action arguments="{uid: '+uid+'}" action="ajax" controller="Mycontroller1" pageType="89657201" />';
jQuery.getJSON(uri, function(result) {
// do something
});
});
I tried it with arguments, no idea if that is the right way. Additionally, as Marcus Biesioroff suggested, I should save my JS into a separate file, but then I would have to write the uri on my own instead of the Fluid way, right?
My ajaxAction looks like this:
public function ajaxAction($uid) {
$dataFromRepo = $this->myRepository->findByUid($uid);
$resultArray = array(
"field1" => $dataFromRepo->getField1(),
"field2" => $dataFromRepo->getField2(),
"field3" => $dataFromRepo->getField3(),
"field4" => $dataFromRepo->getField4(),
);
return json_encode($resultArray);
}
I'm sure that the uid is not passed correctly, everything else works.
There are some mistakes:
You can't break vievhelper's syntax with JS even if it's placed in view, you need to pass full path of the action from <f:uri.action />
you cannot place this JS in view, because it contains curly brackets there's other description of the issue
you need to call ajax function from external file and pass to it action's link and uid separately, and then add the
in the view:
<script type="text/javascript">
var actionsPathFromViewHelperSetInTheView
= '<f:uri.action action="ajax" controller="Mycontroller1" pageType="89657201" />';
</script>
<script type="text/javascript" src="path/to/ext/Public/yourExternal.js"></script>
<!-- of course this field may/should be created with Fluid's viewhelper -->
<select id="mySelectBox" onchange="performAjaxCall(this)">
<option value="1">Item 1</option>
<option value="2">Item 2</option>
<option value="3">Item 3</option>
</select>
in the yourExternal.js (of course you need to change tx_yourextkey_yourplugin prefix to your own)
function performAjaxCall(selectFieldObj) {
$.ajax({
url: actionsPathFromViewHelperSetInTheView,
data:{
"tx_yourextkey_yourplugin[uid]":selectFieldObj.value
},
success:function (data) {
// do something with your json
alert('Load was performed.');
}
});
}
in your controller:
public function ajaxAction() {
// try to always validate the incoming arguments
if (!$this->request->hasArgument('uid') || intval($this->request->getArgument('uid')) == 0) {
header('HTTP/1.1 400 Bad Request');
return json_encode(array('error'=> 'Bad request'));
}
$uid = intval($this->request->getArgument('uid'));
$dataFromRepo = $this->myRepository->findByUid($uid);
if ($dataFromRepo == null) {
header('HTTP/1.1 404 Not found');
return json_encode(
array('error'=> 'Not found or you have no access or something else... happens...')
);
}
$resultArray = array(
"field1" => $dataFromRepo->getField1(),
// etc...
);
return json_encode($resultArray);
}

Resources