Behat Pass a value from a test step - testing

I'm trying to make assertion that the random text entered in one field appears on next page (confirmation)
I do it like this
When I fill in "edit-title" with random value of length "8"
/**
* Fills in form field with specified id|name|label|value with random string
* Example: And I fill in "bwayne" with random value of length "length"
*
* #When /^(?:|I )fill in "(?P<field>(?:[^"]|\\")*)" with random value of length "(?P<length>(?:[^"]|\\")*)"$/
*/
public function fillFieldWithRandomValue($field, $length)
{
$field = $this->fixStepArgument($field);
$value = $this->generateRandomString($length);
$this->getSession()->getPage()->fillField($field, $value);
}
Than I want to make assertion - something like this:
Then I should see text matching "<RANDOM VALUE ENTERED IN THE PREVIOUS STEP>"
is it possible?
UPDATE:
But how would it look like with setters and getters if i want to use a generateRandomString method multiple times and then get the values of this methods one after another? DO I have to make variables and functions for every test step? like this:
When I fill in "x" with random value of length "8"
And I fill in "y" with random value of length "12"
And I go to other page
Then I should see text matching "VALUE ENTERED TO X"
And I should see text matching "VALUE ENTERED TO Y"

You can create a property and set it in the previous step. And use it in the next one, but assert it if it has value.
Also it would be nice and readable to define that property with proper visibility type
/**
* #var string
*/
private randomString;
/**
* Fills in form field with specified id|name|label|value with random string
* Example: And I fill in "bwayne" with random value of length "length"
*
* #When /^(?:|I )fill in "(?P<field>(?:[^"]|\\")*)" with random value of length "(?P<length>(?:[^"]|\\")*)"$/
*/
public function fillFieldWithRandomValue($field, $length)
{
$field = $this->fixStepArgument($field);
$this->randomString = $this->generateRandomString($length);
$this->getSession()->getPage()->fillField($field, $this->randomString);
}
/**
*
* #Then /^(?:|I )should see that page contains random generated text$/
*/
public function assertPageContainsRandomGeneratedText()
{
//Assertion from phpunit
$this->assertNotNull($this->randomString);
$this->assertPageContainsText($this->randomString);
}
NOTE: Depending on your behat setup - assertion from phpunit might not work.

Since you will will call the generateRandomString method in multiple places then you should also have a method for getting this value like getRandomString like setters and getters.
My recommendation would be to have a class with related methods that handle all the data and not saving in variable in every place you will use data, generate+save and read from the same place anywhere you need.
Tip: You could be more flexible about the step definition and have a default length for the random string in case one one not provided.
High level example:
class Data
{
public static $data = array();
public static function generateRandomString($length = null, $name = null)
{
if ($name = null) {
$name = 'random';
};
if ($length = null) {
$length = 8;
};
// generate string like $string =
return self::$data[$name] = $string;
}
public static function getString($name = null)
{
if ($name = null) {
$name = 'random';
};
// exception handling
if (array_key_exists($name, self::$data) === false) {
return null;
}
return self::$data[$name];
}
}
In context:
/**
* #Then /^I fill in "x" with random value as (.*?)( and length (\d+))?$/
*/
public function iFillInWithRandomValue($selector, $name, $length = null){
$string = Data::generateRandomString($length, $name);
// fill method
}
/**
* #Then /^I should see text matching "first name"$/
*/
public function iShouldSeeTextMatching($variableName){
$string = Data::getString($variableName);
// assert/check method
}
This is high level example, you might need to do some adjustments.
If you have the validation in the same class then you can also have all these in the same class, meaning generateRandomString and getString in the same class with the steps.

Related

With Behat/Mink, how to match an exact number?

