Playwright + CodeceptJS - Unable to find element by Xpath - xpath

In my code I can usually find an element by Xpath and perform actions like shown below
await I.fillField('//*[#id="edit-name"]','user1');
I am seeing the following error when I perform the following action. As the ID is dynamically created. Is there a recommended approach to tackle this type of elements?
await I.fillField('//*[#id="crmUiId_1"]','SomeTextHere');
Error:
**TypeError: Cannot read property '$$' of null
at findElements (node_modules/codeceptjs/lib/helper/Playwright.js:2087:18)
at Playwright._locate (node_modules/codeceptjs/lib/helper/Playwright.js:822:12)**
Associated HTML:
<div crm-ui-field="{name: 'caseTypeDetailForm.title', title: ts('Title')}" class="ng-isolate-scope crm-section"><div class="label">
<label crm-ui-for="caseTypeDetailForm.title" crm-depth="1" crm-ui-force-required="" for="crmUiId_1"><span ng-class="cssClasses"><span ng-transclude=""><span class="ng-binding ng-scope">Title</span></span><span crm-ui-visible="crmIsRequired" class="crm-marker ng-isolate-scope" title="This field is required." style="visibility: inherit;">*</span></span></label>
<!-- ngIf: help -->
</div>
<div class="content" ng-transclude="">
<input crm-ui-id="caseTypeDetailForm.title" type="text" name="title" ng-model="caseType.title" class="big crm-form-text ng-pristine ng-scope ng-empty ng-invalid ng-invalid-required ng-touched" required="" id="crmUiId_1">
</div>
<div class="clear"></div>
</div>
The issue here is actually the ID "crmUiId_1" is dynamically generated. Instead I tried xpath with ng-model="caseType.title" but it doesn't seem to be working either.

I would just make sure you wait for it:
await page.waitForSelector('#crmUiId_1')
await page.fill('#crmUiId_1', 'whatever')
Otherwise the page might still be loading.

Related

Thymeleaf setting object attribute based on click

What I am trying to do set object user variable attribute based on click.
`
<form class="container" th:action="#{/processSignup}" method="post"
th:object="${user}">
<div class="switch">
<div class="MenteeSignUp" onclick="tab1();" th:onclick="*{}" >Mentee</div>
<div class="MentorSignUp" onclick="tab2();" th:value="MENTOR" th:field="*{userRole}">Mentor</div>
</div>
`
Trying to add different role-based what user click which either mentor or mentee which you can see from the screenshot.
I am kind of new to thyme leaf, so I tried to have th:onClick and then tried to assign it but it didn't work
form
The code you have there doesn't really make sense. <div />s do not have a value attribute, and the expression in a th:onclick must be valid javascript (instead you have a blank selection variable expressions: th:onclick="*{}"). Maybe you're looking for something like this?
<form class="container" th:action="#{/processSignup}" method="post" th:object="${user}">
<input type="hidden" th:field="*{userRole}" id="userRole" />
<div class="switch">
<div class="MenteeSignUp" onclick="document.getElementById('userRole').value = 'MENTEE';">Mentee</div>
<div class="MentorSignUp" onclick="document.getElementById('userRole').value = 'MENTOR';">Mentor</div>
</div>

CSS selector path for text box that comes immediately after h3 with text "Company Name"

Below is my html
<div class="right" data-bindattr-13="13">
<h3>Company Name</h3>
<div class="input-row">
<input id="ember4258" class="ember-view ember-text-field" type="text"/>
</div>
<h3>Full Company Legal Name</h3>
<div class="input-row">
<input id="ember4259" class="ember-view ember-text-field" type="text"/>
</div>
<h3>Company Phone</h3>
<div class="input-row">
<input id="ember4260" class="ember-view ember-text-field" type="text"/>
</div>
<h3>Federal Tax / Employer ID (EIN)</h3>
<div class="input-row">
<input id="ember4261" class="ember-view ember-text-field" type="text"/>
</div>
</div>
Since class value off all the text fields and respective parent div class attributes are same I need to fill these text fields without using nth-of-type.
I have done work around to create a CSS selector that should point the text box that is immediately after <h3>Full Company Legal Name</h3> .
h3:contains(^Company Name$)+div>input
But my Capybara script is not recognizing the above way and throwing the below error.
annotateInvalidSelectorError_': The given selector h3:contains(^Company Name$)+div>input is either invalid or does not result in a WebElement. The following error occurred: (Selenium::WebDriver::Error::InvalidSelectorError)
InvalidSelectorError: An invalid or illegal selector was specified
Can any one provide a CSS that matches my requirement?
Regards,
Avinash Duggirala
The contains pseudo class was deprecated, which is why the InvalidSelectorError occurs. There is currently no way to check text nodes using CSS-selectors.
Instead, you can use XPath to traverse the DOM to sibling elements.
text_field = page.find(:xpath, '//h3[text() = "Company Name"]/following-sibling::div[1]/input')
text_field.set('some value')

