I am writing a test to go through a test flow of a monetary registration. The registration has Add-ons that appear in different steps that flow. My goal is to be able to select the checkboxes in the add-on shadow root, so I can get to the next step. The elements within the shadow root that I'm trying to focus on seem like they can be targeted, and they are Enumerators, Arrays, and Hashes.
What I need help with is clicking on the checkbox, but the .click method doesn't work for the elements that I'm targeting. I don't return an error for .select, but it doesn't seem to do anything either.
As I step through the code, I think that I am targeting the correct element(s) which is centered around a checkbox. But none of the elements I see seem to be classes that can be "clicked" or interacted with. What I've tried so far:
sales_channels_reg.execute_script("return arguments[0].shadowRoot", sales_channels_reg.regsaver_section.regsaver_testtest.find("#sales-channels-element", visible: false)).find(css: "label[for='addToCart--false']")
Returns:
#<Enumerator: {"shadow-6066-11e4-a52e-4f735466cecf"=>"416aad9c-c512-496d-93c3-91c17ec6e27d"}:find(css: label[for='addToCart--false'])>
- sales_channels_reg.execute_script("return arguments[0].shadowRoot", sales_channels_reg.regsaver_section.regsaver_testtest.find("#sales-channels-element", visible: false)).find(id: "addToCart--false")
Returns
#<Enumerator: {"shadow-6066-11e4-a52e-4f735466cecf"=>"416aad9c-c512-496d-93c3-91c17ec6e27d"}:find(id: addToCart--false)>
- sales_channels_reg.execute_script("return arguments[0].shadowRoot", sales_channels_reg.regsaver_section.regsaver_testtest.find("#sales-channels-element", visible: false))
Returns
{"shadow-6066-11e4-a52e-4f735466cecf"=>"416aad9c-c512-496d-93c3-91c17ec6e27d"}
sales_channels_reg.execute_script("return arguments[0].shadowRoot", sales_channels_reg.regsaver_section.regsaver_testtest.find("#sales-channels-element", visible: false)).class
Returns
Hash
sales_channels_reg.execute_script("return arguments[0].shadowRoot", sales_channels_reg.regsaver_section.regsaver_testtest.find("#sales-channels-element", visible: false)).find(id: "addToCart--false").each do |c| puts c, c.class end
Returns
shadow-6066-11e4-a52e-4f735466cecf
416aad9c-c512-496d-93c3-91c17ec6e27d
Array
*** NoMethodError Exception: undefined method `call' for {:id=>"addToCart--false"}:Hash
(byebug) sales_channels_reg.execute_script("return arguments[0].shadowRoot", sales_channels_reg.regsaver_section.regsaver_testtest.find("#sales-channels-element", visible: false)).find(id: "addToCart--false", :visible => false).click
*** NoMethodError Exception: undefined method `click' for #<Enumerator:0x00007fbeb9195558>
nil
Code for what I'm writing
-first file-
describe "Showing 4 max add-ons in reg flow", type: :feature, service: "sales_channels1" do
context "yadda yadda" do
subject(:sales_channels_reg) { SalesChannelsRegGenerals.new }
subject(:se_signup_page) { SELogin.new }
# subject(:gen_reg) { RegistrationHelper.new }
let(:form_number) { "848624247" }
it "in test flow does thing" do
couple of other steps first
byebug (where I'm at in the code now)
end
end
end
-different file-
require "./spec/page_models/sales_channels_ncsa_section.rb"
require "./spec/page_models/sales_channels_medsaver_section.rb"
require "./spec/page_models/sales_channels_regsaver_section.rb"
require "./spec/page_models/sales_channels_four_addons_section.rb"
class SalesChannelsRegGenerals < SitePrism::Page
set_url "https://zachpartyka#{SeleniumTest.ngin_site}/register/form/{/form_number}"
section :regsaver_section, RegSaverSection, "div#siteContainer2"
end
-different file-
class RegSaverSection < SitePrism::Section
element :regsaver_testtest, "#pageContentContainer"
end
None of those values should be Enumerators, Hashes, etc (unless site-prism is really screwing with Capybaras returns). I'm guessing the reason is that you're using execute_script when you should be using evaluate_script. execute_script shouldn't be used when you expect a return value, and won't unwrap results into element references, evaluate_script does.
sales_channels_reg.evaluate_script("arguments[0].shadowRoot", ...)
Related
I'm using Watir to load all apps in a directory with infinite scroll and count them:
grid = browser.divs(class: 'rows')[1]
app_urls = grid.map { |app| app.a(class: 'element Link clickable-element').href }
app_urls.count
but I'm getting the following error:
in `method_missing': undefined method `count' for #<Watir::Map: located: false; {:class=>"rows", :tag_name=>"div", :index=>1} --> {:tag_name=>"map"}> (NoMethodError)
Why can't I use .map in this case and how should I count the number of apps instead?
grid is a single Watr::Div element. This means that #map is actually the method for looking for a map element rather than the Enumerable one.
Assuming you're wanting to iterate over the divs with class "row", then it should be:
grid = browser.divs(class: 'rows')
I'm trying to get the count of shares from a Facebook page, but I can't seem to get the result out of the hash.
Here's what I have:
y_response = #graph.get_connection('some_fb_page','posts',
{fields: ['message', 'id', 'from', 'type',
'properties', 'link', 'shares', 'likes.summary(true)',
'comments.summary(true)', 'created_time', 'updated_time']
})
So y_response is Koala::Facebook::API::GraphCollection
y_response.each do |post|
and each of the post elements is a Hash
puts post["shares"]
gives me: {"count"=>3}
but
puts post["shares"]["count"]
gives an
undefined method `[]' for nil:NilClass (NoMethodError)
I've also tried
puts post["shares"][:count]
puts post["shares"][count]
puts post["shares"].count
for laughs.
What am I doing wrong?
How do I get the count from the hash?
I am using a combination of cucumber and pageobject to test my web application. Sometimes, the script tries to click an element even before the page that contains the element starts loading. (I confirmed this by capturing the screenshots of failing scenarios)
This inconsistency is not wide-spread and it happens repeatedly only for a few elements. Instead of directly accessing those elements, if I do example_element.when_visible.click, the test suite always passes.
As of now, I click a link using link_name (generated by pageobject module on calling link(:name, identifier: {index: 0}, &block)
I would like to not edit the above mentioned snippet, but act as if i called link_name_element.when_visible.click. The reason is, the test suite is pretty large and it would be tedious to change all the occurences and I also believe that the functionality is already present and somehow I don't see it anywhere. Can anybody help me out?!
This seems solution seems quite hacky and may not be considering some edge cases. However, I will share it since there are no other answers yet.
You can add the following monkey patch assuming that you are using watir-webdriver. This would be added after you require page-object.
require 'watir-webdriver'
require 'page-object'
module PageObject
module Platforms
module WatirWebDriver
class PageObject
def find_watir_element(the_call, type, identifier, tag_name=nil)
identifier, frame_identifiers, wait = parse_identifiers(identifier, type, tag_name)
the_call, identifier = move_element_to_css_selector(the_call, identifier)
if wait
element = #browser.instance_eval "#{nested_frames(frame_identifiers)}#{the_call}.when_present"
else
element = #browser.instance_eval "#{nested_frames(frame_identifiers)}#{the_call}"
end
switch_to_default_content(frame_identifiers)
type.new(element, :platform => :watir_webdriver)
end
def process_watir_call(the_call, type, identifier, value=nil, tag_name=nil)
identifier, frame_identifiers, wait = parse_identifiers(identifier, type, tag_name)
the_call, identifier = move_element_to_css_selector(the_call, identifier)
if wait
modified_call = the_call.dup.insert(the_call.rindex('.'), '.when_present')
value = #browser.instance_eval "#{nested_frames(frame_identifiers)}#{modified_call}"
else
value = #browser.instance_eval "#{nested_frames(frame_identifiers)}#{the_call}"
end
switch_to_default_content(frame_identifiers)
value
end
def parse_identifiers(identifier, element, tag_name=nil)
wait = identifier.has_key?(:wait) ? false : true
identifier.delete(:wait)
frame_identifiers = identifier.delete(:frame)
identifier = add_tagname_if_needed identifier, tag_name if tag_name
identifier = element.watir_identifier_for identifier
return identifier, frame_identifiers, wait
end
end
end
end
end
Basically, the intent of this patch is that the Watir when_present method is always called. For example, your page object call will get translated to Watir as browser.link.when_present.click. In theory, it should get called for any method called on a page object element.
Unfortunately, there is a catch. There are some situations where you probably do not want to wait for the element to become present. For example, when doing page.link_element.when_not_visible, you would not want to wait for the element to appear before checking that it does not appear. In these cases, you can force the standard behaviour of not waiting by including :wait => false in the element locator:
page.link_element(:wait => false).when_not_visible
I have three objects that are all the same class. One was created via Item.new and the other two were pulled from the database (Mongoid). I'm passing one/any of these objects to another method and checking the type in that method via is_a?:
def initialize (item, attrs = nil, options = nil)
super(attrs, options)
raise 'invalid item object' unless item.is_a?(Item)
Well, this raise is getting hit. So I check the class, is_a and instance_of in rails console. I'm getting conflicting results. Why would they have the same class but only one of them be an instance_of that class?
>> i0.is_a? Item
=> false
>> i1.is_a? Item
=> false
>> i2.is_a? Item
=> true
>> i0.class
=> Item
>> i1.class
=> Item
>> i2.class
=> Item
>> i0.instance_of?(Item)
=> false
>> i1.instance_of?(Item)
=> false
>> i2.instance_of?(Item)
=> true
Is there a better way to do this type checking of my inputs? Why would three things that are the same class not all be instances of that class?
I don't know Mongoid, but usually, in a DB access library, you don't get the actual object out of the database but rather a proxy object that acts as a stand-in for the object stored in the DB. Since Ruby lacks the features to implement a perfect transparent proxy, you will sometimes see odd results, especially when using reflection or around object identity.
Inspired on the #KL-7 comment, it must be happening sort of that:
class Item; end
class PseudoItem; end
# PseudodItem think it's an Item:
class << PseudoItem
def inspect
'Item'
end
end
i0 = Item.new
i1 = PseudoItem.new
i0.class #=> Item (correct!)
i1.class #=> Item (wrong, due to redefinition of inspect!)
i0.is_a? Item #=> true
i1.is_a? Item #=> false, as it is a PseudoItem
Ya, same problem here...
Problem resolved (bypassed) with am ugly:
i0.class.to_s==Item.to_s
I have an array of strings, called #theModels, in a routine implemented as part of a Sinatra server. These models are options for the user to select, and are obtained by the back end (the idea being, as new models are added, then the front end code should not change).
I'm using haml to render html.
How can I enumerate each element in the list of #theModels such that each element is a checkbox? And how can I obtain which checkboxes the user has selected?
I see that just putting
= #theModels
will give me the list of strings contained in #theModels, but without spacing or the like, and certainly not in checkboxes. I've found this question that appears to be similar, but my haml-fu isn't good enough to convert that into what I need.
UPDATE:
These are options associated with a file upload, such that now the code looks like:
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
- #theModelHash.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%input{:type=>"file",:name=>"file"}
%input{:type=>"submit",:value=>"Upload"}
Problem is, that puts a file upload button on each option, instead of at the end. I only want one submit button in the end; should I have two forms that both report their results when the 'Upload' button is pressed?
UPDATE2:
After a moment's thought, the above can be modified to:
Thanks!
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
- #theModelHash.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
%input{:type=>"file",:name=>"file"}
%input{:type=>"submit",:value=>"Upload"}
And that appears to do what I want.
I think you should send the content as an hash instead.
This will give you the opportunity to set initial values in the form.
The hash #params will give you the result.
E.g. {"oranges"=>"1"}
#app.haml
%form{:method => 'post', :action => "/"}
- #models.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%input{:type => :submit, :value => "Save"}
#app.rb
require 'sinatra'
require 'haml'
get '/' do
#models = {"oranges" => true, "bananas" => false}
haml :app
end
post '/' do
#params.inspect
end
The link you provided linked to a rails solution where you have a function returning the proper html.
You can define this function yourself:
Input: key, value
Output: %input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
def check_box(key, value)
...
end
and call it in haml with
=check_box(key,value)