Selecting elements inside a frame with a dynamic src and NO id - watir-webdriver

I am trying to select a value in a dropdown which is inside a iFrame. The frame contains no id but has a src that is dynamic.
/MyApplicantPortal/Applicant/244029/SelectOrderTemplate?t=1
HTML:
<html class = ......>
.
.
.
the number 244029 changes and because of this, I am not able to identify the objects inside the frame. The iframe is the child of the div.
My code is
in_frame(:src => 'MyApplicantPortal/Applicant/244025/SelectOrderTemplate?t=1') do |frame|
select_list(:template, :id => 'selectlisname', :frame => frame)
end

Solution 1 - Using a regexp
Contrary to my initial assumption, the in_frame method does not support locating a frame using regular expressions (Issue 197). However, it is possible to monkey patch the functionality in. Where you require page-object, add the following code to monkey match the nested_frames method.
require 'watir-webdriver'
require 'page-object'
module PageObject
module Platforms
module WatirWebDriver
class PageObject
def nested_frames(frame_identifiers)
return if frame_identifiers.nil?
frame_str = ''
frame_identifiers.each do |id|
value = id.values.first
if value.is_a?(Regexp)
frame_str += "frame(:#{id.keys.first} => #{value.inspect})."
else
frame_str += "frame(:#{id.keys.first} => #{value})." if value.to_s.is_integer
frame_str += "frame(:#{id.keys.first} => '#{value}')." unless value.to_s.is_integer
end
end
frame_str
end
end
end
end
end
This then allows you to use a regular expression to define the src of the frame (when using in_frame):
class MyPage
include PageObject
in_frame(:src => /MyApplicantPortal\/Applicant\/\d+\/SelectOrderTemplate\?t=1/) do |frame|
select_list(:template, :id => 'selectlisname', :frame => frame)
end
end
Note that in a regexp, you need to escape the characters \*?{}.. The \d+ part means that any number will be matched.
Solution 2 - Define a frame_element
If you do not want to monkey patch the page object gem, then you could:
Define the frame element (using the regular expression)
Use a block, which references the frame element, to define the select list
This would look like:
class MyPage
include PageObject
element(:select_order, :frame, :src => /MyApplicantPortal\/Applicant\/\d+\/SelectOrderTemplate\?t=1/)
select_list(:template){ select_order_element.select_list_element(:id => 'selectlisname') }
end

Related

using element_name text in rspec match when_visibile

