Will page.has_content? wait? - capybara

I'm just wondering if I'm doing
click_on('Open')
page.has_content?("foo")
click_on("Done")
Will Capybara know to wait until the page has content 'foo' or it's just going to continue on to click "Done" regardless of the return value for page.has_content?('foo')???

Based on the code, I believe that has_content? should wait. If I recall correctly, that is what the synchronize part is for.
def has_text?(*args)
query = Capybara::Query.new(*args)
synchronize(query.wait) do
raise ExpectationNotMet unless text_found?(*args)
end
return true
rescue Capybara::ExpectationNotMet
return false
end
alias_method :has_content?, :has_text?

Related

Can you pass a "next" back to the function that called the current function?

I have a series of nested each loops that iterate through a list of cards. These loops call out to other sub-functions that test if certain conditions are met in order to proceed.
def card_handler
cards.each do |card|
#some non-relevant code is here on my end
already_sent?
end
end
def already_sent?
# allows for checking if different emails have been sent on the same card
if list_action == 147
a_s_helper(p1_label)
elsif list_action == 146
a_s_helper(p2_label)
elsif list_action == 145
a_s_helper(p3_label)
end
end
def a_s_helper(label)
if card::card_labels.include? label
# if the card already has the label, I want to log the error and return all the way to the next card in the iteration
puts '\n Order info: \n id: #{id} \n Email already sent'
next
# doesn't work
else
real_id?
end
end
Like I say in my comment in a_s_helper, if the card already has the label, I want to log the error and return all the way to the next card in the iteration. I get an "Invalid next" error from the current setup.
Is there a way to return a next back to the parent function or loop?
next is only valid in the direct context of a loop. Once you call into a method, you are no longer directly in that loop context. You cannot use next to short-circuit the outer loop like this.
You have a couple of options:
Return statuses from your predicate functions (which is what you should do, from a predicate!) and short-circuit the loop based on those, or
Use Ruby's catch...throw construct (which is NOT its raise/rescue exception handler, but is instead something like a block-scoped GOTO statement)
Option 1: Returning statuses. This is the most appropriate method, IMO. Predicate methods (those ending in ?) should conventionally return a boolean and be idempotent (that is, should have no side effects, such as logging a statement). They are conventionally used to ask a yes/no question. Deciding what to do based on that question should ideally be outside of their scope.
def card_handler
cards.each do |card|
#some non-relevant code is here on my end
if already_sent?
puts '\n Order info: \n id: #{id} \n Email already sent'
next
end
end
end
def already_sent?
case list_action
when 145
a_s_helper(p3_label)
when 145
a_s_helper(p2_label)
when 147
a_s_helper(p1_label)
end
end
def a_s_helper(label)
card::card_labels.include? label
end
This causes your helpers to return a true or false value to your loop, which can decide to log a message and go to the next iteration.
Option 2: catch...throw
def card_handler
cards.each do |card|
# Put all your code that should nomally run inside the catch block. If
# the message :email_sent is thrown, then Ruby will zip up the stack and
# resume execution at the end of the block. This will skip any unexecuted
# code in the block, essentially terminating the execution.
catch :email_sent do
already_sent?
end
end
end
def already_sent?
# ...
end
def a_s_helper(label)
# ...
throw :email_sent if card::card_labels.include? label
# ...
end
You may be tempted to use option 2, since it requires less careful control over method construction, but it is perilously close to exceptions as flow control which are widely considered an antipattern (it's essentially a slightly more fancy GOTO, which is notorious for making code difficult to read and debug). If you can simply return a status from your helpers and decide whether or not to continue the loop based on that, you should do so.
I want to show how I ended up implementing the solution I got from #Chris-heald for future people who see this question. I made it a little more compact. This was the code I ended up using:
def card_handler
cards.each do |card|
real_id?
puts "real_id? : #{real_id?}"
next if !(real_id?)
needs_email?
puts "needs_email? : #{needs_email?}"
next if !(needs_email?)
get_email_info
end
end
def needs_email?
case list_action
when 147
!(card::card_labels.include? p1_label::id)
when 146
!(card::card_labels.include? p2_label::id)
when 145
!(card::card_labels.include? p3_label::id)
else
false
end
end
def real_id?
id != 0 ? true : false
end
def get_email_info
#more stuff
end

Checking .empty? after destroy not working

I wrote a test that checks for referral's from user's within the same company. In the assertions I am checking that the referrals are not empty, then running the destroy_referrals method before finally checking that the referrals are empty. assert referrals.empty? is returning a failure,
1) Failure:
CompanyTest#test_destroy_referrals_with_referrals [test/models/company_test.rb:634]:
Expected false to be truthy.
company_test.rb
def test_destroy_referrals_with_referrals
company = companies(:default)
referrals = company.users.map {|u| u.referrals unless u.referrals.empty?}.uniq.compact
assert !referrals.empty?
company.destroy_referrals
assert referrals.empty?
end
I was expecting that the last assertion would confirm that the referrals have been deleted. Any ideas why this throws a failure?
The problem is that you have prepared the referrals in advance and never changed it. To make the test to pass, you should reload them:
def test_destroy_referrals_with_referrals
company = companies(:default)
referrals = -> { company.users.map(&:referrals).reject(&:empty?) }
assert !referrals.().empty?
company.destroy_referrals
assert referrals.().empty?
end
Referrals is an array object. You loaded it once, it stays in memory. Why would you expect it to change?
Re-read it.
referrals = company.users.map {|u| u.referrals unless u.referrals.empty?}.uniq.compact
assert !referrals.empty?
company.destroy_referrals
company.reload # just for good measure
referrals = company.users.map {|u| u.referrals unless u.referrals.empty?}.uniq.compact
assert referrals.empty?