I'm trying to match an exact number for a page element with Behat/Mink.
My test looks like this:
Then the "td.points" element should contain "1"
This matches if td.points is 1 (good), but it also matches if td.points is 10 or 21 (bad).
I tried using a regex like this:
Then the "td.views-field-field-int-repetitions" element should contain "\b1\b"
But the regex wasn't picked up.
I tried to dig through the code and I see that MinkContext has assertElementContains, but I couldn't find anything like AssertElementIs.
What I want is something like
Then the "td.points" element should be exactly "1"
How can I implement this?
EDIT: This is the element contains feature from MinkContext.php:
/**
* Checks, that element with specified CSS contains specified HTML
* Example: Then the "body" element should contain "style=\"color:black;\""
* Example: And the "body" element should contain "style=\"color:black;\""
*
* #Then /^the "(?P<element>[^"]*)" element should contain "(?P<value>(?:[^"]|\\")*)"$/
*/
public function assertElementContains($element, $value)
{
$this->assertSession()->elementContains('css', $element, $this->fixStepArgument($value));
}
For extracting the number from the step you could use for a number:
the "(.*)" element should contain (\d+)
or for string
the "(.*)" element should contain "(.*)"
or other example for string
the "(.*)" element should contain (.*)
and for asserting depends on how your code is organized, use what you have or you could just do:
if($someActual != $expected)
{
throw new \Exception("something meaningful");
}
Thanks to #lauda, I was able to write the code I wanted:
/**
* #Then the :element element should be exactly :value
*
* Checks that the element with the specified CSS is the exact value.
*/
public function theElementShouldBeExactly($element, $value) {
$page = $this->getSession()->getPage();
$element_text = $page->find('css', "$element")->getText();
if ($element_text === NULL || strlen($element_text < 1)) {
throw new Exception("The element $element had a NULL value.");
}
if ($element_text !== $value) {
throw new Exception("Element $element_text did not match value $value.");
}
}

custom Yii validation class

I've created this custom class to validate some numbers on my website.
class EPriceValidator extends CValidator
{
public $number_type;
/*
* Regular Expressions for numbers
*/
private $default_pattern = '/[^0-9,.]/';
private $price_pattern = '/[^0-9,.]/';
/*
* Default error messages
*/
private $default_msg = '{attribute} is an invalid number.';
private $price_msg = '{attribute} is an invalid price.';
/**
* Validates the attribute of the object.
* If there is any error, the error message is added to the object.
* #param CModel $object the object being validated
* #param string $attribute the attribute being validated
*/
protected function validateAttribute($object,$attribute)
{
// check the strength parameter used in the validation rule of our model
if ($this->number_type == 'price')
{
$pattern = $this->price_pattern;
$error_message = $this->price_msg;
}
else {
$pattern = $this->default_pattern;
$error_message = $this->default_msg;
}
// extract the attribute value from it's model object
$value=$object->$attribute;
if(!preg_match($pattern, $value))
{
$this->addError($object,$attribute, $error_message);
}
}
/**
* Implementing Client Validation
*
* Returns the JavaScript needed for performing client-side validation.
* #param CModel $object the data object being validated
* #param string $attribute the name of the attribute to be validated.
* #return string the client-side validation script.
* #see CActiveForm::enableClientValidation
*/
public function clientValidateAttribute($object,$attribute)
{
// check the strength parameter used in the validation rule of our model
if ($this->number_type == 'price')
{
$pattern = $this->price_pattern;
$error_message = $this->price_msg;
}
else
{
$pattern = $this->default_pattern;
$error_message = $this->default_msg;
}
$condition="value.match(".$pattern.")";
return "
if(".$condition.") {
messages.push(".CJSON::encode($error_message).");
}
";
}
}
it works fine. but how do i make it display the correct field name of the error? right now when there is an error detected on client side, the clientValidateAttribute() displays
{attribute} is an invalid number.
instead of
Total orders is an invalid number.
where Total orders is the input field that is in valid.
Any idea how to fix this?
I rechecked this in the Yii documentation, and it seems you have to add an array with parameters to replace the placeholders in your string. But if you only use the default placeholder for the attribute, it should work by default.
Do you have only the problem on client validation? Because I now checked also the Yii code, and it seems that your code is right, and should work (at least the server validation). But in the client validation you just pass the error mesasage to JSON without any processing, so the {attribute} is not replaces anywhere.
Try to add this to youc client validation before the return
$params['{attribute}']=$object->getAttributeLabel($attribute);
$error_message = strtr($error_message,$params));

Does Laravel Input::hasfile() work on input arrays?