Magento - newsletter pop code using ajax update in form

I have the below code that is being used in cms page pop up
the problem im having is if you leave email address input field blank and hit the submit (in the case "Get Code" button, you briefly see the red validation message but then hides the form, shows the hidden div.
why or how can I prevent this process, i want it to validate but proceed to show the hidden div content. Below is the code.
<div id="pop-confirm" style="display: none;">
<h1 style="color: #000000; font-weight: bold;">THANKS!</h1>
<p> </p>
<h2>Use code: <strong>extra15</strong><br /> for 15% off your purchase.</h2>
</div>
<form id="newsletter-validate-detail" action="home/send" method="post" onSubmit="new Ajax.Updater({success:'newsletter-validate-detail'}, 'newsletter/subscriber/new', {asynchronous:true, evalScripts:false, onComplete:function(request, json){Element.hide('newsletter-validate-detail');Element.show('pop-confirm');}, onLoading:function(request, json){}, parameters:Form.serialize(this)}); return false;">
<div class="block-content">
<h2>ENTER YOUR EMAIL<br /> AND GET</h2>
<div class="form-subscribe-header">
<h1>15% OFF</h1>
<br />
<h1>EVERYTHING!</h1>
</div>
<div class="input-box"><input id="newsletter" class="input-text required-entry validate-email" title="Sign up for our newsletter" type="text" name="email" /></div>
<br />
<div class="actions"><button class="button" title="Get Code" type="submit"><span><span>Get Code</span></span></button></div>
<br />
<p><em>* Promotion ends April 6th</em></p>
</div>
</form>
<script type="text/javascript">// <![CDATA[
var newsletterSubscriberFormDetail = new VarienForm('newsletter-validate-detail');
// ]]></script>
Im very new to ajax so maybe ive done something wrong.
any ideas or suggestion would be greatly appreciated.
Thanks
Try changing your onSubmit to the following:
onSubmit="if(newsletterSubscriberFormDetail.validator.validate()){ new Ajax.Updater({success:'newsletter-validate-detail'}, 'newsletter/subscriber/new', {asynchronous:true, evalScripts:false, onComplete:function(request, json){Element.hide('newsletter-validate-detail');Element.show('pop-confirm');}, onLoading:function(request, json){}, parameters:Form.serialize(this)}); } return false;"`
This wil trigger the validator before submitting the form with AJAX. Works perfect for me :)

How to write XPATH for nodes under dynamic hierarchy

I got a big XML. A snippet of that XML look like this:
<div class="x-column-inner" id="ext-gen422" style="width: 850px;">
<div id="ext-comp-1206" style="width: 14px;" class=" x-column">
<div tabindex="-1" class="x-form-item x-hide-label" id="ext-gen434">
<label class="x-form-item-label" style="width:100px;" for="ext-comp-1180" id="ext-gen435"></label>
<div style="padding-left:105px" id="x-form-el-ext-comp-1180" class="x-form-element">
<div class="x-form-check-wrap" id="ext-gen436" style="width: 14px; height: 28px;">
<input type="checkbox" name="ext-comp-1180" id="ext-comp-1180" autocomplete="off" class=" x-form-checkbox x-form-field">
<label class="x-form-cb-label" for="ext-comp-1180" id="ext-gen437"> </label>
</div></div> <div class="x-form-clear-left">
</div>
</div>
</div>
<div id="ext-comp-1207" style="width: 150px;" class=" x-column">
<label id="ext-comp-1203" style="width: 140px;">Add to Watchlist</label>
</div>
<div id="ext-comp-1208" style="width: 107px;" class=" x-column">
I need to find 'input' node of checkbox type based on label node having text 'Add to Watchlist'.
As both 'input' and 'label' node lies in different hierarchy, // syntax doesn't seem to work:
//div[label[contains(text(),'Add to Watchlist')]]
will just give parent div of child label.
I tried to start from the topmost node of this snippet
$x("//div[#class='x-column-inner' and //label[contains(text(),'Add to Watchlist')]]")
but that is giving 6 possible matches.
Note: #id attribute can't be used as this is getting assigned dynamically to nodes so next time page loads #id will be different.
I don't want to use position() predicate as that makes XPATH static and xpath may break with any change in position.
You could try something like this, but it looks very greedy... Basically what it does is searching in every axes of the input tags to see if there is an associated label tag. So for each input it searches in its ancestors, descendants and siblings.
There are certainly some smarter solutions.
//input[#type = 'checkbox' and (#id = ancestor::label/#for or #id = descendant::label/#for or #id = following::label/#for or #id = preceding::label/#for)]
However your snippet is not interesting no input tag will be matched, please consider providing a better snippet. It would improve the answers accuracy.
Edit : Here is a (non-tested) way to add the 'Add to Watchlist' constraint.
//input[#type = 'checkbox' and (#id = ancestor::label[. = 'Add to Watchlist']/#for or #id = descendant::label[. = 'Add to Watchlist']/#for or #id = following::label[. = 'Add to Watchlist']/#for or #id = preceding::label[. = 'Add to Watchlist']/#for)]
But once again, those xpath requests are very greedy and your are not guaranteed to match every input element associated to a label for example the following input won't be match in this snippet:
<div>
<div>
<label for="id">Add to Watchlist</label>
</div>
<div>
<input type="checkbox" id="id" />
</div>
<div>
There may be more efficient solutions in one xpath request, but you should consider doing several request.
For example, one request to find every for attribute value of the label elements with the text 'Add to Watchlist' and then doing another request to find the associated input elements.
I should also try to restrict your request to the scope a the underlying form element. Perhaps I will edit with a better request if I find the time.
Edit 2
Here is a working and smarter request
//form//input[#type = 'checkbox' and #id = ancestor::form[1]//label[. = 'Add to Watchlist']/#for]
You can confront it to this snippet
<html>
<form>
<label for="bar">Add to Watchlist</label>
<div>
<div>
<label for="id">Add to Watchlist</label>
</div>
<div>
<input type="checkbox" id="id" />
<input type="checkbox" id="foo" />
<input type="checkbox" id="bar" />
<input type="checkbox" />
<input type="checkbox" id="" />
</div>
</div>
</form>
<label for="foo">Add to Watchlist</label>
</html>
Bust the most important is that you understand how it works and why it is better. Please take the time to think about it.

Form Submit using a Javascript to invoke webflow transition, doesn't take the updated value on form

I am trying to invoke a form submit using javascript (jquery) to invoke a webflow transition. It works and the submit invokes the desired transition. But, the updated radio button values is not reflected on the model object which is posted.
Here is the code:
<form:form method="post" action="#" commandName="infoModel" name="pageForm">
<form:input type="input" path="testMsg" id="success" />
<input type="button" id="clearSelections" value="Clear Selections">
<div class="question">
<h4><c:out value="${infoModel.questionInfo.description}"/> </h4>
<form:radiobuttons path="infoModel.answerId"
itemValue="answerId" itemLabel="answerDescription" items="${infoModel.answers}" delimiter="<br/>" />
</div>
<input type="submit" name="_eventId_saveQualitativeInput" value="Save" id="save" />
$(document).ready(function() {
$('#tabs').tabs();
//Clear selections (copy is server-side)
$('#clearSelections').click(function() {
//$('input[type="radio"]').prop('checked', false);
$('input[type="radio"]').removeAttr('checked');
$('#save').trigger('click');
});
});
</form:form>
The form:radiobutton, generates the below html:
<div class="question">
<h4>Is this a general obligation of the entity representing a full faith and credit pledge? </h4>
<span>
<input type="radio" checked="checked" value="273" name="infoModel.answerId" id="infoModel.answerId1">
<label for="infoModel.answerId1">Yes</label>
</span>
<span><br>
<input type="radio" value="274" name="infoModel.answerId" id="infoModel.answerId2">
<label for="infoModel.answerId2">No</label>
</span>
<br>
<span class="error"></span>
</div>
The input id= "success" value is registered and when the control goes to the server, the value of input id= "success" is updated in the "infoModel" object. But the value of answerId is not updated on the "infoModel" object.
Thoughts if i am missing something in the form:radiobutton element or if there is something else wrong?
Thanks in advance!
EDIT:::::::
Thanks mico! that makes sense. I stripped of some of the code first time to make it precise, but i have a list which is being used for building the radio-buttons, below is the code:
<c:forEach items="${infoModel.list["index"]}" var="qa" varStatus="rowCount">
<div class="question">
<h4><c:out value="${question.questionInfo.description}"/> </h4>
<form:radiobuttons path="list["index"][${rowCount.index}].answerId" itemValue="answerId" itemLabel="answerDescription" items="${question.answers}" delimiter="<br/>" />
<br>
</div>
</c:forEach>
Could you please suggest how i could try this one out?
NOTE: The same code works on a regular form submit on click of a button of type submit. Its the javascript form submit which is not working. I also tried to do whatever i want to do in javascript and then invoke the button.trigger('click'); form got submitted but the changes made on form in my javascript didnt reflect.
With commandName inside a form:form tag you set "Name of the model attribute under which the form object is exposed" (see Spring Documentation). Then in path you should tell the continuation of the path inside the model attribute.
With this said I would only drop the extra word infoModel from path="infoModel.answerId" and have it rewritten as path="answerId" there under the form:radiobutton.

Resources