I have a page-object where i define an element as
class ProjectCreate
include PageObject
div(:feedback, :class => 'feedback_valid')
button(:save, :text => 'Save', :index => 0)
end
I'm using rpsec and i'm attempting to wait for feedback_element to be visible and then use the text from the div element in my match. Here is my test..
describe 'Add Project' do
it 'Provided Form Sucessfully' do
on(ProjectCreate) do |page|
page.set_client('add_project/valid')
page.save
expect(page.feedback_element.when_present.feedback).to match /Form successfully saved/
end
end
However I'm getting this error.
NoMethodError: undefined method `feedback' for #<Watir::Div:0x3b86f50>
I thought that by calling div element by name it would return the text and I could use that in my expect match.
Any thoughts on where I'm going wrong?
The when_present method returns the element that it as waiting for. The element does not have the feedback method; it is the page-object that does.
You can either get the text of the element that is returned by when_present:
expect(page.feedback_element.when_present.text).to match /Form successfully saved/
Or you can wait for the feedback element and then check the value of the feedback text:
page.feedback_element.when_present
expect(page.feedback).to match /Form successfully saved/

How to iterate over a page-object table

What's the cleanest way to check that each row in the table has an edit link?
example code:
class AccountPage
include PageObject
table(:cards, id: 'cards')
link(:edit, href: /edit/)
end
I want to be able to do something like this:
page.cards_element.each do |card|
card.edit? should == true
end
This wont work as the each block will return a PageObject table row and the only option is to iterate again to get cells and then only cell text can be achieved I suppose.
Solution 1 - Nested Locator
The quickest solution would be use the nested element methods while iterating through the table rows.
class AccountPage
include PageObject
table(:cards, id: 'cards')
end
page = AccountPage.new(browser)
page.cards_element.each do |card|
card.link_element(href: /edit/).visible?.should == true
end
The card.link_element(href: /edit/).visible? line is saying that for each card (ie table row), check that there is visible link element.
Solution 2 - Widget
The disadvantage of using the nested locator approach is that details of the page are now in the test code rather than the page object. This can be solved by using a custom widget.
You will need to define a widget that represents a table row:
class Card < PageObject::Elements::TableRow
def edit_element
link_element(href: /edit/)
end
end
PageObject.register_widget :card, Card, :tr
The page object would then be defined to include the widget:
class AccountPage
include PageObject
cards(:card, :css => 'table#cards tr')
end
This then allows you to write the test as:
page = AccountPage.new(browser)
page.card_elements.each do |card|
card.edit_element.visible?.should == true
end
Note that this will fail on the first row without an edit link. To improve readability and ensure all the rows are tested, I would suggest going to the expect syntax that includes an all method:
page = AccountPage.new(browser)
expect(page.card_elements.map(&:edit_element)).to all be_visible

Multiple method overrides in Mason2

Building a website using Mason2. Each page should call 3 filters (methods, called by content):
<% $.filter1(args...) {{ %>
<% $.filter2(args...) {{ %>
<% $.filter3(args...) {{ %>
I have 3 different implementation of those filters (methods). The default set of those 3 filters, are defined in the top level /Base.mc component.
Now, for a different parts of the site - (different routes), say for the
/a/all/paths>/...>
/b/.....
need use the above default set of filters/methods, but for another routes,
/c/...
/d/...
/e/...
need use another set of filter1, filter2, filter3 and for the
/x/....
/y/...
want use the third set.
The methods can be easily redefined, in lower level components, but doing it as in (this question), isn't honors the DRY principe, e.g. in each
/c/Base.mc
/d/Base.mc
/e/Base.mc
Need repeat the same 3
<%override filter1>
...
<%override filter2>
...
<%override filter3>
The question is:
how to write only once the 3 different implementations of the methods, and how to use them at once?
I tried make an compomponent, like: /set2.mi, /set3.mi, where I tried override those filter-methods, and called it in the needed /{c,d,e}/Base.mc as
<& /set2.mi &>
But this doesn't works.
How to write the 3 different methods implementatinons and call them at once in the needed deeprr Base.mc? Is it possible?
In standard perl i would probably use roles, so in the needed packages I would use something like:
with 'Set1';
with 'Set2';
Where the packages Set1 and Set2 would contain the implementations of the needed methods, or for dynamic loading would use the require ... . Is something like possible in the Mason2, or i must repeat the %ovverride?
Hope this make sense... if not, please add an comment and i will try update the question.
EDIT
Example, for shorting the code, using only one filter not 3.
The /Base.mc
<%augment wrap><% inner() %></%augment>
% #this is the "default" MyHead filter
<%filter MyHead($arg)>
<!-- in the real code it is more complicated, but for an illustration it is enough -->
<h1 class="<% $arg %>"><% $yield->() %></h1>
</%filter>
When in the /a/index.mc using it as
% $.MyHead('big') {{
some head text
% }}
will output like:
<h1 class="big">some head text</h1>
now, have an another MyHead.
<%filter MyHead($arg)>
<!-- in the real code it is more complicated - basically want output different thing -->
<h2 id="<% $arg %>"><% $yield->() %></h2>
</%filter>
If I add the above code to my /b/Base.mc it will work, and calling the MyHead filter in the /b/index.mc
% $.MyHead('modal') {{
some other text
% }}
will call the redefined filter, and will output what i want
<h2 id="modal">some other text</h2>
The problem is,
I don't want repeat the above filter code, in N other Base.mc components, like in /c/Base.mc and /d/Base.mc and so on.
How to achieve, to write the filter once and "use" it in many other components for "redefine" the default one.
One solution could be (not a nice one and produces an "spaghetty" like code) playing with the inheritance chain, as next:
Remove your "default" filter from the /Base.mc, so it would contains only the <%augment wrap
Create one component called for example: BaseSetDefault.mc and enter into it, your "default" filter and explicitly set the inheritance chain to the top level /Base.mc
<%augment wrap><% inner() %></%augment>
<%filter MyHead($arg)><h1 class="<% $arg %>"><% $yield->() %></h1></%filter>
<%flags>
extends => '/Base.mc'
</%flags>
Similarly, create an another component say called as /BaseSet2.mc and put here your "Set2" filters, like:
<%augment wrap><% inner() %></%augment>
<%filter MyHead($arg)><h2 id="<% $arg %>"><% $yield->() %></h2></%filter>
<%flags>
extends => '/Base.mc'
</%flags>
Now, in the /a/Base.mc and /b/Base.mc - everywhere, you want the "default" set, change the chain
<%flags>
extends => '/BaseSetDefault.mc'
</%flags>
and in the /c/Base.mc and /d/Base.mc - everywhere you want the "Set2" filters, use the
<%flags>
extends => '/BaseSet2.mc'
</%flags>
From now, the inheritance chain for /c/index.mc will be:
/c/index.mc -> /c/Base.mc -> /BaseSet2.mc -> /Base.mc
and the execution would be done as
/Base.mc -augment-> /BaseSet2.mc -augment-> /c/Base.mc -main-> /c/index.mc
^^^^^^^^^^^^ - defines the Set2 filters
and for the /a/index.mc inheritance chain
/a/index.mc -> /a/Base.mc -> /BaseSetDefault.mc -> /Base.mc
the execution
/Base.mc -augment-> /BaseSetDefault.mc -augment-> /a/Base.mc -main-> /a/index.mc
^^^^^^^^^^^^^^^^^^ - defines the "Default" filters
It is not very nice solution, but works...

Fields in CoffeeScript?

class #A
A_Function_Alias: => #my_function
my_function: =>
usage_of_alias: =>
#A_Function_Alias.call()
What i want, is usage_of_alias to be
usage_of_alias: =>
#A_Function_Alias
And have it behave the same. i.e. I want a more functional style syntax here. I've tried multiple combinations with no success. The fact that i have to evaluate my function to get the goodies inside bothers me.
One application would be instead of this:
event: => new Event(#my_function)
which is accessed as event.call().hi()
i could have some other declaration that would allow me to access event as event.hi()
Something that behaves more like a field instead of a property.
I'm thinking you want this:
class #A
my_function: => alert 'my function!'
A_Function_Alias: #::my_function
usage_of_alias: =>
#A_Function_Alias()
# some other code...
See what this compiles to here.
When evaluating a class # is the class constructor object, the class object itself. And the :: operator lets you drill into the prototype. So #::foo in a class body compiles to MyClass.prototype.foo.
So what this is doing is first making a normal instance method named my_function, defined on A.prototype.my_function. Then we make a new instance method named A_Function_Alias and directly assign the function from class objects prototype that we just created. Now my_function and A_Function_Alias both point to the exact same function object.
But as a more sane alternative, might I suggest a pattern that defines a private inner function that the class can use, and then assigning it more directly, without crazy the prototype accessing?
class #A
# Only code inside this class has access to this local function
innerFunc = -> alert 'my function!'
# Assign the inner funciton to any instance methods we want.
my_function: innerFunc
A_Function_Alias: innerFunc
usage_of_alias: =>
#A_Function_Alias()
# or
#my_function()
# or
innerFunc()
# they all do the same thing

How do I change the tag used by Zend_View_Helpers_FormErrors?

When using Zend_Form, if an element is not valid the form returns the errors by way of an unordered list. How do I change this to use paragraph tags instead?
I have attempted loading the Errors decorator for the elements and calling setOptions() to pass in a bunch of tags to replace the ul/li stuff being used by Zend_Form_Decorator_FormErrors, but that didn't work =/ Instead Zend_Form_Decorator_Errors just put the options as attribute/value pairs in the ul tag.
Instead of extending the Errors decorator I have extended the formErrors view helper, getting it to accept and process the options in the array. The formErrors view helper has setters to let me change the tags being used:
class My_View_Helper_FormErrors extends Zend_View_Helper_FormErrors
{
public function formErrors($errors, array $options = null)
{
if(key_exists('htmlElementStart', $options))
{
$this->setElementStart($options['htmlElementStart']);
unset($options['htmlElementStart']);
}
if(key_exists('htmlElementEnd', $options))
{
$this->setElementEnd($options['htmlElementEnd']);
unset($options['htmlElementEnd']);
}
if(key_exists('htmlElementSeparator', $options))
{
$this->setElementSeparator($options['htmlElementSeparator']);
unset($options['htmlElementSeparator']);
}
return parent::formErrors($errors, $options);
}
}
To pass options, I got the error decorator and setOptions() on it:
$element->getDecorator()->setOptions(
array(
'class' => 'error',
'htmlElementStart' => '<p%s>',
'htmlElementEnd' => '</p>',
'htmlElementSeparator' => '<br/>'
)
);
And tell the elements to load the helper path:
$element->getView()->addHelperPath('My/View/Helper', 'My_View_Helper');
Unfortunatelly, you cannot change the output format by just passing a bunch of options.
If you like to change this behaviour you have no choice but to write your own Errors decorator (most likely a derivative from the original Errors decorator). This new decorator has to have its render () method overwritten in order to be able to call your own view helper (instead of the formErrors helper which ZF uses by default).