Rails application helper return if false

I'm writing a helper method to determine if current has any pending reviews to write. If there is a pending review, simply print a line in the view.
My helper is putting exactly the right stuff to the console, however I'm struggling with how to simply return it. In this scenario, current user has an id: 4.
My Code:
def review_pending
gallery = current_user.galleries.first
if gallery
if gallery.permissions.accepted
gallery.permissions.accepted.each do |p|
return true if p.user.reviews.find_by_reviewer_id(!current_user)
puts "already written review: #{p.user.reviews.find_by_reviewer_id(4)} - prints correctly"
end
end
end
end
My goal: if there is a user from the list that current user has not yet reviewed return true.
Thanks!!!
Thanks for all your pointers!
I had forgotten/learned 2 things to make it work:
First, if nil is returned, ruby returns the last returned value which in my case was true (if gallery.permissions.accepted).
Secondly, I placed the "!" before current_user, and should have placed it before the entire line.
Corrected Code:
def review_pending
gallery = current_user.galleries.first
if gallery
gallery.permissions.accepted.each do |p|
return !p.user.reviews.find_by_reviewer_id(current_user.id)
end
end
return false
end

Will returning a nil value from a block passed to Rails.cache.fetch clear it?

Let's suppose I have a method like this:
def foo
Rails.cache.fetch("cache_key", :expires_in => 60.minutes) do
return_something
end
end
return_something sometimes returns a nil value. When this happens, I don't want the nil value to be cached for 60 minutes. Instead, the next time I call foo, I want the block passed to fetch to be executed again.
Is Rails.cache.fetch working like this by default? Or do I have to implement this functionality?
Update (with Answer)
Turns out, the answer was no, at least when using Memcached.
it depends on the implementation of the cache-store that you are using. i would say that it should not cache nil values, but empty strings are ok to cache.
look at the dalli store implementation ie:
def fetch(name, options=nil)
options ||= {}
name = expanded_key name
if block_given?
unless options[:force]
entry = instrument(:read, name, options) do |payload|
payload[:super_operation] = :fetch if payload
read_entry(name, options)
end
end
if !entry.nil?
instrument(:fetch_hit, name, options) { |payload| }
entry
else
result = instrument(:generate, name, options) do |payload|
yield
end
write(name, result, options)
result
end
else
read(name, options)
end
end
The updated answer to this question is: By default fetch caches nil values, but using the dalli_store engine you can avoid it with cache_nils option:
Rails.cache.fetch("cache_key", expires_in: 60.minutes, cache_nils: false) do
return_something
end
Worth noting, the defaults for Dalli have changed in recent years - the flag for nil-caching is currently false by default. See https://github.com/petergoldstein/dalli
It's definitely worth adding a test to check that your setup does what you expect (especially for production mode)

Is there a more rubylike way of doing this helper function

def work_location(application)
if application.contact.work_location.blank? rescue nil
return false
else
return true
end
return false
end
Basically i want to return true or false ....I only want to return true if the work_location is not blank and i need to catch the nil error
Actually this produces a syntax error
syntax error, unexpected modifier_rescue, expecting keyword_then or ';' or '\n'
..._location.blank? rescue nil
def work_location(application)
application.try(:contact).try(:work_location).present?
end
Personally I dislike handling potential nils by doing rescue false because you catch far more than nils: such a rescue rescues all sorts of other errors, for example it will catch NoMethodError, so if you'd typed one of the method names it would squash that error and make it much harder to track down.
Write tests and check both true and false return cases
Shorten code above with:
def work_location(application)
application.contact.work_location.blank? rescue true
end
As far as I can tell, you are creating a helper method here.
I should define a method on application, which you can then use in your views.
The advantage: it is purely object-oriented. An application should know if it has a workplace or not.
Secondly, use try: it will only attempt the given method or block if the receiver is not nil, else it returns nil.
So :
class Application
def has_work_location?
self.contact.try { |c| c.work_location.present? }
end
end
Note that this usage of try only works in rails 3.2, if you are on an older version it does not accept a block. Furthermore nil.present? works and returns falso, so you could write
def has_work_location?
self.contact.try(:work_location).present?
end
Note: because we are adding a method to application, we can safely assume application, so we only need to check that the contact exists anymore.
In your views you can then just write:
<%= #application.contact.workplace if #application.has_work_place? %>
or something similar. Hope this helps.

Resources