playwright-test: test that input is required - playwright

I have an input for a login form that is required:
<input
autoComplete="email"
defaultValue={email}
disabled={state === 'submitting'}
id="email"
name="email"
placeholder={t('user-authentication:email-placeholder')}
required // HERE!
type="email"
/>
How can I test that the input is required with Playwright? My native browser warning in Chromium is: "Please fill in this field."
Is there a way to check / assert for this warning in Playwright? Or another way to test that the input is required?
I tried writing nothing in the input with .fill('') and then pressing enter with page.keyboard.press('Enter'), but those did not cause the warning to show up.

If the message is displayed by the browser and not something added to the DOM, you would struggle to test it directly in Playwright.
You could check the the required attribute is present, and trust that the browser is going to display the warning.
await page.locator('input#email[required]')
There's also a Constraint validation you could apply
If the element is required and the element's value is the empty string, then the element is suffering from valueMissing and the element will match the :invalid pseudo class.
await page.locator('input#email[required]:invalid')

Related

Playwright: Two elements with the same name, how to fill the one that is visible?

I have an Ionic React app that uses react-router 5 to display various pages.
The app defaults to a login form, but if users go to the support page, there is a contact form.
I'm trying to test the contact form in Playwright with code like this:
await page.fill('input[name=mail]', 'playwright#example.com');
However, I'm getting an error:
=========================== logs ===========================
waiting for selector "input[name=mail]"
selector resolved to 2 elements. Proceeding with the first one.
selector resolved to hidden <input name="mail" type="email" placeholder="" autocorr…/>
elementHandle.fill("playwright#example.com")
waiting for element to be visible, enabled and editable
element is not visible - waiting...
The problem is that Playwright is seeing the first email address input on the login form, which is not visible, and then trying to fill that in instead of the visible email address input on the contact page.
Obviously, I never want to fill in an element that isn't visible, so how can I force playwright to only try to fill in visible elements?
You can use page.locator to check for visibility first and then fill in the value.
await page.locator('input[name=mail] >> visible=true').fill('playwright#example.com');
To make tests easier to write, wrap this in a helper function:
async fillFormElement(
inputType: 'input' | 'textarea',
name: string,
value: string,
) {
await this.page
.locator(`${inputType}[name=${name}] >> visible=true`)
.fill(value);
}
It can be done in this way, if you are trying to check is element visible. Even you have two same elements with one selector one hidden one visible.
const element = await page.waitForSelector(selector, {state:visible});
await element.fill(yourString);

Testing a vuetify on rails project with capybara selenium

