# Mache [![Build Status](https://travis-ci.org/nullobject/mache.svg?branch=master)](https://travis-ci.org/nullobject/mache) A [page object](https://martinfowler.com/bliki/PageObject.html) library for writing cleaner acceptance tests with [Capybara](https://github.com/teamcapybara/capybara). ## Getting started Consider the following HTML snippet: ```html

Welcome

lorem ipsum
``` To define a page object class to wrap this HTML snippet, we extend the `Mache::Page` class. The only method our class needs to provide is `path`, this tells Mache where to go when we want to visit the page. ```ruby class WelcomePage < Mache::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: ```ruby class WelcomePage < Mache::Page element :main, "body > main" def path "/welcome" end end ``` 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 `Mache::Component` class. A component class can contain any number of elements or other components: ```ruby class Header < Mache::Component element :title, "h1" end ``` Our page object class can mount our component at a given CSS selector using the `component` macro: ```ruby class WelcomePage < Mache::Page component :header, Header, "header" element :main, "body > main" def path "/welcome" end end ``` 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 < Mache::Component element :title, "h1" end class NavItem < Mache::Component def selected? node[:class].include?("selected") end end class Nav < Mache::Component components :items, NavItem, "a" end class WelcomePage < Mache::Page component :header, Header, "header" component :nav, Nav, "nav" element :main, "main" def path "/welcome" end end ``` We can use our page objects to write expressive tests: ```ruby feature "Home page" do let(:home_page) { WelcomePage.visit } scenario "A user visits the home page" do expect(home_page).to be_current 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[0]).to be_selected expect(home_page.main.text).to eq("lorem ipsum") end end ```