{"id":15046917,"url":"https://github.com/nullobject/mache","last_synced_at":"2025-08-03T10:30:44.514Z","repository":{"id":17911610,"uuid":"77033708","full_name":"nullobject/mache","owner":"nullobject","description":"A library for writing cleaner and more expressive acceptance tests using page objects.","archived":false,"fork":false,"pushed_at":"2022-03-28T22:25:28.000Z","size":110,"stargazers_count":40,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-06-15T03:47:16.104Z","etag":null,"topics":["capybara","library","page-object","rspec","ruby","testing"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nullobject.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-12-21T08:39:53.000Z","updated_at":"2022-08-23T17:43:09.000Z","dependencies_parsed_at":"2022-08-07T09:00:23.483Z","dependency_job_id":null,"html_url":"https://github.com/nullobject/mache","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/nullobject/mache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fmache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fmache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fmache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fmache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nullobject","download_url":"https://codeload.github.com/nullobject/mache/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nullobject%2Fmache/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267453949,"owners_count":24089847,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["capybara","library","page-object","rspec","ruby","testing"],"created_at":"2024-09-24T20:53:44.654Z","updated_at":"2025-08-03T10:30:44.192Z","avatar_url":"https://github.com/nullobject.png","language":"Ruby","readme":"# Mâché\n\n[![Build Status](https://travis-ci.org/nullobject/mache.svg?branch=master)](https://travis-ci.org/nullobject/mache)\n\nMâché (pronounced \"mash-ay\") is a tool that helps you to write cleaner and more\nexpressive acceptance tests for your Ruby web applications using page objects.\n\n## Table of Contents\n\n* [What is a Page Object?](#what-is-a-page-object)\n* [Getting Started](#getting-started)\n  * [Elements](#elements)\n  * [Components](#components)\n  * [Helpers](#helpers)\n* [Examples](#examples)\n* [Documentation](#documentation)\n* [License](#license)\n\n## What is a Page Object?\n\nA [page object](https://martinfowler.com/bliki/PageObject.html) is a data\nstructure that provides an interface to your web application for the purposes\nof test automation. For example, it could represent a single HTML page, or\nperhaps even a fragment of HTML on a page.\n\nFrom [Martin Fowler](https://martinfowler.com/bliki/PageObject.html):\n\n\u003e A page object wraps an HTML page, or fragment, with an application-specific\n\u003e API, allowing you to manipulate page elements without digging around in the\n\u003e HTML.\n\n[Capybara](https://github.com/teamcapybara/capybara) can get us part of the way\nthere. It allows us to work with an API rather than manipulating the HTML\ndirectly, but what it provides isn't an *application specific* API. It gives us\nlow-level API methods like `find`, `fill_in`, and `click_button`, but it\ndoesn't provide us with high-level methods to do things like \"sign in to the\napp\" or \"click the Dashboard item in the navigation bar\".\n\nThis is where page objects come in. Using Mâché, we can define a page object\nclass called `SignInPage` and use it any time we want to automate\nauthenticating with our app. It could handle visiting the sign in page,\nentering the user's credentials, and clicking the \"Sign in\" button.\n\n## Getting Started\n\nLet's dive straight in and take a look at an example. Consider the following\nHTML fragment for the welcome page in our app:\n\n```html\n\u003chtml\u003e\n  \u003cbody\u003e\n    \u003cheader\u003e\n      \u003ch1\u003eWelcome\u003c/h1\u003e\n      \u003cdiv id=\"flash\" class=\"notice\"\u003elorem ipsum\u003c/div\u003e\n    \u003c/header\u003e\n    \u003cnav\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"/foo\" class=\"selected\"\u003efoo\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"/bar\"\u003ebar\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"/baz\"\u003ebaz\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/nav\u003e\n    \u003cmain\u003e\n      lorem ipsum\n    \u003c/main\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nTo define a `WelcomePage` page object class to wrap this HTML page, we extend\nthe `Mache::Page` class. The only method our class needs to provide is `path`,\nthis tells Mâché where to go when we want to visit the page:\n\n```ruby\nrequire \"mache\"\n\nclass WelcomePage \u003c Mache::Page\n  def path\n    \"/welcome\"\n  end\nend\n```\n\nWe can visit our welcome page using our page object:\n\n```ruby\npage = WelcomePage.visit\npage.current? # true\n```\n\nMâché also handily exposes the Capybara API on our page object:\n\n```ruby\npage.find(\"main\").text # \"lorem ipsum\"\n```\n\n### Elements\n\nTo make our page object more useful, we can define an element on our page\nobject class using the `element` macro. An element is simply an HTML element\nthat we expect to find on the page using a CSS selector.\n\nLet's define a `main` element to represent the main section of our HTML page:\n\n```ruby\nrequire \"mache\"\n\nclass WelcomePage \u003c Mache::Page\n  element :main, \"main\"\n\n  def path\n    \"/welcome\"\n  end\nend\n```\n\nWe can query the `main` element as an attribute of our page object:\n\n```ruby\npage.main.text # \"lorem ipsum\"\n```\n\n### Components\n\nFor elements that can be shared across any number of page object classes it may\nbe useful to define a reusable component by extending the `Mache::Node` class.\nA component can contain any number of elements (or even other components).\n\nLet's define a `Header` component to represent the header of our HTML page:\n\n```ruby\nrequire \"mache\"\n\nclass Header \u003c Mache::Node\n  element :title, \"h1\"\nend\n```\n\nWe can mount the `Header` component in our page object class at a given CSS\nselector using the `component` macro:\n\n```ruby\nrequire \"mache\"\n\nclass WelcomePage \u003c Mache::Page\n  component :header, Header, \"header\"\n  element :main, \"main\"\n\n  def path\n    \"/welcome\"\n  end\nend\n```\n\nQuerying a component of our page object is much the same as with an element:\n\n```ruby\npage.header.title.text # \"Welcome\"\n```\n\n### Helpers\n\nMâché provides helpers for testing Rails apps.\n\n#### Flash\n\nThe `Flash` helper provides methods for testing flash messages. First define a\nflash in your page object class:\n\n```ruby\nrequire \"mache\"\nrequire \"mache/helpers/rails\"\n\nclass WelcomePage \u003c Mache::Page\n  include Mache::Helpers::Rails::Flash\n\n  flash \"#flash\"\nend\n```\n\nThen you can query the flash on your page object:\n\n```ruby\npage.has_message?(:success, \"lorem ipsum\")\npage.has_message?(:success, /lorem ipsum/)\n```\n\nThere are even convenience matchers for the common types of flash messages:\n\n```ruby\npage.has_success_message?(\"lorem ipsum\")\npage.has_notice_message?(\"lorem ipsum\")\npage.has_alert_message?(\"lorem ipsum\")\npage.has_error_message?(\"lorem ipsum\")\n```\n\n#### Routes\n\nThe `Routes` helper mixes the Rails URL helpers into your page object class.\nThis allows you to use the `*_path` and `*_url` methods as you normally would\nin your Rails.\n\n```ruby\nrequire \"mache\"\nrequire \"mache/helpers/rails\"\n\nclass WelcomePage \u003c Mache::Page\n  include Mache::Helpers::Rails::Routes\n\n  def path\n    welcome_path\n  end\nend\n```\n\n## Examples\n\nLet's look at an example of an acceptance test for our `WelcomePage`. Note that\nthe `Header`, `NavItem`, and `Nav` components can be reused in any other page\nobject classes we may define later for our web application.\n\n```ruby\nrequire \"mache\"\nrequire \"mache/helpers/rails\"\n\nclass Header \u003c Mache::Node\n  element :title, \"h1\"\nend\n\nclass NavItem \u003c Mache::Node\n  def selected?\n    node[:class].include?(\"selected\")\n  end\nend\n\nclass Nav \u003c Mache::Node\n  components :items, NavItem, \"a\"\n\n  def selected_item\n    items.find(\u0026:selected?)\n  end\nend\n\nclass WelcomePage \u003c Mache::Page\n  include Mache::Helpers::Rails::Flash\n  include Mache::Helpers::Rails::Routes\n\n  component :header, Header, \"header\"\n  component :nav, Nav, \"nav\"\n  element :main, \"main\"\n  flash \"#flash\"\n\n  def path\n    welcome_path\n  end\nend\n\nfeature \"Welcome page\" do\n  let(:home_page) { WelcomePage.visit }\n\n  scenario \"A user visits the welcome page\" do\n    expect(home_page).to be_current\n\n    # header\n    expect(home_page).to have_header\n    expect(home_page.header.title).to eq(\"Welcome\")\n\n    # nav\n    expect(home_page).to have_nav\n    expect(home_page.nav).to have_items\n    expect(home_page.nav.items.count).to be(3)\n    expect(home_page.nav.items[0].text).to eq(\"foo\")\n    expect(home_page.nav.items[1].text).to eq(\"bar\")\n    expect(home_page.nav.items[2].text).to eq(\"baz\")\n    expect(home_page.nav.selected_item).to eq(\"foo\")\n\n    # main\n    expect(home_page.main.text).to eq(\"lorem ipsum\")\n\n    # flash\n    expect(home_page).to have_flash\n    expect(home_page).to have_notice_message(\"lorem ipsum\")\n  end\nend\n```\n\n## Documentation\n\nRead the [API reference](http://www.rubydoc.info/gems/mache) on RubyDoc.\n\n## License\n\nMâché is licensed under the [MIT License](/LICENSE.md).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnullobject%2Fmache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnullobject%2Fmache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnullobject%2Fmache/lists"}