How to validate a complete form with RSpec and Capybara? - ruby

I'm writing a request test with RSpec and Capybara. I have a hash that maps form field names to expected values.
How can I check easily that each form field has the expected value?
So far, I'm doing this, but it's complex and unmaintainable. I'm also considering only two kind of input controls in this case (select boxes and the rest):
expected_data = {
"address" => "Fake st 123",
"city" => "Somewhere",
"email" => "whoknows#example.com",
"gender" => "Male",
"state" => "FL",
}
select_boxes = ["gender", "state"]
# check for the select boxes
expected_data.select {|k,v| select_boxes.include?(k)}.each do |name, expected_value|
page.has_select?(name, :selected_value => expected_value).should == true
end
# check for the input fields
expected_data.reject {|k,v| select_boxes.include?(k)}.values.each do |expected_value|
page.should have_css("input[value=\"#{expected_value}\"]")
end
Is there a gem or something to do this in one line?

I find the following far more maintainable:
describe "form" do
subject {page}
before { visit "/path/to/form" }
it { should have_field("address", :with => "Fake st 123") }
it { should have_select("gender", :selected => "Male") }
# And so on ...
end

Related

Create a nested HASH from a API Call doesn't work properly

I am new here and i hope that I'm doing everything right.
I also searched the Forum and with Googel, but I didn't find the answer. (Or I did not notice that the solution lies before my eyes. Then I'm sorry >.< .)
i have a problem and i dont exactly know what i am doing wrong at the moment.
I make a API request and get a big JSON back. It looks somehow like that:
"apps": [
{
"title": "XX",
... many more data
},
{
"title": "XX",
... many more data
},
{
"title": "XX",
... many more data
}
... and so on
]
After that i want to create a hash with the data i need, for example it should look like:
{
"APP_0" => {"Title"=>"Name1", "ID"=>"1234", "OS"=>"os"}
"APP_1" => {"Title"=>"Name2", "ID"=>"5678", "OS"=>"os"}
}
but the values in the hash that i create with my code looks like:
"APP_1", {"Title"=>"Name2", "ID"=>"5678", "OS"=>"os"}
dont now if this is a valid hash? And after that i want to iterate through the Hash and just output the ID. But I get an error (TypeError). What am i doing wrong?
require 'json'
require 'net/http'
require 'uri'
require 'httparty'
response = HTTParty.get('https://xxx/api/2/app', {
headers: {"X-Toke" => "xyz"},
})
all_apps_parse = JSON.parse(response.body)
all_apps = Hash.new
all_apps_parse["apps"].each_with_index do |app, i|
all_apps["APP_#{i}"] = {'Title' => app["title"],
'ID' => app["id"],
'OS' => app["platform"]}
end
all_apps.each_with_index do |app, i|
app_id = app["App_#{i}"]["id"]
p app_id
end
I hope someone can understand the problem and can help me :-). Thanks in advance.
Assuming the data looks something like this:
all_apps_parse = { "apps" => [
{
"title" => "Name1",
"id" => 1234,
"platform" => "os"
},
{
"title" => "Name2",
"id" => 5678,
"platform" => "os"
},
{
"title" => "Name3",
"id" => 1111,
"platform" => "windows"
}]
}
and with a little idea of what you want to achieve, here is my solution:
all_apps = Hash.new
all_apps_parse["apps"].each_with_index do |app, i|
all_apps["APP_#{i}"] = { 'Title' => app["title"],
'ID' => app["id"],
'OS' => app["platform"] }
end
all_apps
=> {"APP_0"=>{"Title"=>"Name1", "ID"=>1234, "OS"=>"os"}, "APP_1"=>{"Title"=>"Name2", "ID"=>5678, "OS"=>"os"}, "APP_2"=>{"Title"=>"Name3", "ID"=>1111, "OS"=>"windows"}}
all_apps.each do |key, value|
puts key # => e.g. "APP_0"
puts value['ID'] # => e.g. 1234
end
# Prints
APP_0
1234
APP_1
5678
APP_2
1111

How to use a string description to access data from a hash-within-hash structure?

