Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jhthorsen/test-mojo-role-selenium

Test::Mojo in a real browser
https://github.com/jhthorsen/test-mojo-role-selenium

Last synced: 27 days ago
JSON representation

Test::Mojo in a real browser

Awesome Lists containing this project

README

        

# NAME

Test::Mojo::Role::Selenium - Test::Mojo in a real browser

# SYNOPSIS

## External app

use Mojo::Base -strict;
use Test::More;

$ENV{MOJO_SELENIUM_BASE_URL} ||= 'http://mojolicious.org';
$ENV{MOJO_SELENIUM_DRIVER} ||= 'Selenium::Chrome';

my $t = Test::Mojo->with_roles("+Selenium")->new->setup_or_skip_all;

$t->navigate_ok('/perldoc')
->live_text_is('a[href="#GUIDES"]' => 'GUIDES');

$t->driver->execute_script(qq[document.querySelector("form").removeAttribute("target")]);
$t->element_is_displayed("input[name=q]")
->send_keys_ok("input[name=q]", ["render", \"return"]);

$t->wait_until(sub { $_->get_current_url =~ qr{q=render} })
->live_value_is("input[name=search]", "render");

done_testing;

## Internal app

use Mojo::Base -strict;
use Test::More;

my $t = Test::Mojo->with_roles("+Selenium")->new("MyApp")->setup_or_skip_all;

# All the standard Test::Mojo methods are available
ok $t->isa("Test::Mojo");
ok $t->does("Test::Mojo::Role::Selenium");

$t->navigate_ok("/")
->status_is(200)
->header_is("Server" => "Mojolicious (Perl)")
->text_is("div#message" => "Hello!")
->live_text_is("div#message" => "Hello!")
->live_element_exists("nav")
->element_is_displayed("nav")
->active_element_is("input[name=q]")
->send_keys_ok("input[name=q]", "Mojo")
->capture_screenshot;

$t->submit_ok("form")
->status_is(200)
->current_url_like(qr{q=Mojo})
->live_element_exists("input[name=q][value=Mojo]");

$t->click_ok("nav a.logo")->status_is(200);

done_testing;

# DESCRIPTION

