Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc doc changes #254

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
29 changes: 26 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
/_build
/deps
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where third-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
hound-*.tar

# Temporary files for e.g. tests.
/tmp/

# Misc.
.DS_Store
doc/
screenshot-*.png
179 changes: 132 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Hound

For browser automation and writing integration tests in Elixir.

<a href="http://github.com/HashNuke/Hound" target="_parent">Source</a> | <a href="http://hexdocs.pm/hound" target="_parent">Documentation</a>

[![Build Status](https://travis-ci.org/HashNuke/hound.png?branch=master)](https://travis-ci.org/HashNuke/hound)
[![Module Version](https://img.shields.io/hexpm/v/hound.svg)](https://hex.pm/packages/hound)
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/hound/)
[![Total Download](https://img.shields.io/hexpm/dt/hound.svg)](https://hex.pm/packages/hound)
[![License](https://img.shields.io/hexpm/l/hound.svg)](https://github.com/HashNuke/hound/blob/master/LICENSE)
[![Last Updated](https://img.shields.io/github/last-commit/HashNuke/hound.svg)](https://github.com/HashNuke/hound/commits/master)

For browser automation and writing integration tests in Elixir.

## Features

Expand All @@ -16,72 +19,109 @@ For browser automation and writing integration tests in Elixir.

* Implements the WebDriver Wire Protocol.

**Internet Explorer may work under Selenium, but hasn't been tested.**
**Note:** Internet Explorer may work under Selenium, but hasn't been tested.

## Setup

#### Example
Hound requires Elixir 1.4 or higher.

##### ExUnit example
Add dependency to your mix project

```elixir
defmodule HoundTest do
use ExUnit.Case
use Hound.Helpers
{:hound, "~> 1.0"}
```

hound_session()
Start Hound in your `test/test_helper.exs` file **before** the `ExUnit.start()` line:

test "the truth", meta do
navigate_to("http://example.com/guestbook.html")
```elixir
Application.ensure_all_started(:hound)
ExUnit.start()
```

element = find_element(:name, "message")
fill_field(element, "Happy Birthday ~!")
submit_element(element)
When you run `mix test`, Hound is automatically started. __You'll need a webdriver server__ running, like Selenium Server or Chrome Driver. If you aren't sure what it is, then [read this](https://github.com/HashNuke/hound/wiki/Starting-a-webdriver-server).

assert page_title() == "Thank you"
end
### If you're using Phoenix

end
```
Ensure the server is started when your tests are run. In `config/test.exs` change the `server` option of your endpoint config to `true`:

Here's another [simple browser-automation example](https://github.com/HashNuke/hound/blob/master/notes/simple-browser-automation.md).
```elixir
config :hello_world_web, HelloWorldWeb.Endpoint,
http: [port: 4001],
server: true
```

## Setup
## Configure

Hound requires Elixir 1.0.4 or higher.
To configure Hound, use the project's `config/config.exs` file or equivalent (v0.14.0 and above). Here are some examples:

* Add dependency to your mix project
```elixir
# Start with selenium driver (default)
config :hound, driver: "selenium"
```

```elixir
# Use Chrome with the default driver (selenium)
config :hound, driver: "chrome"
```

{:hound, "~> 1.0"}
```elixir
# Start with default driver at port 1234 and use firefox
config :hound, port: 1234, browser: "firefox"
```

* Start Hound in your `test/test_helper.exs` file **before** the `ExUnit.start()` line:
```elixir
# Start Hound for PhantomJs
config :hound, driver: "phantomjs"
```

```elixir
Application.ensure_all_started(:hound)
ExUnit.start()
# Start Hound for ChromeDriver (default port 9515 assumed)
config :hound, driver: "chrome_driver"
```

When you run `mix test`, Hound is automatically started. __You'll need a webdriver server__ running, like Selenium Server or Chrome Driver. If you aren't sure what it is, then [read this](https://github.com/HashNuke/hound/wiki/Starting-a-webdriver-server).
```elixir
# Use Chrome in headless mode with ChromeDriver (default port 9515 assumed)
config :hound, driver: "chrome_driver", browser: "chrome_headless"
```

#### If you're using Phoenix
Ensure the server is started when your tests are run. In `config/test.exs` change the `server` option of your endpoint config to `true`:
```elixir
# Start Hound for remote PhantomJs server at port 5555
config :hound, driver: "phantomjs", host: "http://example.com", port: 5555
```

```elixir
config :hello_world_web, HelloWorldWeb.Endpoint,
http: [port: 4001],
server: true
# Define your application's host and port (defaults to "http://localhost:4001")
config :hound, app_host: "http://localhost", app_port: 4001
```

## Configure
```elixir
# Define how long the application will wait between failed attempts (in miliseconds)
config :hound, retry_time: 500
```

To configure Hound, use your `config/config.exs` file or equivalent.
```elixir
# Define http client settings
config :hound, http: [recv_timeout: :infinity, proxy: ["socks5", "127.0.0.1", "9050"]]
```

Example:
```elixir
# Define selenium hub settings
config :hound,
driver: "chrome_driver",
host: "http://localhost",
port: 32770,
path_prefix: "wd/hub/"
```

```config :hound, driver: "phantomjs"```
```elixir
# Set genserver timeout
config :hound, genserver_timeout: 480000
```

[More examples here](https://github.com/HashNuke/hound/blob/master/notes/configuring-hound.md).
```elixir
# Set default request retries
config :hound, retries: 3
```

## Usage

Expand All @@ -97,6 +137,55 @@ hound_session()

If you prefer to manually start and end sessions, use `Hound.start_session` and `Hound.end_session` in the setup and teardown blocks of your tests.

## Examples

### Through ExUnit

```elixir
defmodule HoundTest do
use ExUnit.Case
use Hound.Helpers

hound_session()

test "the truth", meta do
navigate_to("http://example.com/guestbook.html")

element = find_element(:name, "message")
fill_field(element, "Happy Birthday ~!")
submit_element(element)

assert page_title() == "Thank you"
end

end
```

### Simple Browser Automation

Make sure to configure Hound first, or you will get an error.

```elixir
Application.start :hound

defmodule Example do
use Hound.Helpers

def run do
Hound.start_session

navigate_to "http://akash.im"
IO.inspect page_title()

# Automatically invoked if the session owner process crashes
Hound.end_session
end
end

Example.run
```

More examples? [Checkout Hound's own test cases](https://github.com/HashNuke/hound/tree/master/test/helpers)

## Helpers

Expand All @@ -112,25 +201,21 @@ The documentation pages include examples under each function.
* [Session](http://hexdocs.pm/hound/Hound.Helpers.Session.html)
* [Window](http://hexdocs.pm/hound/Hound.Helpers.Window.html)

The docs are at <http://hexdocs.pm/hound>.

### More examples? [Checkout Hound's own test cases](https://github.com/HashNuke/hound/tree/master/test/helpers)

## FAQ

#### Can I run multiple browser sessions simultaneously
### Can I run multiple browser sessions simultaneously

Oh yeah ~! [Here is an example](https://github.com/HashNuke/hound/blob/master/test/multiple_browser_session_test.exs).

If you are running PhantomJs, take a look at the *Caveats* section below.

#### Can I run tests async?
### Can I run tests async?

Yes.

The number of tests you can run async at any point in time, depends on the number of sessions that your webdriver can maintain at a time. For Selenium Standalone, there seems to be a default limit of 15 sessions. You can set ExUnit's async option to limit the number of tests to run parallelly.

#### Will Hound guarantee an isolated session per test?
### Will Hound guarantee an isolated session per test?

Yes. A separate session is started for each test process.

Expand All @@ -157,4 +242,4 @@ You need a webdriver in order to run tests. We recommend `phantomjs` but any can

## Customary proclamation...

Copyright &copy; 2013-2015, Akash Manohar J, under the MIT License (basically, do whatever you want)
Copyright &copy; 2013-2021, Akash Manohar J, under the MIT License (basically, do whatever you want)
12 changes: 9 additions & 3 deletions lib/hound/browser.ex
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
defmodule Hound.Browser do
@moduledoc "Low level functions to customize browser behavior"
@moduledoc """
Low level functions to customize browser behavior.
"""

@type t :: Hound.BrowserLike.t

@callback default_user_agent :: String.t | atom

@callback default_capabilities(String.t) :: map

@doc "Creates capabilities for the browser and options, to be sent to the webdriver"
@doc """
Creates capabilities for the browser and options, to be sent to the webdriver.
"""
@spec make_capabilities(t, map | Keyword.t) :: map
def make_capabilities(browser_name, opts \\ []) do
browser = browser(browser_name)
Expand All @@ -25,7 +29,9 @@ defmodule Hound.Browser do
|> Map.merge(additional_capabilities)
end

@doc "Returns a user agent string"
@doc """
Returns a user agent string.
"""
@spec user_agent(String.t | atom) :: String.t
def user_agent(ua) when is_binary(ua), do: ua

Expand Down
2 changes: 1 addition & 1 deletion lib/hound/browsers/chrome_headless.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ defmodule Hound.Browser.ChromeHeadless do
def default_user_agent, do: :chrome

def default_capabilities(ua) do
%{chromeOptions: %{"args" => ["--user-agent=#{ua}", "--headless", "--disable-gpu"]}}
%{chromeOptions: %{"args" => ["--user-agent=#{ua}", "--headless", "--disable-gpu"]}}
end
end
6 changes: 3 additions & 3 deletions lib/hound/element.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Hound.Element do
@moduledoc """
A representation of a web element
A representation of a web element.
"""

defstruct uuid: nil
Expand All @@ -11,14 +11,14 @@ defmodule Hound.Element do
@type selector :: t | matcher

@doc """
Returns true if the argument is an Element
Returns true if the argument is an Element.
"""
@spec element?(any) :: boolean
def element?(%__MODULE__{}), do: true
def element?(_), do: false

@doc """
Returns an element from a driver element response
Returns an element from a driver element response.
"""
@spec from_response(map) :: t
def from_response(element) when is_map(element) do
Expand Down
6 changes: 5 additions & 1 deletion lib/hound/matchers.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
defmodule Hound.Matchers do
@moduledoc "Text and element matchers"
@moduledoc """
Text and element matchers.
"""

import Hound.Helpers.Page
import Hound.Helpers.Element
Expand All @@ -8,6 +10,7 @@ defmodule Hound.Matchers do
Returns true if text is found on the page.

visible_in_page?(~r/Paragraph/)

"""
@spec visible_in_page?(Regex.t) :: boolean
def visible_in_page?(pattern) do
Expand Down Expand Up @@ -37,6 +40,7 @@ defmodule Hound.Matchers do

element?(:class, "block")
element?(:id, "foo")

"""
@spec element?(Hound.Element.strategy, String.t) :: boolean
def element?(strategy, selector) do
Expand Down
2 changes: 2 additions & 0 deletions lib/hound/metadata.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Hound.Metadata do
@moduledoc """
Metadata allows to pass and extract custom data through.

This can be useful if you need to identify sessions.

The keys and values must be serializable using `:erlang.term_to_binary/1`.
Expand Down Expand Up @@ -50,6 +51,7 @@ defmodule Hound.Metadata do

@doc """
Extracts and parses the metadata contained in a user agent string.

If the user agent does not contain any metadata, an empty map is returned.
"""
@spec parse(String.t) :: %{String.t => String.t}
Expand Down
Loading