Skip to content

Commit

Permalink
Merge pull request #31 from tgaff/tg/within_section
Browse files Browse the repository at this point in the history
Section#within - solution for #21
  • Loading branch information
luke-hill committed Apr 6, 2020
2 parents 88bc16e + 90b3c02 commit 34a1ec9
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 12 deletions.
47 changes: 38 additions & 9 deletions README.md
Expand Up @@ -15,7 +15,7 @@ Make sure to add your project/company to https://github.com/site-prism/site_pris

We love it when people want to get involved with our Open Source Project.

We have a brief set of setup docs [HERE](https://github.com/site-prism/site_prism/blob/master/development_setup.md)
We have a brief set of setup docs [HERE](https://github.com/site-prism/site_prism/blob/master/HACKING.md)

## Supported Rubies / Browsers

Expand Down Expand Up @@ -498,7 +498,7 @@ end

#### Waiting for an element to become visible

A method that gets added by calling `element` is the
A method that gets added by calling `element` is the
`wait_until_<element_name>_visible` method. Calling this method will
cause the test to wait for Capybara's default wait time for the element
to become visible. You can customise the wait time by supplying a number
Expand Down Expand Up @@ -921,14 +921,13 @@ end

##### Accessing section elements using a block

A Section can be scoped so it is only accessible inside a block. This is
You can execute a block within the context of a Section. This is
similar to Capybara's `within` method and allows for shorter test code
particularly with nested sections. Some of this test code can be
made a little prettier by simply passing a block in.
particularly with nested sections. Test code that might have to repeat the block name can be shortened up this way.

```ruby
Then(/^the home page menu contains a link to the various search functions$/) do
@home.menu do |menu|
@home.menu.within do |menu|
expect(menu).to have_search
expect(menu.search['href']).to include('google.com')
expect(menu).to have_images
Expand All @@ -937,6 +936,16 @@ Then(/^the home page menu contains a link to the various search functions$/) do
end
```

Note that on an individual section it's possible to pass a block directly to the section without using `within`. Because the block is executed only during `Section` initialization this won't work when accessing a single Section from an array of Sections. For that reason we recommend using `within` which works in either case.

```ruby
Then(/^the home page menu contains a link to the various search functions$/) do
@home.menu do |menu| # possible, but prefer: `@home.menu.within`
expect(menu).to have_search
end
end
```

#### Getting a section's parent

It is possible to ask a section for its parent (page, or section if this
Expand Down Expand Up @@ -1175,7 +1184,7 @@ This allows for pretty tests ...
```ruby
Then(/^there are lots of search_results$/) do
expect(@results_page.search_results.size).to eq(10)

@home.search_results.each do |result|
expect(result).to have_title
expect(result.blurb.text).not_to be_empty
Expand All @@ -1191,6 +1200,26 @@ element. So if the css selector finds 3 `li` elements, calling
`search_results` will return an array containing 3 instances of
`SearchResults`, each with one of the `li` elements as it's root element.

##### Accessing Within a Collection of Sections

When using an iterator such as `each` to pass a block through to a collection of sections it is possible to skip using `within`. However some caution is warranted when accessing the Sections directly from an array, as the block can only be executed when the section is being initialized. The following does not work:

```rb
@home.search_results.first do |result|
# This block is silently ignored.
expect(result).to have_title
end
```
Instead use `within` to access the inner-context of the Section.

```rb
@home.search_results.first.within do |result|
# This block is run within the context of the Section.
expect(result).to have_title
end
```


#### Anonymous Section Collections

You can define collections of anonymous sections the same way you would
Expand Down Expand Up @@ -1387,7 +1416,7 @@ instance of the class when created.

### Skipping load Validations

Defined load validations can be skipped for one `load` call by
Defined load validations can be skipped for one `load` call by
passing in `with_validations: false`.

```ruby
Expand Down Expand Up @@ -1654,7 +1683,7 @@ as per the code below
Capybara.configure do |config|
config.default_max_wait_time = 11 #=> Wait up to 11 seconds for all queries to fail
# or if you don't want to ever wait
config.default_max_wait_time = 0 #=> Don't ever wait!
config.default_max_wait_time = 0 #=> Don't ever wait!
end
```

Expand Down
5 changes: 5 additions & 0 deletions features/section.feature
Expand Up @@ -13,6 +13,11 @@ Feature: Page Sections
Then I can access elements within the section using a block
But I cannot access elements that are not in the section using a block

Scenario: access elements in the section by passing a block to within
When I navigate to the home page
Then I can execute elements in the context of a section by passing a block to within
But I cannot access elements that are not in the section using within

Scenario: section in a section
When I navigate to the nested section page
Then I can see a section in a section
Expand Down
4 changes: 4 additions & 0 deletions features/sections.feature
Expand Up @@ -15,3 +15,7 @@ Feature: Interaction with groups of elements
Scenario: anonymous sections collection
When I navigate to the nested section page
Then I can see a collection of anonymous sections

Scenario: can access elements in the section by passing a block to within
When I navigate to the nested section page
Then I can execute in the context of each section by passing a block to within
28 changes: 28 additions & 0 deletions features/step_definitions/section_steps.rb
Expand Up @@ -12,13 +12,35 @@
end

Then('I can access elements within the section using a block') do
block_inner_executions = 0

expect(@test_site.home).to have_people

@test_site.home.people do |section|
block_inner_executions += 1

expect(section.headline.text).to eq('People')

expect(section).to have_individuals(count: 4)
end

expect(block_inner_executions).to eq 1
end

Then('I can execute elements in the context of a section by passing a block to within') do
block_inner_executions = 0

expect(@test_site.home).to have_people

@test_site.home.people.within do |section|
block_inner_executions += 1

expect(section.headline.text).to eq('People')

expect(section).to have_individuals(count: 4)
end

expect(block_inner_executions).to eq 1
end

Then('I cannot access elements that are not in the section using a block') do
Expand All @@ -27,6 +49,12 @@
end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
end

Then('I cannot access elements that are not in the section using within') do
expect do
@test_site.home.people.within { |section| expect(section).to have_not_here }
end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
end

Then('access to elements is constrained to those within the section') do
expect(@test_site.home).to have_css('.welcome')

Expand Down
28 changes: 28 additions & 0 deletions features/step_definitions/sections_steps.rb
Expand Up @@ -17,3 +17,31 @@

expect(anonymous_sections.size).to eq(2)
end

Then('I can execute in the context of each section by passing a block to within') do
block_inner_executions = 0

expect(@test_site.nested_sections).to have_search_results(count: 4)

@test_site.nested_sections.search_results.first.within do |sec|
block_inner_executions += 1

expect(sec).to be_an_instance_of(SearchResults)
expect(sec).to have_text('Result 0')
end

@test_site.nested_sections.search_results.last.within do |sec|
block_inner_executions += 1
expect(sec).to be_an_instance_of(SearchResults)
expect(sec).to have_text('Result 3')
end

@test_site.nested_sections.search_results.each do |sec|
block_inner_executions += 1

expect(sec).to be_an_instance_of(SearchResults)
expect(sec).to have_text(/Result/)
end

expect(block_inner_executions).to eq 6
end
2 changes: 2 additions & 0 deletions features/support/env.rb
Expand Up @@ -15,6 +15,8 @@
require_relative 'js_helper'
require_relative 'sections/all'

SimpleCov.start if defined? SimpleCov

Capybara.register_driver :site_prism do |app|
browser = ENV.fetch('browser', 'firefox').to_sym
# Needed whilst we support Webdriver 3.x (Can be removed once we only support 4.x)
Expand Down
8 changes: 6 additions & 2 deletions lib/site_prism/section.rb
Expand Up @@ -25,10 +25,14 @@ def self.default_search_arguments
nil
end

def initialize(parent, root_element)
def initialize(parent, root_element, &block)
@parent = parent
@root_element = root_element
Capybara.within(@root_element) { yield(self) } if block_given?
within(&block) if block_given?
end

def within
Capybara.within(@root_element) { yield(self) }
end

# Capybara::DSL module "delegates" Capybara methods to the "page" method
Expand Down
3 changes: 2 additions & 1 deletion site_prism.gemspec
Expand Up @@ -22,10 +22,11 @@ Gem::Specification.new do |s|
s.files = Dir.glob('lib/**/*') + %w[LICENSE.md README.md]
s.require_path = 'lib'
s.add_dependency 'addressable', ['~> 2.5']
s.add_dependency 'capybara', ['~> 3.3']
s.add_dependency 'capybara', ['<= 3.29']
s.add_dependency 'site_prism-all_there', ['>= 0.3.1', '< 1.0']

s.add_development_dependency 'cucumber', ['~> 3.1']
s.add_development_dependency 'pry-byebug'
s.add_development_dependency 'rake', ['>= 12.3']
s.add_development_dependency 'rspec', ['~> 3.8']
s.add_development_dependency 'rubocop', ['~> 0.75.0']
Expand Down
11 changes: 11 additions & 0 deletions spec/site_prism/section_spec.rb
Expand Up @@ -77,6 +77,7 @@ class SingleSection < SitePrism::Section
it { is_expected.to respond_to(:wait_until_section_visible) }
it { is_expected.to respond_to(:wait_until_section_invisible) }
it { is_expected.to respond_to(:all_there?) }
it { is_expected.to respond_to(:within) }
end

context 'when second argument is not a Class but a block is given' do
Expand All @@ -96,6 +97,7 @@ class SingleSection < SitePrism::Section
it { is_expected.to respond_to(:wait_until_anonymous_section_visible) }
it { is_expected.to respond_to(:wait_until_anonymous_section_invisible) }
it { is_expected.to respond_to(:all_there?) }
it { is_expected.to respond_to(:within) }
end

context 'when second argument is a Class and a block is given' do
Expand All @@ -115,6 +117,7 @@ class SingleSection < SitePrism::Section
it { is_expected.to respond_to(:wait_until_anonymous_section_visible) }
it { is_expected.to respond_to(:wait_until_anonymous_section_invisible) }
it { is_expected.to respond_to(:all_there?) }
it { is_expected.to respond_to(:within) }
end

context 'when second argument is not a Class and no block is given' do
Expand Down Expand Up @@ -278,6 +281,14 @@ class NewPage < SitePrism::Page
end
end

describe '#within' do
it 'passes the block to Capybara#within' do
expect(Capybara).to receive(:within).with(locator)

section_without_block.within { :noop }
end
end

describe '#visible?' do
it 'delegates through root_element' do
expect(locator).to receive(:visible?)
Expand Down

0 comments on commit 34a1ec9

Please sign in to comment.