I often use this site but it had never happened to me to ask a question. Now I am blocked and so it is time to ask the first one.
I need to test a sign up form created with vue 2 and vuetify, server side rendered with ruby on rails, webpack 5.
I configured capybara with selenium chrome headless driver, it works when it comes to interacting with text fields and buttons but when I try to check the checkbox:
(byebug) check('Accept')
*** Capybara::ElementNotFound Exception: Unable to find visible checkbox "Accept" that is not disabled
Vuetify hides the input and replaces it with beautiful div but, what is the best approach to check a v-checkbox?
Signup form
If I add the visible attribute, the input is found but nothing happens. I think I need to interact with some other element?
(byebug) check('Accept', visible: false)
#<Capybara::Node::Element tag="input" path="/HTML/BODY[1]/DIV[1]/DIV[1]/DIV[1]/MAIN[1]/DIV[1]/DIV[2]/DIV[1]/DIV[1]/DIV[1]/FORM[1]/DIV[2]/DIV[2]/DIV[1]/DIV[1]/DIV[1]/INPUT[1]">
I also tried this but still nothing happen:
(byebug) page.find('input[type=checkbox]', visible: false).set(true)
#<Capybara::Node::Element tag="input" path="/HTML/BODY[1]/DIV[1]/DIV[1]/DIV[1]/MAIN[1]/DIV[1]/DIV[2]/DIV[1]/DIV[1]/DIV[1]/FORM[1]/DIV[2]/DIV[2]/DIV[1]/DIV[1]/DIV[1]/INPUT[1]">
So I also tried the click way but getting this error:
(byebug) page.find('input[type=checkbox]', visible: false).click
*** Selenium::WebDriver::Error::ElementClickInterceptedError Exception: element click intercepted: Element <input aria-checked="false" id="input-96" role="checkbox" type="checkbox" value=""> is not clickable at point (234, 531). Other element would receive the click: <div class="v-input--selection-controls__ripple"></div>
(Session info: headless chrome=85.0.4183.121)
I tried also executing the raw script:
page.execute_script("window.uiApp.$data.terms_and_conditions = true")
The vue app is mounted in this way:
window.uiApp = new Vue({
i18n,
vuetify,
store,
router,
el: id,
render: h => h(App, {
props
})
})
But window.uiApp.$data is empty, so this attempt also seems to fail :( How to access vue component data (without vue web tool)?
I don't know what else to try, thanks in advance
Looking at the HTML shown in your linked image (in the future when asking questions it would be helpful if you included the relevant HTML directly in your question) it looks like you have a label associated with the hidden checkbox that the user can click. In that case you can use
check('Accept', allow_label_click: true)
which, when the actual checkbox is hidden, will click on the associated label instead. If you want that behavior to be on by default you can set Capybara.automatic_label_click = true.
Your other option is to determine exactly which element is actually being shown as the 'checkbox' and use find(...).click to locate that element and click on it.
I changed the checkbox in this way:
<v-checkbox v-model="terms_and_conditions"
#input='$v.terms_and_conditions.$touch()'
#blur='$v.terms_and_conditions.$touch()'
:label="$t('commons.accept')">
</v-checkbox>
<div class="ml-2">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<a
target="_blank"
href="/users/terms_and_conditions"
#click.stop
v-on="on"
>
{{ $t('session.sign_up.terms') }}
</a>
</template>
{{ $t('session.sign_up.terms_hint') }}
</v-tooltip>
</div>
Thank you

How to listen on an on-input-valid paper-input property

Please consider the following snippet:
<paper-input floatinglabel required
id="title"
class=''
label="Title"
value='{{name.title}}'
validate='^[A-Za-z'\s-]*$'
error="Only letters are allowed"
on-input-valid='{{validInputHandler}}'>
</paper-input>
void validInputHandler() {
print( 'valid input' );
}
Although the input is valid for a letter, the validInputHandler is not being triggered.
Reading the docs here http://www.polymer-project.org/docs/elements/core-elements.html#core-input
suggest I am on the correct path but the validInputHandler does not prints anything.
It seems the input-valid event was added later and is not yet contained in Dart core-elements (paper-input extends core-input).
Currently only input-invalid is supported in Dart.
See also Taking total control of PaperInput validation for an example about custom validation.

How to reset input value if a condition fails Primefaces3.5

I have a problem. I am trying to save a information into the database, and before that I check some conditions like if-else like:
if(condition){
//Some Action
}else{
getFacesContext().addMessage(null,MessageFactory.getMessage(ResourceBundle.FACES_BUNDLE.getName(), FacesBundle.LANDLINE_NUMBER_SHOULD_BE_TEN.getName()));
getExternalContext().getFlash().setKeepMessages(true);
return;
}
and When the condition does not match I would like to restore the previous values of the input field.
In that case, it was 22 in place of ss. but the field does not take input except numbers. So the validation fails and shows an message Invalid Input via growl.
How can I also reset the value of the field ss to 22 in java?
Please suggest!
You can use default validator that comes with JSF
<p:inputText id="number" value="" label="Number">
<f:validateDoubleRange minimum="0" maximum="99" />
</p:inputText>
<p:message for="number" />
But if you reset the filed with old value, user may not know what he's entered. However you will not submit until validation success.
Since the input text component implements EditableValueHolder you can call EditableValueHolder.resetValue() on the input component (or call the more convenient RequestContext.getCurrentInstance().reset(inputTextId)) and then re-render it by calling RequestContext.getCurrentInstance().update(inputTextId) if it isn't already being re-rendered through the update attribute of p:ajax.

Grails: checkbox not being set back to false