I'm working on a Laravel project that uses a form with multiple file inputs. If I submit the form with the first input empty and all other inputs with a file, then hasFile returns false. It will only return true if the first input contains a file.
if(Input::hasfile('file'))
{
// do something
}
This is the input array via Input::file('file). The small image input is empty, but the large is not. I'd like it to look at the whole array and if there any files present, then proceed with the "do something".
Array
(
[small] =>
[large] => Symfony\Component\HttpFoundation\File\UploadedFile Object
(
[test:Symfony\Component\HttpFoundation\File\UploadedFile:private] =>
[originalName:Symfony\Component\HttpFoundation\File\UploadedFile:private] => image_name.jpg
[mimeType:Symfony\Component\HttpFoundation\File\UploadedFile:private] => image/jpeg
[size:Symfony\Component\HttpFoundation\File\UploadedFile:private] => 44333
[error:Symfony\Component\HttpFoundation\File\UploadedFile:private] => 0
[pathName:SplFileInfo:private] => /Applications/MAMP/tmp/php/phpHILgX2
[fileName:SplFileInfo:private] => phpHILgX2
)
)
Is this expected behavior? Or, should it be looking at the entire array?
You can check by using the array key for example like below :-
HTML Input type File Element :
<input type="file" name="your_file_name[]" />
Laravel 5 : $request->hasFile('your_file_name.'.$key)
Laravel 4.2 : Input::hasFile('your_file_name.'.$key)
Taken from source:
/**
* Determine if the uploaded data contains a file.
*
* #param string $key
* #return bool
*/
public function hasFile($key)
{
if (is_array($file = $this->file($key))) $file = head($file);
return $file instanceof \SplFileInfo;
}
It seems that it only checks the first one from the array, head returns the first item from the array.
Since I can't comment, seems I'll have to post.
Ronak Shah's answer really should be marked the correct one here, and when I figured out why, it instantly had me saying "Sonnofa--" after 30-40 minutes trying to figure this... "mess" out.
Turns out to use hasFile() on an input array, you need to use dot notation.
So (using my own example) instead of
$request->hasFile("img[29][file]")
it needs to be
$request->hasFile("img.29.file")
That's certainly an eye-opener, given that PHP and dot notation don't really go together. Input arrays really are problem children.
here is a snippet that may help
if(Input::hasFile('myfile')){
$file = Input::file('myfile');
// multiple files submitted
if(is_array($file))
{
foreach($file as $part) {
$filename = $part->getClientOriginalName();
$part->move($destinationPath, $filename);
}
}
else //single file
{
$filename = $file->getClientOriginalName();
$uploadSuccess = Input::file('myfile')->move($destinationPath, $filename);
}
} else {
echo 'Error: no file submitted.';
}
Taken from
http://forumsarchive.laravel.io/viewtopic.php?id=13291
At the time of writing (Laravel 8) the Request class now supports arrays for the hasFile method, as from the source code:
/**
* Determine if the request contains the given file.
*
* #param string $name
* #param string|null $value
* #param string|null $filename
* #return bool
*/
public function hasFile($name, $value = null, $filename = null)
{
if (! $this->isMultipart()) {
return false;
}
return collect($this->data)->reject(function ($file) use ($name, $value, $filename) {
return $file['name'] != $name ||
($value && $file['contents'] != $value) ||
($filename && $file['filename'] != $filename);
})->count() > 0;
}

Play framework select input validation

I am using Play 2.1
I made a select drop down box using the helper field constructor.
The drop down box have 3 fields, default:"choose a gender", Male, and Female.
How do I ensure that the user choose one of male or female, and not the default value? (A required drop down field)
I am using Play!Framework 2.1.0, below is a simple solution for your problem:
The model should be like this: (Below is simple model for your problem)
package models;
import play.data.validation.Constraints;
public class Gender {
// This field must have a value (not null or not an empty string)
#Constraints.Required
public String gender;
}
The controller should be like this:
/** Render form with select input **/
public static Result selectInput() {
Form<Gender> genderForm = Form.form(Gender.class);
return ok(views.html.validselect.render(genderForm));
}
/** Handle form submit **/
public static Result validateSelectInput() {
Form<Gender> genderForm = Form.form(Gender.class).bindFromRequest();
if (genderForm.hasErrors()) { // check validity
return ok("Gender must be filled!"); // can be bad request or error, etc.
} else {
return ok("Input is valid"); // success validating input
}
}
The template/view should be like this:
#(genderForm: Form[models.Gender])
#import views.html.helper._
#main(title = "Validate Select") {
#form(action = routes.Application.validateSelectInput()) {
#********** The default value for select input should be "" as a value *********#
#select(
field = genderForm("gender"),
options = options("" -> "Select Gender", "M" -> "Male", "F" -> "Female")
)
<input type="submit" value="Post">
}
}
See also this post as reference : Use of option helper in Play Framework 2.0 templates

