diff --git a/README.md b/README.md index 63f604e..32cfd61 100644 --- a/README.md +++ b/README.md @@ -4,80 +4,167 @@ A [page object](https://martinfowler.com/bliki/PageObject.html) library for writing cleaner acceptance tests with [Capybara](https://github.com/teamcapybara/capybara). -## Examples +## Getting started + +Consider the following HTML snippet: ```html -
-

Page Title

-
- -
- lorem ipsum -
+ + +
+

Welcome

+
+ +
+ lorem ipsum +
+ + ``` -We can define a `HomePage` class to semantically wrap the above content. +To define a page object class to wrap this HTML snippet, we extend the +`Paper::Page` class. The only method our class needs to provide is `path`, this +tells Paper where to go when we want to visit the page. ```ruby -class HomePage < Paper::Page - component :header, Header, "header" - component :nav, Nav, "nav" - element :article, "article" +class WelcomePage < Paper::Page + def path + "/welcome" + end +end +``` + +This is how we visit our page object: + +```ruby +page = WelcomePage.visit +page.current? # true +``` + +Any Capybara API methods will be forwarded to the underlying node: + +```ruby +page.find("body > main").text # "lorem ipsum" +``` + +### Elements + +To make our page object more useful, we can define an element on our page +object class using the `element` macro. An element is simply a HTML element +that we expect to find on the page using a CSS selector. + +Let's define a `main` element: - def page - "/" +```ruby +class WelcomePage < Paper::Page + element :main, "body > main" + + def path + "/welcome" end end ``` -The `Header` class wraps the header component. +Now we can query the element on our page object: + +```ruby +page.has_main? # true +page.main.visible? # true +page.main.text # "lorem ipsum" +``` + +### Components + +For elements that can be shared across an number of page object classes, it may +be useful to define a reusable component by extending the `Paper::Component` +class. A component class can contain any number of elements or other +components: + ```ruby class Header < Paper::Component element :title, "h1" end ``` -The `Nav` class wraps the navigation component. +Our page object class can mount our component at a given CSS selector using the +`component` macro: ```ruby -class Nav < Paper::Component - components :items, NavItem, "a" +class WelcomePage < Paper::Page + component :header, Header, "header" + element :main, "body > main" + + def path + "/welcome" + end end ``` -The `NavItem` class wraps a single navigation item. +Querying a component on our page object is much the same as an element: ```ruby +page.has_header? # true +page.header.visible? # true +page.header.title.text # "Welcome" +``` + +## Example + +Let's look at a more complete example for our `WelcomePage`: + +```ruby +class Header < Paper::Component + element :title, "h1" +end + class NavItem < Paper::Component def selected? - @node[:class].include?("selected") + node[:class].include?("selected") + end +end + +class Nav < Paper::Component + components :items, NavItem, "a" +end + +class WelcomePage < Paper::Page + component :header, Header, "header" + component :nav, Nav, "nav" + element :main, "main" + + def path + "/welcome" end end ``` -We can then make assertions with these page objects. +We can use our page objects to write expressive tests: ```ruby feature "Home page" do - let(:home_page) { HomePage.new } + let(:home_page) { WelcomePage.visit } scenario "A user visits the home page" do - home_page.visit expect(home_page).to be_current - expect(home_page.header.title).to be("Page Title") + expect(home_page).to have_header + expect(home_page.header.title).to eq("Welcome") + expect(home_page).to have_nav + expect(home_page.nav).to have_items expect(home_page.nav.items.count).to be(3) + expect(home_page.nav.items[0].text).to eq("foo") expect(home_page.nav.items[1].text).to eq("bar") expect(home_page.nav.items[2].text).to eq("baz") - expect(home_page.nav.items[2]).to be_selected - expect(home_page.article.text).to eq("lorem ipsum") + expect(home_page.nav.items[0]).to be_selected + + expect(home_page.main.text).to eq("lorem ipsum") end end ``` diff --git a/spec/integration/page_spec.rb b/spec/integration/page_spec.rb index 6a39814..000f821 100644 --- a/spec/integration/page_spec.rb +++ b/spec/integration/page_spec.rb @@ -3,7 +3,7 @@ class Label < Paper::Component def required? - @node[:class].include?("required") + node[:class].include?("required") end end @@ -49,6 +49,9 @@ def sign_in(username, password) end it "has a form" do + expect(page.form).to have_fields + expect(page.form.fields.count).to be(2) + expect(page.form.fields[0].label.text).to eq("Username") expect(page.form.fields[0].label).to be_required expect(page.form.fields[0].input.value).to be_nil