I am developing a Grails (1.0.4) app where I want to edit a collection of collections on a single page in a grid view. I got it to work quite well depending only on the indexed parameter handling of Spring MVC, except for one thing:
boolean (or, for that matter, Boolean) values in the grid can be set via checkbox, but not unset, i.e. when I check the checkbox and update, the value is set to true, but afterwards when I edit again, uncheck the checkbox and update, it remains true.
This is the GSP code of the checkbox:
<g:checkBox name="tage[${indexTag}].zuweisungen[${indexMitarb}].fixiert" value="${z.fixiert}" />
And this is the HTML that is generated:
<input type="hidden" name="tage[0].zuweisungen[0]._fixiert" />
<input type="checkbox" name="tage[0].zuweisungen[0].fixiert" checked="checked" id="tage[0].zuweisungen[0].fixiert" />
I've found a Grails bug that describes exactly this effect, but it's marked as fixed in 1.0.2, and the problem mechanism described there (underscore in hidden field name is put in the wrong place) is not present in my case.
Any ideas what could be the reason?
This is the solution a guy named Julius Huang proposed on the grails-user mailing list. It's reusable but relies on JavaScript to populate a hidden field with the "false" response for an unchecked checkbox that HTML unfortunately does not send.
I hack GSP to send "false" when
uncheck the box (true -> false) with
custom TagLib.
By default checkBox send nothing when
uncheck, so I use the checkBox as
event handler but send hidden field
instead.
"params" in Controller can handle
"false" -> "true" without any
modification. eg. Everything remain
same in Controller.
The Custom Tag Usage in GSP (sample usedfunc_F is "true"),
<jh:checkBox name="surveyList[${i}].usedfunc_F" value="${survey.usedfunc_F}"></jh:checkBox>
Here is what the Tag generate,
<input type="hidden" name="surveyList[#{i}].usedfunc_F" id="surveyList[#{i}].usedfunc_F" value="false" />
<input type="checkbox" onclick="jhtoggle('surveyList[#{i}].usedfunc_F')" checked="checked" />
The Javascript
<script type="text/javascript">
function jhtoggle(obj) {
var jht = document.getElementById(obj);
jht.value = (jht.value !='true' ? 'true' : 'false');
}
</script>
This is my own solution, basically a workaround that manually does what the grails data binding should be doing (but doesn't):
Map<String,String> checkboxes = params.findAll{def i = it.key.endsWith("._fixiert")} // all checkboxes
checkboxes.each{
String key = it.key.substring(0, it.key.indexOf("._fixiert"))
int tagIdx = Integer.parseInt(key.substring(key.indexOf('[')+1, key.indexOf(']')))
int zuwIdx = Integer.parseInt(key.substring(key.lastIndexOf('[')+1, key.lastIndexOf(']')))
if(params.get(key+".fixiert"))
{
dienstplanInstance.tage[tagIdx].zuweisungen[zuwIdx].fixiert = true
}
else
{
dienstplanInstance.tage[tagIdx].zuweisungen[zuwIdx].fixiert = false
}
}
Works, requires no change in grails itself, but isn't reusable (probably could be made so with some extra work).
I think that the simplest workaround would be to attach a debugger and see why Grails is failing to populate the value. Considering Grails is open source you'll be able to access the source code and once you figure out the solution for it you can patch your version.
I have also found this other bug GRAILS-2861 which mentions the issue related to binding to booleans (see Marc's comment in the thread). I guess that is exactly the problem you are describing.
I would create a small sample app that demonstrates the problem and attach it to the Grails bug (or create a new one). Someone here may be able to debug your sample app or you'll have shown the bug isn't really fixed.
Try this out, set the logs to DEBUG, frist try the first 3 if they don't show the problem up, flip them all to DEBUG:
codehaus.groovy.grails.web.servlet="error" // controllers
codehaus.groovy.grails.web.pages="error" // GSP
codehaus.groovy.grails.web.sitemesh="error" // layouts
codehaus.groovy.grails."web.mapping.filter"="error" // URL mapping
codehaus.groovy.grails."web.mapping"="error" // URL mapping
codehaus.groovy.grails.commons="info" // core / classloading
codehaus.groovy.grails.plugins="error" // plugins
codehaus.groovy.grails.orm.hibernate="error" // hibernate integration
This should allow you to see exactly when and how the parameters setting is failing and probably figure out a work around.

Resources