Selenium: Get unique value?

Just using selenium to fill out some forms for me. I need it to generate a unique value for my username field. How can I do that?
I've got
Command: type
Target: id_of_my_field
Value: username+unique_value ???
You can use javascript to do that:
Value: javascript{'username'+Math.floor(Math.random()*100000)}
This will append a 6 digit random number to your username.
See this SO question and answers for more details and other ways to do this...
My solution which works well for me:
Save the following TEXT as a .js file and add to the Options->Options "Selenium Core Extensions" list...
Selenium.prototype.doTypeRandomName = function(locator)
{
/**
* Sets the value of an input field to a random "name"
* as though you typed it in.
*/
// All locator-strategies are automatically handled by "findElement"
var element = this.page().findElement(locator);
/* The following block generates a random name 8 characters in length */
var allowedChars = "abcdefghiklmnopqrstuvwxyz";
var stringLength = 8;
var randomstring = '';
for (var i=0; i<stringLength; i++) {
var rnum = Math.floor(Math.random() * allowedChars.length);
randomstring += allowedChars.substring(rnum,rnum+1);
}
// Replace the element text with the new text
this.browserbot.replaceText(element, randomstring);
}
Once done, you can simply select the TypeRandomName command in Selenium for each text box where you want a random "name" to be generated.
Steps for a globally reusable solution is as follows
1) Download sideflow.js from Download here
2) Add following lines into it :
Selenium.prototype.doTypeRandomName = function(locator) {
/**
* Sets the value of an input field to a random email id,
* as though you typed it in.
*
* #param locator an element locator
*/
// All locator-strategies are automatically handled by "findElement"
var element = this.page().findElement(locator);
/* The following block generates a random email string */
var allowedChars = "abcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
var stringLength = 8;
var randomstring = '';
for (var i=0; i<stringLength; i++) {
var rnum = Math.floor(Math.random() * allowedChars.length);
randomstring += allowedChars.substring(rnum,rnum+1);
}
// Replace the element text with the new text
this.browserbot.replaceText(element, randomstring);
};
3) Save the file
4) Go to Selenium ide -> options -> options ->Selenium Core extensions -> give reference of your file there.
5) Now your randomname function will appear in auto-intellisense and will be as "typerandomname" command category.
6) Sample usage could be (if base url is google.com)
<tr>
<td>open</td>
<td>/</td>
<td></td>
</tr>
<tr>
<td>typerandomname</td>
<td>css=input[class='gbqfif']</td>
<td></td>
</tr>
Hope this helps you
Here is a way to generate unique numbers without using JavaScript in your code: (i used Java)
DateFormat dateFormat = new SimpleDateFormat("ddHHmmss"); /* Here you create object of the class DateFormat, and SPECIFY THE FORMAT (ddHHmmss). (Don`enter code here`t forget to import class Date(ctrl+shift+o if you are using Eclipse)) */
Date date = new Date(); /* Here you create object of the class Date. (Dont forget to import class Date. (Dont forget to import class Date(ctrl+shift+o if you are using Eclipse)) */
String random_number = dateFormat.format(date); /* Assign your current Date(something like 19184123) (ddHHmmSS) to a local variable(it require a String) */
yourWebDriver().findElemnt(By.cssSelector("input#someId")).sendKeys("test"+random_number+"#tester.com"); /* send keys to your input injecting random number */
Such method will give truly unique number that will never repeat itself, because it use your current time...
You can add even further randomness if include mile seconds into DateFormat