I have the following:
data_spec['data'] = "some.awesome.values"
data_path = ""
data_spec['data'].split('.').each do |level|
data_path = "#{data_path}['#{level}']"
end
data = "site.data#{data_path}"
At this point, data equals a string: "site.data['some']['awesome']['values']"
What I need help with is using the string to get the value of: site.data['some']['awesome']['values']
site.data has the following value:
{
"some" => {
"awesome" => {
"values" => [
{
"things" => "Stuff",
"stuff" => "Things",
},
{
"more_things" => "More Stuff",
"more_stuff" => "More Things",
}
]
}
}
}
Any help is greatly appreciated. Thanks!
You could do as tadman suggested and use site.data.dig('some', 'awesome', values') if you are using ruby 2.3.0 (which is awesome and I didn't even know existed). This is probably your best choice. But if you really want to write the code yourself read below.
You were on the right track, the best way to do this is:
data_spec['data'] = "some.awesome.values"
data = nil
data_spec['data'].split('.').each do |level|
if data.nil?
data = site.data[level]
else
data = data[level]
end
end
To understand why this works first you need to understand that site.data['some']['awesome']['values'] is the same as saying: first get some then inside that get awesome then inside that get values. So our first step is retrieving the some. Since we don't have that first level yet we get it from site.data and save it to a variable data. Once we have that we just get each level after that from data and save it to data, allowing us to get deeper and deeper into the hash.
So using your example data would initally look like this:
{"awesome" => {
"values" => [
{
"things" => "Stuff",
"stuff" => "Things",
},
{
"more_things" => "More Stuff",
"more_stuff" => "More Things",
}
]
}
}
Then this:
{"values" => [
{
"things" => "Stuff",
"stuff" => "Things",
},
{
"more_things" => "More Stuff",
"more_stuff" => "More Things",
}
]
}
and finally output like this:
[
{
"things" => "Stuff",
"stuff" => "Things",
},
{
"more_things" => "More Stuff",
"more_stuff" => "More Things",
}
]
If you're receiving a string like 'x.y.z' and need to navigate a nested hash, Ruby 2.3.0 includes the dig method:
spec = "some.awesome.values"
data = {
"some" => {
"awesome" => {
"values" => [
'a','b','c'
]
}
}
}
data.dig(*spec.split('.'))
# => ["a", "b", "c"]
If you don't have Ruby 2.3.0 and upgrading isn't an option you can just patch it in for now:
class Hash
def dig(*path)
path.inject(self) do |location, key|
location.respond_to?(:keys) ? location[key] : nil
end
end
end
I wrote something that does exactly this. Feel free to take any information of value from it or steal it! :)
https://github.com/keithrbennett/trick_bag/blob/master/lib/trick_bag/collections/collection_access.rb
Check out the unit tests to see how to use it:
https://github.com/keithrbennett/trick_bag/blob/master/spec/trick_bag/collections/collection_access_spec.rb
There's an accessor method that returns a lambda. Since lambdas can be called using the [] operator (method, really), you can get such a lambda and access arbitrary numbers of levels:
accessor['hostname.ip_addresses.0']
or, in your case:
require 'trick_bag'
accessor = TrickBag::CollectionsAccess.accessor(site.data)
do_something_with(accessor['some.awesome.values'])
What you are looking for is something generally looked down upon and for good reasons. But here you go - it's called eval:
binding.eval data

Ruby Array of Hashes of Hashes Map

I have this hash,
[{ "player" => { "name" => "Kelvin" , "id" => 1 } , "player" => { "name" => "David",
"id" => 2 }]
I checked if each event contains the keys [id,name] with the following line in my Rspec,
json_response.map{|player| ["name","id"].all? {|attribute| player["player"].key?
(attribute)}}.should_not include(false)
which works perfectly. How can I simplify this and make it more efficient?
How about :
json_response.each do |event|
event['player'].should have_key('name')
event['player'].should have_key('id')
end
Much clearer IMHO
Edit : if you need to check a lot of columns :
json_response.each do |event|
['name', 'id', 'foo', 'bar', 'baz'].each do |column|
event['player'].should have_key(column)
end
end
According to the documentation you should be able to do this:
json_response.each do |event|
event['player'].should include('name', 'id')
end

Update activerecord relation given a hash of multiple entries

I'm quite new to Rails, so be gentle :)
I have the following models set-up:
class User
has_many :it_certificates, :class_name => 'UserCertificate'
class UserCertificate
belongs_to :skill
Given the following input (in JSON)
{
"certificates":[
{ // update
"id":1,
"name":"Agile Web Dev 2",
"entity":"Agile Masters!",
"non_it":false,
"date_items":{
"month":10,
"year":2012
},
"skill": {
"id":57
}
},
{ // create
"name":"Agile Web Dev 1",
"entity":"Agile Masters!",
"non_it":false,
"date_items":{
"month":10,
"year":2011
},
"skill": {
"id":58
}
}
]
}
How's the easiest way to update the information for the relation it_certificates?
I've been looking to update_all but it doesn't match my needs (it only updates given fields with the same value).
So I've been struggling around with the approach of iterating over each of these records and then update them one-by-one.
I mean struggling because it looks to me there are lots of things I have to care of when the idea of Rails is the opposite.
Thanks in advance!
So, here's my solution for now:
def self.update_from_hash(data, user_id)
self.transaction do
data.each do |certificate|
if certificate[:id] == nil
# create
if !self.create(
:name => certificate[:name],
:entity => certificate[:entity],
:user_id => user_id,
:non_it => certificate[:non_it],
:skill_id => certificate[:skill][:id],
:date => self.build_date_from_items(certificate[:date_items][:month], certificate[:date_items][:year])
)
raise ActiveRecord::Rollback
end
else
# update
if !self.update(certificate[:id], {
:name => certificate[:name],
:entity => certificate[:entity],
:non_it => certificate[:non_it],
:skill_id => certificate[:skill][:id],
:date => self.build_date_from_items(certificate[:date_items][:month], certificate[:date_items][:year])
})
raise ActiveRecord::Rollback
end
end
end
end
return true
end
It works, but I'm still expecting a more elegant solution :)

using rescue in variable assignment

I have this
User.new ( gender: auth.extra.raw_info.gender.capitalize, ...)
auth is a hash that looks like this
auth => {:extra => { :raw_info => { :gender => ... , .. }, ..} ..}
sometimes, for whatever reason, the gender doesn't exist and I want to have a default value for it when I create a new user
If I try
gender: auth.extra.raw_info.gender.try(:capitalize) || "Male"
but the gender itself doesn't exist, and I can't try on gender
using gender: auth.extra.raw_info.gender.capitalize rescue "Male"
also doesn't work since it says I can't use capitalize on nil (which is gender)
Is there a way to do this without using a variable for this (since it will become messier)
I think the standard way to do this is to use reverse_merge:
auth.reverse_merge! {:extra => { :raw_info => { :gender => "Male" } } }

Resources