[Test::Mojo::Role::Selenium](https://metacpan.org/pod/Test%3A%3AMojo%3A%3ARole%3A%3ASelenium) is a role that extends [Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo) with
additional methods which checks behaviour in a browser. All the heavy lifting
is done by [Selenium::Remote::Driver](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3ADriver).

Some of the [Selenium::Remote::Driver](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3ADriver) methods are available directly in this
role, while the rest are available through the object held by the ["driver"](#driver)
attribute. Please create an issue if you think more tests or methods should be
provided directly by [Test::Mojo::Role::Selenium](https://metacpan.org/pod/Test%3A%3AMojo%3A%3ARole%3A%3ASelenium).

# OPTIONAL DEPENDENCIES

[Selenium::Remote::Driver](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3ADriver) require some external dependencies to work. Here
are a quick intro to install some of the dependencies to make this module work.

- [Selenium::Chrome](https://metacpan.org/pod/Selenium%3A%3AChrome)

# macOS
$ brew install chromedriver

# Ubuntu
$ sudo apt-get install chromium-chromedriver

# Run tests
$ MOJO_SELENIUM_DRIVER=Selenium::Chrome prove -l

# CAVEAT

["tx" in Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo#tx) is only populated, if the request went through an ["Internal app"](#internal-app).
This means that methods such as ["header\_is" in Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo#header_is) will not work or
probably fail completely when testing an ["External app"](#external-app).

# ENVIRONMENT VARIABLES

## MOJO\_SELENIUM\_BASE\_URL

Setting this variable will make this test send the requests to a remote server,
instead of starting a local server. Note that this will disable [Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo)
methods such as ["status\_is"](#status_is), since ["tx" in Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo#tx) will not be set. See
also ["CAVEAT"](#caveat).

This variable will get the value of ["TEST\_SELENIUM"](#test_selenium) if it looks like a URL.

## MOJO\_SELENIUM\_TEST\_HOST

In some cases you may want to override the host of your test server, when
running Selenium on a separate server or in a pod-style networking environment
this still retains the automatically generated port. This will not disable the
[Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo) methods.

## MOJO\_SELENIUM\_DRIVER

This variable can be set to a classname, such as [Selenium::Chrome](https://metacpan.org/pod/Selenium%3A%3AChrome) which will
force the selenium driver. It can also be used to pass on arguments to the
driver's constructor. Example:

MOJO_SELENIUM_DRIVER='Selenium::Remote::Driver&browser_name=firefox&port=4444'

The arguments will be read using ["parse" in Mojo::Parameters](https://metacpan.org/pod/Mojo%3A%3AParameters#parse), which means they
follow standard URL format rules.

## TEST\_SELENIUM

This variable must be set to a true value for ["setup\_or\_skip\_all"](#setup_or_skip_all) to not skip
this test. Will also set ["MOJO\_SELENIUM\_BASE\_URL"](#mojo_selenium_base_url) if it looks like an URL.

# ATTRIBUTES

## driver

$driver = $self->driver;

An instance of [Selenium::Remote::Driver](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3ADriver).

## driver\_args

$hash = $self->driver_args;
$self = $self->driver_args({driver_class => "Selenium::Chrome"});

Used to set args passed on to the ["driver"](#driver) on construction time. In addition,
a special key "driver\_class" can be set to use another driver class, than the
default.

Note that the environment variavble `MOJO_SELENIUM_DRIVER` can also be used to
override the driver class.

## screenshot\_directory

$path = $self->screenshot_directory;
$self = $self->screenshot_directory(File::Spec->tmpdir);

Where screenshots are saved.

## screenshots

$array = $self->screenshots;

Holds an array ref with paths to all the screenshots taken with
["capture\_screenshot"](#capture_screenshot).

# METHODS

## active\_element\_is

$self = $self->active_element_is("input[name=username]");

Checks that the current active element on the page match the selector.

## capture\_screenshot

$self = $self->capture_screenshot;
$self = $self->capture_screenshot("%t-page-x");
$self = $self->capture_screenshot("%0-%t-%n"); # default

Capture screenshot to ["screenshot\_directory"](#screenshot_directory) with filename specified by the
input format. The format supports these special strings:

Format | Description
-------|----------------------
%t | Start time for script
%0 | Name of script
%n | Auto increment

## click\_ok

$self = $self->click_ok("a");
$self = $self->click_ok;

Click on an element matching the selector or click on the currently active
element.

## current\_url\_is

$self = $self->current_url_is("http://mojolicious.org/");
$self = $self->current_url_is("/whatever");

Test the current browser URL against an absolute URL. A relative URL will be
converted to an absolute URL, using ["MOJO\_SELENIUM\_BASE\_URL"](#mojo_selenium_base_url).

## current\_url\_like

$self = $self->current_url_like(qr{/whatever});

Test the current browser URL against a regex.

## element\_is\_displayed

$self = $self->element_is_displayed("nav");

Test if an element is displayed on the web page.

See ["is\_displayed" in Selenium::Remote::WebElement](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWebElement#is_displayed).

## element\_is\_hidden

$self = $self->element_is_hidden("nav");

Test if an element is hidden on the web page.

See ["is\_hidden" in Selenium::Remote::WebElement](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWebElement#is_hidden).

## go\_back

$self = $self->go_back;

Equivalent to hitting the back button on the browser.

See ["go\_back" in Selenium::Remote::Driver](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3ADriver#go_back).

## go\_forward

$self = $self->go_forward;

Equivalent to hitting the forward button on the browser.

See ["go\_forward" in Selenium::Remote::Driver](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3ADriver#go_forward).

## if\_tx

$self = $self->if_tx(sub { ... }, @args);
$self = $self->if_tx($method, @args);

Call either a code ref or a method on `$self` if ["tx" in Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo#tx) is defined.
`tx()` is undefined if ["navigate\_ok"](#navigate_ok) is called on an external resource.

Examples:

$self->if_tx(status_is => 200);

## live\_element\_count\_is

$self = $self->live_element_count_is("a", 12);

Checks that the selector finds the correct number of elements in the browser.

See ["element\_count\_is" in Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo#element_count_is).

## live\_element\_exists

$self = $self->live_element_exists("div.content");

Checks that the selector finds an element in the browser.

See ["element\_exists" in Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo#element_exists).

## live\_element\_exists\_not

$self = $self->live_element_exists_not("div.content");

Checks that the selector does not find an element in the browser.

$self = $self->live_element_exists("div.foo");

See ["element\_exists\_not" in Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo#element_exists_not).

## live\_text\_is

$self = $self->live_text_is("div.name", "Mojo");

Checks text content of the CSS selectors first matching HTML element in the
browser matches the given string.

## live\_text\_like

$self = $self->live_text_is("div.name", qr{Mojo});

Checks text content of the CSS selectors first matching HTML element in the
browser matches the given regex.

## live\_value\_is

$self = $self->live_value_is("div.name", "Mojo");

Checks value of the CSS selectors first matching HTML element in the browser
matches the given string.

## live\_value\_like

$self = $self->live_value_like("div.name", qr{Mojo});

Checks value of the CSS selectors first matching HTML element in the browser
matches the given regex.

## navigate\_ok

$self = $self->navigate_ok("/");
$self = $self->navigate_ok("http://mojolicious.org/");

Open a browser window and go to the given location.

## new

$self = $class->new;
$self = $class->new($app);

Same as ["new" in Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo#new), but will not build `$app` if
["MOJO\_SELENIUM\_BASE\_URL"](#mojo_selenium_base_url) is set.

## refresh

$self = $self->refresh;

Equivalent to hitting the refresh button on the browser.

See ["refresh" in Selenium::Remote::Driver](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3ADriver#refresh).

## send\_keys\_ok

$self->send_keys_ok("input[name=name]", ["web", \"space", "framework"]);
$self->send_keys_ok(undef, [\"return"]);

Used to send keys to a given element. Scalar refs will be sent as
[Selenium::Remote::WDKeys](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWDKeys) strings. Passing in `undef` as the first argument
will cause the keys to be sent to the currently active element.

List of some of the special keys:

- alt, control, shift
- right\_arrow, down\_arrow, left\_arrow, up\_arrow
- backspace, clear, delete, enter, return, escape, space, tab
- f1, f2, ..., f12
- command\_meta, pause

## set\_window\_size

$self = $self->set_window_size([$width, $height]);
$self = $self->set_window_size([375, 667]);

Set the browser window size.

## setup\_or\_skip\_all

$self = $self->setup_or_skip_all;

Will ["skip\_all" in skip all#Test::More](https://metacpan.org/pod/skip%20all%23Test%3A%3AMore#skip_all) tests unless `TEST_SELENIUM` is set and
and ["driver"](#driver) can be built.

Will also set ["MOJO\_SELENIUM\_BASE\_URL"](#mojo_selenium_base_url) if `TEST_SELENIUM` looks like a URL.

## submit\_ok

$self = $self->submit_ok("form");

Submit a form, either by selector or the current active form.

See ["submit" in Selenium::Remote::WebElement](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWebElement#submit).

## toggle\_checked\_ok

$self = $self->toggle_checked_ok("input[name=human]");

Used to toggle the "checked" attribute either with a click event or fallback to
javascript.

## wait\_for

$self = $self->wait_for(0.2);
$self = $self->wait_for('[name="agree"]', "test description");
$self = $self->wait_for('[name="agree"]:enabled', {interval => 1.5, timeout => 10});
$self = $self->wait_for('[name="agree"]:selected');
$self = $self->wait_for('[href="/"]:visible');
$self = $self->wait_for('[href="/hidden"]:hidden');
$self = $self->wait_for('[name=checkbox]:checked');

Simpler version of ["wait\_until"](#wait_until) for the most common use cases:

- Number

Allows the browser and server to run for a given interval in seconds. This is
useful if you want the browser to receive data from the server or simply let
`setTimeout()` in JavaScript run.

- String

Wait for an element matching the CSS selector with some additional modifiers:
[:enabled](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWebElement%23is_enabled),
[:hidden](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWebElement%23is_hidden),
[:selected](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWebElement%23is_selected) and
[:visible](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWebElement%23is_displayed).

Check out [Selenium::Remote::WebElement](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3AWebElement) for details about the modifiers.

## wait\_until

$self = $self->wait_until(sub { my $self = shift; return 1 }, \%args);
$self = $self->wait_until(sub { $_->get_current_url =~ /foo/ }, \%args);

# Use it as a sleep(0.8)
$self = $self->wait_until(sub { 0 }, {timeout => 0.8, skip => 1});

Start [Mojo::IOLoop](https://metacpan.org/pod/Mojo%3A%3AIOLoop) and run it until the callback returns true. Note that
`$_[0]` is `$self` and `$_` is ["driver"](#driver). `%args` is optional, but can
contain these values:

{
interval => $seconds, # Default: 0.5
timeout => $seconds, # Default: 60
skip => $bool, # Default: 0
}

## window\_size\_is

$self = $self->window_size_is([$width, $height]);
$self = $self->window_size_is([375, 667]);

Test if window has the expected width and height.

# AUTHOR

Jan Henning Thorsen

# COPYRIGHT AND LICENSE

Copyright (C) 2014, Jan Henning Thorsen

This program is free software, you can redistribute it and/or modify it under
the terms of the Artistic License version 2.0.

# SEE ALSO

[Test::Mojo](https://metacpan.org/pod/Test%3A%3AMojo).

[Selenium::Remote::Driver](https://metacpan.org/pod/Selenium%3A%3ARemote%3A%3ADriver)