I'm using codeception to write acceptance tests for a laravel application. The application is a simple CRUD app, and my tests just test filling out and submitting the application's various forms. I have a working test which creates a new resource but then when I try to edit that same resource I get the following error:
Here's what the (working) test looks like for creating the resource:
$I = new AcceptanceTester($scenario);
$I->wantTo('Create Additional');
$I->login($I, 'username', 'password');
$I->click('Additional');
$I->seeInCurrentUrl('/admin/additional');
$I->click('Add Additional');
$I->seeInCurrentUrl('admin/additional/create');
$I->see('Add Additional');
//Fill out form fields
$I->fillField('name', 'Test Name');
$I->fillField('anchor', 'Additional Services in Cusco');
$I->selectOption('publish', '1');
$I->click('Save');
$I->click('Additional');
$I->see('Additional Test Name');
$I->cleanup($I);
The test for editing the resource is almost identical:
$I = new AcceptanceTester($scenario);
$I->wantTo('Edit Additional');
$I->login($I, 'username', 'password');
$I->click('Additional');
$I->seeInCurrentUrl('/admin/additional');
$I->click('Edit');
$I->seeInCurrentUrl('admin/additional/edit/1');
$I->see('Editing');
// Fill out form fields
$I->fillField('name', 'Additional Name Edited');
$I->fillField('anchor', 'Edited anchor');
$I->selectOption('publish', '1');
$I->click('Save');
$I->click('Additional');
$I->see('Additional Name Edited');
$I->cleanup($I);
Yet when I run the edit test I get the following error:
1) Failed to edit additional in AdditionalEditCept
Couldn't fill field "name","Additional Name Edited":
InvalidArgumentException: Unreachable field "name"
That seems straightforward enough--the "name" field must not be there. But when I look at the source name="name" is there just like it is for the additional/create view. I should point out that codeception didn't detect the field with
$I->see('name');
either. Here's what the relevant code looks like in AdditionalEditCept.fail.html
<div class="form-group">
<label for="name">Additional Name:</label>
<input class="form-control" placeholder="" autocomplete="off" name="name" type="text" value="Edited Additional Services in Cusco" id="name">
</div>
It seems really simple; codeception needs to find html elements by CSS selectors, I give them a CSS selector. What could be going on here?
A bit late on this but I've had a similar error if my html isn't valid eg
<div class="form-group">
<label for="name">Additional Name:</label>
<input name="name" type="text">
</div>
</div>
Related
I dont know what I am doing wrong. I have some fields with images, I want to do an update to the database only if there is an image. I have tried setting to nullable but I keep on getting error the photo must be of type image.
This is my code in Livewire class:
$this->validate([
'photo1'=>'sometimes|image',
'photo2'=>'nullable|image',
'photo3'=>'nullable|image'
]);
In the blade
<div class="col-md-6">
<label>Front Right</label>
<input type="file" wire:model="photo1" accept="image/*">
<span class="text-danger">#error('photo1'){{ $message }}#enderror</span>
</div>
<div class="col-md-6">
<label>Flont Left</label>
<input type="file" wire:model="photo2" accept="image/*">
<span class="text-danger">#error('photo2'){{ $message }}#enderror</span>
</div>
</div>
Probably it's bit late for it, but I had the same issue and this is the solution:
If you are using updated instead of updating it will work. The problem when using updating is that the property does not have the file assigned, so it's still null during validation.
Let's say your are using updating() and your property name is $logo:
You would need to assign the $value to the property ($logo) before validation, e.g.:
function updating($key,$value) {
$this-logo = $value; // This line you need to add to make it work
$this-validateOnly($key);
}
I do simply use updated() instead.
Have you added enctype="multipart/form-data" in form element?
Add mimes in validation: 'photo2'=>'nullable|image|mimes:jpg,jpeg,png,svg,gif' and then check if it works.
I'm using Livewire to build my Laravel project, and I'm also implementing the security/authentication layer with Fortify.
For various reasons though, I'm not using Jetstream. With that said, there are clearly some very useful features and techniques which Jetstream employs to do its stuff, and so in the true spirit of creativity, I've "borrowed" the following "update-password" form as an example of how Jetstream uses Livewire to interface with Fortify.
This HTML template is a simplified version of the actual HTML code that goes into building my form, but I've taken out all the styling and labelling complexities etc. so that we can focus only on what's important:
<!-- update-password-form.blade.php (Livewire Template) -->
<form wire:submit.prevent="updatePassword">
<input id="current_password" type="password" name="current_password" required="true" />
<input id="password" type="password" name="password" required="true" />
<input id="password_confirmation" type="password" name="password_confirmation" required="true" />
<button type="submit">Change Password</button>
</form>
As we see, Livewire prevents the default submission of the form, and redirects it to the function updatePassword() in the Livewire class. The function as you can see here looks like this:
/**
* Update the user's password.
*
* #param \Laravel\Fortify\Contracts\UpdatesUserPasswords $updater
* #return void
*/
public function updatePassword(UpdatesUserPasswords $updater)
{
$this->resetErrorBag();
$updater->update(Auth::user(), $this->state);
$this->state = [
'current_password' => '',
'password' => '',
'password_confirmation' => '',
];
$this->emit('saved');
}
This all appears to work fine-ish. When I (the user) press [Change Password] Livewire sets the form inactive (to prevent the user from double-submitting the form), and when the response from Laravel/Livewire is received, the form is enabled again. And... well that's it.
The problem is that it doesn't matter what data I submit. If I enter all correct values, the password doesn't update! If I enter incorrect current_password, it doesn't error. If I submit the correct current_password with mismatching password and password_confirmation, I get the same "nothing happened" experience (as an end user). When I check the "network" tab on the browser's dev-tools, I get valid 200 responses each time without any obvious errors being reported in the detail. I know the PHP function is being invoked because if I put a dd($this) style debug in it, the JSON response throws back the Livewire object.
So the question I therefore have is the following...
How does the Fortify framework manage errors, and how am I supposed to catch them (in Livewire) to give the user some useful feedback?
Does the ErrorBag (which is reset in the first line of code) somehow get populated in the $updater->update() function?
Moreover, I copied (sorry, borrowed) this solution from Jetstream project. Does this also mean that the Jetstream interface is equally as unintuitive (from an end-user point of view)? Is there some higher-level concept that I've missed from the Jetstream project?
I was being stupid. The error bag is returned to the view, there just wasn't an outlet in the template to display the response. All I needed was a conditional label (or a span or a div) to display the field if the error exists.
<form wire:submit.prevent="updatePassword">
<input id="current-password" type="password" name="current_password" required="true" />
#error('current_password')
<label id="current-password-err" class="error" for="current-password">{{ $message }}</label>
#enderror
<input id="password" type="password" name="password" required="true" />
#error('password')
<label id="password-err" class="error" for="password">{{ $message }}</label>
#enderror
<input id="password-confirmation" type="password" name="password_confirmation" required="true" />
#error('password_confirmation')
<label id="password-confirmation-err" class="error" for="password-confirmation">{{ $message }}</label>
#enderror
<button type="submit">Change Password</button>
</form>
According to twitter bootstrap, this is how we do a radio:
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
Option one is this and that—be sure to include why it's great
</label>
</div>
And this is my code:
$browser->click('#menu-reports')
->waitForText('Users')
->click('#menu-reports-users')
->radio('sitesActive', '2')
->radio('includeDisabled', '2')
->radio('includeNonCertifiable', '2')
->press('Apply')
->waitForText('Showing 0 to 0 of 0 entries')
;
With the input inside the label tag. But the problem is that Dusk (actually Facebook Webdriver) is not able to find it this way. It keeps raising:
Facebook\WebDriver\Exception\ElementNotVisibleException: element not visible
To make it work I have put the input outside the label, but then, of course, the boostrap radio does not show as it should anymore.
<div class="radio">
<input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
<label>
Option one is this and that—be sure to include why it's great
</label>
</div>
Does not work using IDs either:
Not even setting an ID to the input:
<input
type="radio"
name="sitesActive"
id="sitesActive3"
value="2"
>
And trying to select it this way:
->radio('#sitesActive3', '2')
The problem is that Dusk (Webdriver) cannot even see the element in the page, as this simple like fails the exact same way:
$browser->waitFor('#sitesActive3');
Resulting in:
Facebook\WebDriver\Exception\TimeOutException: Waited 5 seconds for selector [#sitesActive3].
And that happens every time I have a form with an input with a label surrounding it, if I take the input out of the label, it works. But that's not as simple with radios, as it was with some other inputs, radios.
This is a properly coded radio:
This is a radio with the input outside the label tag:
So, how are you doing this?
My form has a radio button. This how I checked it.
userCreate.blade.php
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Gender</label>
<div>
<label class="radio-inline">
<input type="radio" id="gender" name="gender" value="m">Male
</label>
<label class="radio-inline">
<input type="radio" id="gender" name="gender" value="f">Female
</label>
</div>
</div>
</div>
</div>
CreateUserTest.php
class CreateUserTest extends DuskTestCase
{
public function testCreateForm()
{
$this->browse(function (Browser $browser) {
$browser->visit('/user/create')
->radio('#gender', 'm')
->click('button[type="submit"]')
->assertSee('Successfully created user.');
});
}
}
This works for me. I think this will help you.
simplest way is to click on the parent
$el = $this->resolver->resolveForRadioSelection($field, $value);
$el = $el->findElement(WebDriverBy::xpath(".."));
$el->click();
Since the radio is not visible dusk cannot click on it
You may create a trait like the following if you are using bootstrap in your project
trait BootstrapInteraction
{
/**
* Undocumented variable
* #var \Laravel\Dusk\ElementResolver $resolver
*/
public $resolver;
public function radioB($field, $value)
{
/**
* #var RemoteWebElement $el
*/
$radio = $this->resolver->resolveForRadioSelection($field, $value);
// click on parent
$el = $radio->findElement(WebDriverBy::xpath(".."));
$el->click();
// if not selected click on label
if (!$radio->isSelected()) {
$el = $el->findElement(WebDriverBy::cssSelector("label"));
$el->click();
}
PHPUnit::assertTrue(
$radio->isSelected(),
"Not able to select Radio [{$field}] within value [{$value}]."
);
return $this;
}
You may not be happy to edit your views for the sake of your test script but if you are open to that, what about adding a class to the
<input type="radio" ... >
and then using
->click('.yourClass')
in your Dusk test?
The Dusk docs say:
To "select" a radio button option, you may use the radio method. Like many other input related methods, a full CSS selector is not required. If an exact selector match can't be found, Dusk will search for a radio with matching name and value attributes: $browser->radio('version', 'php7');
In my case, Dusk was working fine for most of my radio buttons, but it would not work for:
->radio('Field728', 'Know it\'s what anyone committed to this dream would do if she found a great program that features a proven process and supportive community')
I also tried using double-quotes, but that didn't work either. (Maybe the value is too long? I don't know.)
So instead I did this:
->radio('#Field728_0', true)//this is the specific ID of the first radio button of this group
I'm building the website based on Joomla 3. I need to disable the validation of the fields: name, username, password1 (the second, because the first is password2) and email2 and use email1 as username (I installed the plugin Email for user authorization).
I have tried to remove these fields in file components/com_users/models/forms/registration.xml but the validation is still remaining. If I don't remove them but only change the rows required="true" to false for these fields the registration doesn't work at all and any user stored in the DB. How can I disable these fields?
It's not an easy workaround, and you will need some basic knowledge of Joomla and PHP, but I'll try to explain it to you as simple as i can.
>>> Creating view template override
First of all you will need to create your Registration view template override (to keep it Joomla update proof). To do so, create folder /templates/YOUT_TEMPLATE/html/com_users/registration and copy /components/com_users/views/registration/tmpl/default.php file there.
From now on you can modify registration output in your template folder.
>>> Modifying registration form output
By default Joomla takes all fields from form file /components/com_users/models/forms/registration.xml, where they are defined, and outputs them in a view. But if we don't want to use ALL the fields, we need to output fields manually.
My example code only output E-mail and Password fields for registration. Here's a sample code to do so: (default.php file)
<?php
defined('_JEXEC') or die;
JHtml::_('behavior.keepalive');
?>
<div class="grid_8" id="register_block">
<div class="content_block">
<h1>Registracija</h1>
<div class="login<?php echo $this->pageclass_sfx?>">
<form id="member-registration" action="<?php echo JRoute::_('index.php?option=com_users&task=registration2.register'); ?>" method="post" enctype="multipart/form-data">
<div>
<div class="login-fields">
<label id="jform_email1-lbl" for="jform_email1">E-mail:</label>
<input type="text" name="jform[email1]" id="jform_email1" value="" size="30">
</div>
<div class="login-fields">
<label id="jform_password1-lbl" for="jform_password1">Password:</label>
<input type="password" name="jform[password1]" id="jform_password1" value="" autocomplete="off" size="30">
</div>
<button type="submit" class="button"><?php echo JText::_('JREGISTER');?></button>
<input type="hidden" name="option" value="com_users" />
<input type="hidden" name="task" value="registration2.register" />
<?php echo JHtml::_('form.token');?>
</div>
</form>
</div>
</div>
</div>
Please note, that I've also replaced task value from registration.register to registration2.register, I did this to bypass some of validation rules using my own controller.
>>> Creating controller override
Locate file /components/com_users/controllers/registration.php and create a copy of it called registration2.php in same folder.
Open file registration2.php and change It's class name from UsersControllerRegistration to UsersControllerRegistration2
From now on Joomla registration form will use this class to create a new user.
Find a method called register and find this line:
$requestData = JRequest::getVar('jform', array(), 'post', 'array');
This is where Joomla get's registration form data. Add the following lines:
$requestData['name'] = $requestData['email1'];
$requestData['username'] = $requestData['email1'];
$requestData['email2'] = $requestData['email1'];
$requestData['password2'] = $requestData['password1'];
It will add missing registration info, and help you pass validation.
NOTE: This is a sample code, to show the main logic. If anyone has a better solution, I'd be more than happy to hear it.
Following same idea, a simpler solution might be just including hidden inputs with values in com_users\views\registration\tmpl\default.php above
<button type="submit" class="btn btn-primary validate"><?php echo JText::_('JREGISTER');?></button>
add
<input type="hidden" id="jform[username]" name="jform[username]" value="username" />
<input type="hidden" id="jform_name" name="jform[name]" value="name" />
It would pass the validation and you would no longer need to override controllers etc.
We have a web application which has a list of reports, which I am trying to automate.
There is an option to view all these reports. When I open a report to view, there are some values I need to select to view it, which is where I am stuck.
The id used for these reports are generic. If the id for <label>Application<lable> is <select id="id1" name="id[1]" class="valid">, the same label in a different report will have a different id. How do I proceed from here?
Giving the tags of two sample reports:
First report:
<div>
<label id="PackageID">
Package ID <span class="required">*</span></label>
<input id="id_1__Name" name="id[1].Name" type="hidden" value="PackageID">
<select id="id_1__SelectedValues" name="id[1].SelectedValues" class="valid">
</div>
Second report:
<div>
<label id="PackageID">
Package ID <span class="required">*</span></label>
<input id="id_5__Name" name="id[5].Name" type="hidden" value="PackageID">
<select id="id_5__SelectedValues" name="id[5].SelectedValues" class="valid">
</div>
Not sure if this suggestion is suitable for your scenario, but you could get the id with jQuery and store it in a hidden field:
var id = $('.className').attr('id');
or use control's ClientID property.
Anyway, to select proper value you just need
#browser.select_list(:id, "id_5__SelectedValues").set("3")