Check if an element has a label in Selenium/WebDriver (Ruby) - ruby

I have the following HTML code:
<input id="session_remember_me" name="session[remember_me]" type="checkbox" value="1" />
<label for="session_remember_me">Remember me</label>
I want to check if the session_remember_me field has label Remember me
How do I check that?

I don't know Ruby, but in Java, you could just do the following :
boolean isLabelPresent = true;
try {
driver.findElement(By.xpath("//label[#for='session_remember_me']"));
} catch (NoSuchElementException e) {
isLabelPresent = false;
}

Here is how to do it in Ruby:
def isElementPreset?(type, selector)
begin
#driver.find_element(type, selector)
true
rescue Selenium::WebDriver::Error::NoSuchElementError
false
end
end
assert isElementPresent?(:css, "label[for='session_remember_me']")

I'd do this with XPath:
//form/div/div/div/span/input[#id='session_remember_me']/parent::span/parent::div/parent::div/label[#for='session_remember_me'][contains(text(),'Remember me')]
and just check if such an element exists.

Related

How to get HTML attribute value in Cypress

I am starting to learn Cypress after few years working with Selenium. In Selenium i'm regularly using GetAttribute() method. As an exercise i'm trying to do the same with Cypress, to print class attribute value from the following HTML element:
<input class="form-control ng-touched ng-pristine ng-valid" max="21" min="1" type="number">
This is my code:
cy.log(cy.get('input').invoke('attr', 'class'));
Output:
log Object{5}
I tried to use Lakitna cypress-commands (https://github.com/Lakitna/cypress-commands) with the code:
cy.log(cy.get('input').attribute('class'));
Output:
cy commands are asynchronous so for logging you need to use .then:
cy.get('input').then(($input) => {
cy.log($input.attr('class'));
});
or
// with assertion
cy.get('input').should('have.attr', 'class').then(cy.log);
If you are looking for the value of the html tag and you end up here, this is the simplest way to do that:
cy.get(`[data-testid="${key}"]`).then(($input) => {
if($input.prop('nodeName') === "SELECT") {
//Select corresponding input to value provided in dropdown lists
} else {
//Input each value provided into each field
cy.get(`[data-testid="${key}"]`).clear().should('have.value', '').type(testMachine[key]).should('have.value', testMachine[key])
}

Error: undefined method `ord' for "T":String

I'm making an application to obtain a "key" using tideSDK with ruby. TideSDK support ruby 1.8.7, so I'm using ruby 1.8.7, whatever.
I'm following this wiki for using tideSDK with ruby
I have index.html file looks like this :
<script type="text/ruby">
require 'enumerator'
def gen_key(name, acc_type)
name = name.gsub(/[^A-Za-z0-9]/,"").upcase
acc_type = acc_type.upcase
name_array = name.split("")
data = [0, 0, 0]
name_array.each_slice(3) do |i|
i.each_with_index do |j, index|
data[index] += j.ord*name.length
end
end
# other stuff here for return key
end
</script>
<form id="myForm" action="javascript:getKey();">
<input type="text" name="name" id="name"/>
<input type="text" name="type" id="type"/>
<input type="submit"/>
</form>
<script src="jquery.min.js"></script>
<script>
function getKey(){
var values = {};
$.each($('#myForm').serializeArray(), function(i, field) {
values[field.name] = field.value;
});
alert(gen_key(values['name'], values['type']));
}
</script>
when I launch my app and submit the form, I getting error on console :
Error: undefined method `ord' for "T":String
btw, Thanks for downvote, now I know ord doesn't exist in ruby 1.8, and I'm using this
data[index] += j[0]*name.length
Looks like this is linked to this line:
data[index] += j.ord*name.length
You are calling an "ord" method which does not exist in String in Ruby 1.8.7. Ord does exist in 1.9.3.

AngularJS Form Validation inside an ng-repeat

So I am trying to validate the input of one item inside of an ng-repeat. For examples sake lets say that I have 5 items (1,2,3,4,5) and I only want to validate the form if the 4th item is selected.
I have used ng-pattern before to validate forms, but not one that had a dropdown menu to select item.name
I have included the regex I would like the 4th item to be validated with inside the ng-pattern.
<div>
<select name="name" ng-model="item.name" ng-options="item for item in items" required></select>
</div>
<div>
<input name="results" type="text" ng-model="item.results" ng-pattern="/^\d\d\d\/\d\d\d/" required>
</div>
Any suggestions as to the correct way to validate this situation would be greatly appreciated. I have thought about creating a directive to validate this, but that feels like is an overly complicated solution to this since I would not use the directive more than once in this app.
//////////////////////////////////////////////////
It wouldn't let me answer my own question so here is the answer I figured out.
What I ended up having to do was use ng-pattern and pass it a function.
<input name="results" type="text" ng-model="vital.results" ng-pattern="vitalRegEx()" required>
Here is the controller code
$scope.item4RegEx = /^\d{2,3}\/\d{2,3}$/;
$scope.itemRegEx = function() {
if($scope.item && $scope.item.name === "fourth item")
return $scope.item4RegEx;
else return (/^$/);
};
or else...
add ng-change directive on the select dropdown which calls a Controller method and that controller method sets a flag whether to validate form or not.
eg.
<select ng-change="checkIfFormShouldbeValidated()" ng-model="item.name"></select>
// Inside controller
$scope.checkIfFromShouldBeValidated = function(){
if( $scope.item.name == 4th Item ) $scope.shouldValidate = true;
else $scope.shouldValidate = false;
};
$scope.formSubmit = function(){
if(($scope.shouldValidate && form.$valid) || (!$scope.shouldValidate)){
// Submit Form
}
};
See if it helps.
I wrote this recursive function inside my controller to check the validity of all child scopes.
function allValid(scope) {
var valid = true;
if (scope.$$childHead) {
valid = valid && allValid(scope.$$childHead);
}
if (scope.$$nextSibling) {
valid = valid && allValid(scope.$$nextSibling);
}
if (scope.scorePlannerForm) {
valid = valid && scope.myForm.$valid;
}
return valid;
}
Then in my controller I check this with the controller scope.
function formSubmit() {
if (allValid($scope)) {
// perform save
}
}

AngularJS: integrating with server-side validation

I have an angular app that contains a save button taken from the examples:
<button ng-click="save" ng-disabled="form.$invalid">SAVE</button>
This works great for client side validation because form.$invalid becomes false as user fixes problems, but I have an email field which is set invalid if another user is registered with same email.
As soon as I set my email field invalid, I cannot submit the form, and the user has no way to fix that validation error. So now I can no longer use form.$invalid to disable my submit button.
There must be a better way
This is another case where a custom directive is your friend. You'll want to create a directive and inject $http or $resource into it to make a call back to the server while you're validating.
Some pseudo code for the custom directive:
app.directive('uniqueEmail', function($http) {
var toId;
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
//when the scope changes, check the email.
scope.$watch(attr.ngModel, function(value) {
// if there was a previous attempt, stop it.
if(toId) clearTimeout(toId);
// start a new attempt with a delay to keep it from
// getting too "chatty".
toId = setTimeout(function(){
// call to some API that returns { isValid: true } or { isValid: false }
$http.get('/Is/My/EmailValid?email=' + value).success(function(data) {
//set the validity of the field
ctrl.$setValidity('uniqueEmail', data.isValid);
});
}, 200);
})
}
}
});
And here's how you'd use it in the mark up:
<input type="email" ng-model="userEmail" name="userEmail" required unique-email/>
<span ng-show="myFormName.userEmail.$error.uniqueEmail">Email is not unique.</span>
EDIT: a small explanation of what's happening above.
When you update the value in the input, it updates the $scope.userEmail
The directive has a $watch on $scope.userEmail it set up in it's linking function.
When the $watch is triggered it makes a call to the server via $http ajax call, passing the email
The server would check the email address and return a simple response like '{ isValid: true }
that response is used to $setValidity of the control.
There is a in the markup with ng-show set to only show when the uniqueEmail validity state is false.
... to the user that means:
Type the email.
slight pause.
"Email is not unique" message displays "real time" if the email isn't unique.
EDIT2: This is also allow you to use form.$invalid to disable your submit button.
I needed this in a few projects so I created a directive. Finally took a moment to put it up on GitHub for anyone who wants a drop-in solution.
https://github.com/webadvanced/ng-remote-validate
Features:
Drop in solution for Ajax validation of any text or password input
Works with Angulars build in validation and cab be accessed at formName.inputName.$error.ngRemoteValidate
Throttles server requests (default 400ms) and can be set with ng-remote-throttle="550"
Allows HTTP method definition (default POST) with ng-remote-method="GET"
Example usage for a change password form that requires the user to enter their current password as well as the new password.:
<h3>Change password</h3>
<form name="changePasswordForm">
<label for="currentPassword">Current</label>
<input type="password"
name="currentPassword"
placeholder="Current password"
ng-model="password.current"
ng-remote-validate="/customer/validpassword"
required>
<span ng-show="changePasswordForm.currentPassword.$error.required && changePasswordForm.confirmPassword.$dirty">
Required
</span>
<span ng-show="changePasswordForm.currentPassword.$error.ngRemoteValidate">
Incorrect current password. Please enter your current account password.
</span>
<label for="newPassword">New</label>
<input type="password"
name="newPassword"
placeholder="New password"
ng-model="password.new"
required>
<label for="confirmPassword">Confirm</label>
<input ng-disabled=""
type="password"
name="confirmPassword"
placeholder="Confirm password"
ng-model="password.confirm"
ng-match="password.new"
required>
<span ng-show="changePasswordForm.confirmPassword.$error.match">
New and confirm do not match
</span>
<div>
<button type="submit"
ng-disabled="changePasswordForm.$invalid"
ng-click="changePassword(password.new, changePasswordForm);reset();">
Change password
</button>
</div>
</form>
I have created plunker with solution that works perfect for me. It uses custom directive but on entire form and not on single field.
http://plnkr.co/edit/HnF90JOYaz47r8zaH5JY
I wouldn't recommend disabling submit button for server validation.
Ok. In case if someone needs working version, it is here:
From doc:
$apply() is used to enter Angular execution context from JavaScript
(Keep in mind that in most places (controllers, services)
$apply has already been called for you by the directive which is handling the event.)
This made me think that we do not need: $scope.$apply(function(s) { otherwise it will complain about $digest
app.directive('uniqueName', function($http) {
var toId;
return {
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
//when the scope changes, check the name.
scope.$watch(attr.ngModel, function(value) {
// if there was a previous attempt, stop it.
if(toId) clearTimeout(toId);
// start a new attempt with a delay to keep it from
// getting too "chatty".
toId = setTimeout(function(){
// call to some API that returns { isValid: true } or { isValid: false }
$http.get('/rest/isUerExist/' + value).success(function(data) {
//set the validity of the field
if (data == "true") {
ctrl.$setValidity('uniqueName', false);
} else if (data == "false") {
ctrl.$setValidity('uniqueName', true);
}
}).error(function(data, status, headers, config) {
console.log("something wrong")
});
}, 200);
})
}
}
});
HTML:
<div ng-controller="UniqueFormController">
<form name="uniqueNameForm" novalidate ng-submit="submitForm()">
<label name="name"></label>
<input type="text" ng-model="name" name="name" unique-name> <!-- 'unique-name' because of the name-convention -->
<span ng-show="uniqueNameForm.name.$error.uniqueName">Name is not unique.</span>
<input type="submit">
</form>
</div>
Controller might look like this:
app.controller("UniqueFormController", function($scope) {
$scope.name = "Bob"
})
Thanks to the answers from this page learned about https://github.com/webadvanced/ng-remote-validate
Option directives, which is slightly less than I do not really liked, as each field to write the directive.
Module is the same - a universal solution.
But in the modules I was missing something - check the field for several rules.
Then I just modified the module https://github.com/borodatych/ngRemoteValidate
Apologies for the Russian README, eventually will alter.
I hasten to share suddenly have someone with the same problem.
Yes, and we have gathered here for this...
Load:
<script type="text/javascript" src="../your/path/remoteValidate.js"></script>
Include:
var app = angular.module( 'myApp', [ 'remoteValidate' ] );
HTML
<input type="text" name="login"
ng-model="user.login"
remote-validate="( '/ajax/validation/login', ['not_empty',['min_length',2],['max_length',32],'domain','unique'] )"
required
/>
<br/>
<div class="form-input-valid" ng-show="form.login.$pristine || (form.login.$dirty && rv.login.$valid)">
From 2 to 16 characters (numbers, letters and hyphens)
</div>
<span class="form-input-valid error" ng-show="form.login.$error.remoteValidate">
<span ng:bind="form.login.$message"></span>
</span>
BackEnd [Kohana]
public function action_validation(){
$field = $this->request->param('field');
$value = Arr::get($_POST,'value');
$rules = Arr::get($_POST,'rules',[]);
$aValid[$field] = $value;
$validation = Validation::factory($aValid);
foreach( $rules AS $rule ){
if( in_array($rule,['unique']) ){
/// Clients - Users Models
$validation = $validation->rule($field,$rule,[':field',':value','Clients']);
}
elseif( is_array($rule) ){ /// min_length, max_length
$validation = $validation->rule($field,$rule[0],[':value',$rule[1]]);
}
else{
$validation = $validation->rule($field,$rule);
}
}
$c = false;
try{
$c = $validation->check();
}
catch( Exception $e ){
$err = $e->getMessage();
Response::jEcho($err);
}
if( $c ){
$response = [
'isValid' => TRUE,
'message' => 'GOOD'
];
}
else{
$e = $validation->errors('validation');
$response = [
'isValid' => FALSE,
'message' => $e[$field]
];
}
Response::jEcho($response);
}

The multiemail validation method is not working, if we call the prototype.js on the page?

I have created a add email method (jquery) to validate a multiple emails for recipient text box. it's working fine when prototype.js is not declared on the page. To get rid of the $ conflict i also incorporated the $ noconflict() method measure measure. The other field validations are working in this scenario, except the receipient email validation field. AS per my finding "jQuery.validator.methods.email.call(this, value, element)" line no 50 of the page is not working and hence the method is not firing . I need to call the prototype.js as well. Please see the following code for a clearer understanding.......Thanks in advance.
Please see the code below:
Multi Email Validation
var JQ = jQuery.noConflict();
JQ(document).ready(function() {
// Handler for .ready() called.
JQ("#email-form").validate({
rules : {
email : {
required : true,
email : true
},
recipientEmail : {
multiemail: true,
required : true
// email : true
}
},
messages: {
email: {
required: "Please enter your email address.",
email: "Please enter a valid email address"
},
recipientEmail: {
multiemail: "One or more of your recipient email addresses needs correction.",
required: "Please enter the recipient's email address."
//email: "Please enter a valid email address"
}
}
});
});
JQ.validator.addMethod("multiemail", function(value, element) {
if (this.optional(element)) // return true on optional element
return true;
// var emails = value.split( new RegExp( "\s*,\s*", "gi" ) );
var emails = value.split( new RegExp( "\s*,\s*", "gi" ) );
valid = true;
maxEmaillength = emails.length;
for(var i in emails)
{
value = emails[i];
valid = valid && jQuery.validator.methods.email.call(this, value, element);
// Maximum email length validation
if(maxEmaillength > 5)
{
JQ('label.error:first').html("Please enter only 5 mail IDs at a time");
JQ('label.error:first').css(display, block);
setTimeout(alert("Please enter only 5 mail IDs at a time"), 5);
}
}
return valid;
}, 'One or more email addresses are invalid');
</head>
<body>
<form action="" method="get" name="email-form" id="email-form">
<label for="email">email</label>
<input type="text" name="email" id="email" style="width:200px" />
<br />
<label for="recipientEmail">Recipient Email</label>
<input type="text" name="recipientEmail" id="recipientEmail" style="width:500px" /><br />
<input type="submit" name="Submit" id="Submit" value="Submit" />
</form>
</body>
</html>
I have just change the approach a little bit, as jQuery.validator.methods.email.call(this, value, element) was not working in the previous custom method. Although i could not find the exact reason, why that was not working with prototype.js and what the exact solution for that problem. But the following code snippet is working as desired. Just replace that previous jquery custom email method with the following one.
function validateEmail(field) {
var regex=/\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b/i;
return (regex.test(field)) ? true : false;
}
JQ.validator.addMethod("multiemail", function(value, element)
{
var result = value.split(",");
for(var i = 0;i < result.length;i++)
if(!validateEmail(result[i]) || result.length > 5)
return false;
return true;
},'One or more email addresses are invalid');

Resources