The html structure is shown below:
<app-screen _ngcontent-put-c111>
#shadow-root (open)
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<div class="app-toolbar">
<app-toolbar-root class="ng-star-inserted">
#shadow-root (open)
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<div class="app-container">
<div class="top-level-bar">
<div id="csrContent">
<app-csr-content _ngcontent-put-c121>
<div class="csr-content">
<app-csr-toolbar class="csr-content_toolbar">
<csr-toolbar class="csr-toolbar">
<h3 class="csr-h3">
" Student "
<span _ngcontent-put-c121> - Details </span>
</h3>
</csr-toolbar>
</app-csr-toolbar>
</div>
<app-csr-content>
</div>
</div>
</div>
</app-stud-container>
</div>
</app-screen>
I am trying to retrieve the text like this:
cy.get(app-screen).find(.csr-content_toolbar .csr-toolbar h3).should('contain', 'Student - Details');
But the below error is displaying:
Timed out retrying after 4000ms: expected '<h3.csr-h3>' to contain 'Student - Details'
When I send the expected text to contain 'Student' alone, then it passes. Does it not suppose to get the text from the child elements as well as 'Student - Details'?
The " Student " part of the text is a Text node, which does not behave exactly as you might expect in Cypress.
You can test for all the text including whitespace,
cy.get('csr-toolbar h3')
.should('contain', '\n " Student "\n - Details \n ')
or you can trim beginning and end, but you still have the newline and spaces in between
cy.get('csr-toolbar h3')
.invoke('text')
.then(text => text.trim())
.should('eq', '" Student "\n - Details')
})
or you can extract the text nodes and trim them
cy.get('csr-toolbar h3')
.then($el => $el.contents()) // text nodes and span
.then($nodes => [...$nodes].map(node => node.textContent.trim()) ) // extract text
.should('deep.eq', ['" Student "', '- Details', '']) // check against an array
or you can add a step to join then up
cy.get('csr-toolbar h3')
.then($el => $el.contents()) // text nodes and span
.then($nodes => [...$nodes].map(node => node.textContent.trim()) ) // extract text
.then(texts => texts.join('')
.should('eq', '" Student "- Details')
It's probably better to test the two elements separately
cy.get('csr-toolbar')
.find('h3').should('contain', 'Student')
.find('span').should('contain', '- Details')
As the asserted string includes leading and trailing whitespaces you need to use:
cy.get('app-screen').find('.csr-content_toolbar .csr-toolbar h3').should('include.text', ' Student - Details ')
If you want those leading and trailing whitespaces to be ignored out-of-box without workarounds, then consider using cy.contains(). Otherwise you will probably need to make use of a solution like the one mentioned here.
Related
I have nested shadow elements written like below in cypress:
cy.get('app-screen')
.shadow()
.find('app-toolbar')
.shadow()
.find('app-container .csr-content .csr-content_primary:nth-child(1) csr-multi-record')
.shadow()
.find('.stud-container .stud-rec .stud__title span')
.should('have.text', 'Viola');
The element 'span' has the text (student name - Viola).
But, I don't want to use the whole statement like this. Rather, updated the 'cypress.json' with "includeShadowDom" : true.
Now, the statement I used is:
cy.get('app-screen')
.find('.csr-content_primary:nth-child(1) .stud-container .stud-rec .stud__title span')
.should('have.text', 'Viola');
The results states the text was '' and also it states never found the element.
Again, rewrote the statement as:
cy.get('app-screen')
.find('.csr-content_primary:nth-child(1)')
.find('.stud-container .stud-rec .stud__title span')
.should('have.text', 'Viola');
This time the element was found but the result was 'Viola Marcus Helen Roy Belinda...' instead of just 'Viola'. There were more than 10 elements with class as '.csr-content_primary'.
This statement took the text for all those elements that had '.csr-content_primary' in the 1st find method.
Is that possible to get the required text from '.csr-content_primary:nth-child(1)'? And, how to write in one find method instead of two?
Could someone please help me on this?
The HTML structure looks like this:
<app-screen _ngcontent-put-c111>
#shadow-root (open)
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<div class="app-toolbar">
<app-toolbar-root class="ng-star-inserted">
#shadow-root (open)
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<div class="app-container">
<div class="top-level-bar">
<div id="csrContent">
<app-csr-content _ngcontent-put-c121>
<div class="csr-content">
<app-csr-toolbar>...</app-csr-toolbar>
<div _ngcontent-put-c121>...</div>
<div class="csr-content-main">
<div class="csr-content_primary">
<csr-multi-record _ngcontent-put-c121>
#shadow-root (open)
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<style>...</style>
<div class="stud-container">
<div class="stud_rec">
<div class="stud__title">
<img-icon class="img-icon">title</img-icon>
<span class="title-desc">Viola</span>
</div>
</div>
</div>
</csr-multi-record>
</div>
</div>
</div>
<app-crs-content>
</div>
</div>
</div>
</app-stud-container>
</div>
</app-screen>
Have to guess a bit without seeing the web page, but seems like you can move the :nth-child(1) to Cypress .eq(0) which should give the same effect.
cy.get('app-screen')
.find('.csr-content_primary')
.find('.stud-container .stud-rec .stud__title span')
.eq(0)
.should('have.text', 'Viola');
The shadow DOM is having an unusual effect of the Cypress search, normally the 1st and 2nd version are equivalent.
Perhaps you can also add visibility checks, in case there's delay in loading elements.
cy.get('app-screen')
.find('.csr-content_primary:nth-child(1)')
.should('be.visible')
.find('.stud-container .stud-rec .stud__title span')
.eq(0)
.should('have.text', 'Viola');
I have the following validation in the controller's action:
foreach ($request['qtys'] as $key => $val){
if (!$this->_validateMinQty($key, $job, $val)){
$customerTitle = $job->customers()->where('customer_id',$key)->first()->title;
return redirect()->back()->withErrors(['qtys' => __('The qty of the customer :customerTitle is less than allowed qty',['customerTitle' => $customerTitle])]);
}
}
This check multiple form's input named qtys in the view:
#foreach($job->customers as $customer)
<div class="form-group {{$errors->first('qtys has-error')}}">
{!! Form::label('qtys-'.$customer->id, __('Qty').' '.$customer->title) !!}
<div class="row">
<div class="col-md-9">
{!! Form::text('qtys['.$customer->id.']',$customer->pivot->e_production,['class' =>'form-control qtys', "data-sumequal"=>"qty",'required' => 'required','title' => $customer->pivot->aid,'id' => 'qtys-'.$customer->id]) !!}
<div class="help-block with-errors"></div>
#php ($eleE = $errors->first('qtys'))
#include('layouts.form-ele-error')
</div>
<div class="col-md-3">
<i class="fox-add"></i>{{__('Add Storage')}}
</div>
</div>
</div>
#endforeach
The above code works, but with the following limitation:
The error message is rendered under every input named qtys[x] where x is an integer and the first input only Testana has the invalid qty, like the following screen shot:
In the controller's action return message, I have tried to use indexed name for the input like the following:
return redirect()->back()->withErrors(['qtys.10' => ....
However, it prevents rendering the error message under any qtys field. Is there any solution?
The solution that I have found starts from the definition of first method found in the view :
#php ($eleE = $errors->first('qtys'))
This, in my code, should be changed to:
#php ($eleE = $errors->first('qtys.'.$customer->id))
Because the multiple fields have gotten keys equals to the customer id. This is a technique I usually use, when I want to send double piece of data in single post or in single form element.
Then in the controller, I keep the first try,
return redirect()->back()->withErrors(['qtys.'.$key => __('The qty of the customer :customerTitle is less than allowed qty',['customerTitle' => $customerTitle])]);
Where $key is an integer.
This is the HTML sample:
<div class="wpb_text_column">
<div class="wpb_wrapper">
<p style="text-align: center;">First text part </p>
<p style="text-align: center;">Second text part </p>
<p style="text-align: center;">Third text part</p>
</div>
</div>
<div class="wpb_text_column">
<div class="wpb_wrapper">
<p style="text-align: center;">First text part </p>
<p style="text-align: center;">Second text part</p>
</div>
</div>
With below code
tree = html.fromstring(html_sample)
tree.xpath('//div[#class="wpb_text_column"]/div[#class="wpb_wrapper"]/p/a/text()')
I can get list of text values
['First text part ', 'Second text part ', 'Third text part', 'First text part ', 'Second text part']
However, I want to get all values from each div as single string like
['First text part Second text part Third text part', 'First text part Second text part']
and
//div[#class="wpb_text_column"]/div[#class="wpb_wrapper"]/normalize-space()
seem to be exact XPath to solve the problem, but lxml doesn't support /normalize-space() syntax:
lxml.etree.XPathEvalError: Invalid expression
So how to get desired output in lxml?
Solved with below code:
[" ".join(string.text_content().split()) for string in tree.xpath('//div[#class="wpb_text_column"]/div[#class="wpb_wrapper"]')]
I have a code segment that looks like this
<div class="class1">
<div class="ng-scope"> apple </div>
<div class="ng-scope"> butter </div>
I want to get the text of the children. I do not want to use ng-scope since that is not a unique identifiter, but class1 is. How can I do something like identifying the parent, so ('.class1') or using xpath, and then getting the text from all its children. so the print out would look something like
" [ apple, butter ]; "
$$('.class1 div').getText().then(function(values) {
// This is an array containing ['apple', 'butter']
});
I have a div and inside of this, there are a lot of spans as follows:
<div id="mydiv">
<span id="first_id" itemindex="0">first span</span>
<span id="second_id" itemindex="1">second span</span>
<span id="third_id" itemindex="2">third span</span>
...
</div>
I want to define the function "getItemIndexValues()" in JQuery who get all values in "myDiv". It is possible?
You want to be using data- prefixed attributes.
HTML:
<div id="mydiv">
<span id="first_id" data-itemindex="0">first span</span>
<span id="second_id" data-itemindex="1">second span</span>
<span id="third_id" data-itemindex="2">third span</span>
</div>
JavaScript:
var arr = $( 'span', '#mydiv' ).map( function () {
return $( this ).data( 'itemindex' );
}).get();
Here, arr will be [ '0', '1', '2' ]
Live demo: http://jsfiddle.net/fQJAk/
Btw, if you prefer an array of numbers, do this:
return +$( this ).data( 'itemindex' );
-------^
I didn't verified it but I think this should work:
$("#mydiv>span").each(function(a,b){
alert($(b).attr('itemindex'));
});
By using the child selector here: http://api.jquery.com/child-selector/