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 is a role that extends Test::Mojo with additional methods which checks behaviour in a browser. All the heavy lifting is done by Selenium::Remote::Driver.

Some of the Selenium::Remote::Driver methods are available directly in this role, while the rest are available through the object held by the "driver" attribute. Please create an issue if you think more tests or methods should be provided directly by Test::Mojo::Role::Selenium.

OPTIONAL DEPENDENCIES

Selenium::Remote::Driver require some external dependencies to work. Here are a quick intro to install some of the dependencies to make this module work.

  • Selenium::Chrome

    # 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 is only populated, if the request went through an "Internal app". This means that methods such as "header_is" in Test::Mojo will not work or probably fail completely when testing an "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 methods such as "status_is", since "tx" in Test::Mojo will not be set. See also "CAVEAT".

This variable will get the value of "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 methods.

MOJO_SELENIUM_DRIVER

This variable can be set to a classname, such as Selenium::Chrome 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, which means they follow standard URL format rules.

TEST_SELENIUM

This variable must be set to a true value for "setup_or_skip_all" to not skip this test. Will also set "MOJO_SELENIUM_BASE_URL" if it looks like an URL.

ATTRIBUTES

driver

$driver = $self->driver;

An instance of Selenium::Remote::Driver.

driver_args

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

Used to set args passed on to the "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".

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" 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".

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.

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.

go_back

$self = $self->go_back;

Equivalent to hitting the back button on the browser.

See "go_back" in Selenium::Remote::Driver.

go_forward

$self = $self->go_forward;

Equivalent to hitting the forward button on the browser.

See "go_forward" in Selenium::Remote::Driver.

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 is defined. tx() is undefined if "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.

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.

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.

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.

$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, but will not build $app if "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.

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 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 tests unless TEST_SELENIUM is set and and "driver" can be built.

Will also set "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.

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" 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, :hidden, :selected and :visible.

Check out Selenium::Remote::WebElement 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 and run it until the callback returns true. Note that $_[0] is $self and $_ is "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.

Selenium::Remote::Driver