├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── META6.json
├── README.chromedriver.md
├── README.md
├── examples
├── blackberry.rakutest
├── chrome.rakutest
├── firefox.rakutest
├── phantomjs-multiple.rakutest
└── phantomjs.rakutest
├── lib
└── Selenium
│ ├── WebDriver.rakumod
│ └── WebDriver
│ ├── BlackBerry.rakumod
│ ├── Chrome.rakumod
│ ├── Edge.rakumod
│ ├── Firefox.rakumod
│ ├── InternetExplorer.rakumod
│ ├── Keys.rakumod
│ ├── Opera.rakumod
│ ├── PhantomJS.rakumod
│ ├── Safari.rakumod
│ ├── WebElement.rakumod
│ ├── WebWindow.rakumod
│ ├── Wire.rakumod
│ └── X
│ └── Error.rakumod
├── logotype
└── logo_32x32.png
├── resources
└── firefox_extension
│ ├── prefs.json
│ └── webdriver.xpi
└── t
├── 01-load.rakutest
├── 02-phantomjs.rakutest
├── 03-webelement.rakutest
├── 04-webwindow.rakutest
├── 05-firefox.rakutest
├── 06-chrome.rakutest
├── 07-blackberry.rakutest
└── 99-author-meta.rakutest
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 | tags-ignore:
8 | - '*'
9 | pull_request:
10 |
11 | jobs:
12 | raku:
13 | strategy:
14 | matrix:
15 | os:
16 | - ubuntu-latest
17 | - macOS-latest
18 | - windows-latest
19 | raku-version:
20 | - 'latest'
21 | runs-on: ${{ matrix.os }}
22 | steps:
23 | - uses: actions/checkout@v3
24 | - uses: Raku/setup-raku@v1
25 | with:
26 | raku-version: ${{ matrix.raku-version }}
27 | - name: Install Dependencies
28 | run: zef install --/test --test-depends --deps-only .
29 | - name: Install App::Prove6
30 | run: zef install --/test App::Prove6
31 | - name: Run Tests
32 | run: prove6 -l t
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .precomp
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 |
3 | language: perl6
4 |
5 | os:
6 | - linux
7 | - osx
8 |
9 | perl6:
10 | - 2016.11
11 | - latest
12 |
13 | install:
14 | - echo $TRAVIS_OS_NAME
15 | - rakudobrew build zef
16 | - zef --depsonly install .
17 | - zef install Test::META
18 | - zef build .
19 |
20 | before_script:
21 | # Start xvfb for Firefox headless testing and give it some time to start
22 | - if [ "$TRAVIS_OS_NAME" == "linux" ]; then export DISPLAY=:99.0 ; fi
23 | - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sh -e /etc/init.d/xvfb start ; fi
24 | - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sleep 3 ; fi
25 | # Install PhantomJS on MacOSX using homebrew
26 | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
27 | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install phantomjs ; fi
28 | # Show PhantomJS version
29 | - phantomjs --version
30 |
31 | script:
32 | - AUTHOR_TESTING=1 prove -ve "perl6 -Ilib" t
33 | - zef install .
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Ahmad M. Zawawi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/META6.json:
--------------------------------------------------------------------------------
1 | {
2 | "perl": "6.c",
3 | "name": "Selenium::WebDriver",
4 | "license" : "MIT",
5 | "version": "0.0.2",
6 | "description": "Raku Bindings for Selenium WebDriver",
7 | "depends": [
8 | "File::Temp",
9 | "File::Which",
10 | "File::Zip",
11 | "HTTP::UserAgent",
12 | "JSON::Tiny",
13 | "MIME::Base64",
14 | "URI::Encode"
15 | ],
16 | "test-depends": [
17 | "Test"
18 | ],
19 | "resources": [
20 | "firefox_extension/prefs.json",
21 | "firefox_extension/webdriver.xpi"
22 | ],
23 | "provides": {
24 | "Selenium::WebDriver": "lib/Selenium/WebDriver.rakumod",
25 | "Selenium::WebDriver::BlackBerry": "lib/Selenium/WebDriver/BlackBerry.rakumod",
26 | "Selenium::WebDriver::Chrome": "lib/Selenium/WebDriver/Chrome.rakumod",
27 | "Selenium::WebDriver::Edge": "lib/Selenium/WebDriver/Edge.rakumod",
28 | "Selenium::WebDriver::Firefox": "lib/Selenium/WebDriver/Firefox.rakumod",
29 | "Selenium::WebDriver::InternetExplorer": "lib/Selenium/WebDriver/InternetExplorer.rakumod",
30 | "Selenium::WebDriver::Keys": "lib/Selenium/WebDriver/Keys.rakumod",
31 | "Selenium::WebDriver::Opera": "lib/Selenium/WebDriver/Opera.rakumod",
32 | "Selenium::WebDriver::PhantomJS": "lib/Selenium/WebDriver/PhantomJS.rakumod",
33 | "Selenium::WebDriver::Safari": "lib/Selenium/WebDriver/Safari.rakumod",
34 | "Selenium::WebDriver::WebElement": "lib/Selenium/WebDriver/WebElement.rakumod",
35 | "Selenium::WebDriver::WebWindow": "lib/Selenium/WebDriver/WebWindow.rakumod",
36 | "Selenium::WebDriver::Wire": "lib/Selenium/WebDriver/Wire.rakumod",
37 | "Selenium::WebDriver::X::Error": "lib/Selenium/WebDriver/X/Error.rakumod"
38 | },
39 | "source-url": "git://github.com/azawawi/raku-selenium-webdriver.git"
40 | }
41 |
--------------------------------------------------------------------------------
/README.chromedriver.md:
--------------------------------------------------------------------------------
1 | == Short setup guide for installing the chromedriver ==
2 |
3 | = Debian based systems =
4 |
5 | Install the Chromium browser as well as the chromedriver:
6 |
7 | ```
8 | $ sudo apt-get install chromium-browser chromium-chromedriver
9 | ```
10 |
11 | Please remember to add `/usr/lib/chromium-browser/ to your PATH
12 |
13 | ```
14 | $ export PATH=$PATH:/usr/lib/chromium-browser/
15 | ```
16 |
17 | Then check that it is working as expected:
18 |
19 | ```
20 | $ chromedriver --version
21 | ChromeDriver 2.24
22 | ```
23 |
24 | If you get the following error message instead:
25 |
26 | ```
27 | "chromedriver: error while loading shared libraries: libui_base.so: cannot open shared object file: No such file or directory"
28 | ```
29 |
30 | Then add the library path of the chrome browser:
31 |
32 | ```
33 | $ sudo sh -c 'echo "/usr/lib/chromium-browser/libs" > /etc/ld.so.conf.d/chrome_lib.conf'
34 | ```
35 |
36 | ```
37 | $ sudo ldconfig
38 | ```
39 |
40 | And check again:
41 |
42 | ```
43 | $ chromedriver --version
44 | ChromeDriver 2.24
45 | ```
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Selenium::WebDriver
2 | [](https://github.com/azawawi/raku-selenium-webdriver/actions)
4 |
5 | This module provides the [Raku](http://raku.org) bindings for the [Selenium WebDriver Wire Protocol](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol).
6 |
7 | ***Note:*** This module is a work in progress. Please see its project status [here](https://github.com/azawawi/perl6-selenium-webdriver/blob/master/README.md#project-status).
8 |
9 | ## Example
10 |
11 | ```Raku
12 | use v6;
13 | use Selenium::WebDriver::PhantomJS;
14 |
15 | my $driver = Selenium::WebDriver::PhantomJS.new;
16 | $driver.url("http://google.com");
17 | say "Title: " ~ $driver.title;
18 | say "URL: " ~ $driver.url;
19 | say "Source length: " ~ $driver.source.chars;
20 | $driver.save-screenshot('test.png');
21 | LEAVE {
22 | $driver.quit if $driver.defined
23 | };
24 | ```
25 |
26 | For more examples, please see the [examples](examples) folder.
27 |
28 | ## PhantomJS Installation
29 |
30 | ### Linux/Debian
31 |
32 | To install phantomjs on Debian, please type the following:
33 | ```
34 | $ sudo apt-get install phantomjs
35 | ```
36 |
37 | **CAUTION**: Also there are [prebuilt binaries](
38 | https://bitbucket.org/ariya/phantomjs/downloads) for PhantomJS for Linux if the packaged version is a bit old.
39 |
40 | ### Mac OS X
41 |
42 | To install PhantomJS on Mac OS X, the simplest solution is to use brew:
43 | ```
44 | $ brew update
45 | $ brew install phantomjs
46 | ```
47 |
48 | ### Windows
49 |
50 | To install PhantomJS on windows, please download a copy from
51 | [Here](http://phantomjs.org/) and then make it available in your PATH environment
52 | variable.
53 |
54 | ### Travis CI
55 |
56 | Travis CI comes with [pre-installed PhantomJS](
57 | http://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-PhantomJS).
58 | No special instructions are needed.
59 |
60 | ## Project Status
61 |
62 | | Web Driver | Status |
63 | | ------------- | ------------- |
64 | | PhantomJS | **DONE** but needs more tests |
65 | | Firefox | **DONE** |
66 | | Chrome | **DONE** but needs external [chromedriver](https://sites.google.com/a/chromium.org/chromedriver/) |
67 | | Safari | Pending |
68 | | Opera | Pending |
69 | | MSIE | Pending |
70 | | MSEdge | Pending |
71 | | BlackBerry | **DONE** |
72 |
73 | ## Installation
74 |
75 | To install it using zef (a module management tool bundled with Rakudo Star):
76 |
77 | ```
78 | $ zef install Selenium::WebDriver
79 | ```
80 |
81 | ## Testing
82 |
83 | - To run tests:
84 | ```
85 | $ prove --ext .rakutest -ve "raku -I."
86 | ```
87 |
88 | - To run all tests including author tests (Please make sure
89 | [Test::Meta](https://github.com/jonathanstowe/Test-META) is installed):
90 | ```
91 | $ zef install Test::META
92 | $ TEST_AUTHOR=1 prove --ext .rakutest -ve "raku -I."
93 | ```
94 |
95 | ## Author
96 |
97 | Ahmad M. Zawawi, [azawawi](https://github.com/azawawi/) on #raku
98 |
99 | ## License
100 |
101 | MIT License
102 |
--------------------------------------------------------------------------------
/examples/blackberry.rakutest:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env raku
2 |
3 | use v6;
4 |
5 | use Test;
6 | plan 5;
7 | use Selenium::WebDriver::BlackBerry;
8 |
9 | # Create new BlackBerry webdriver. The ip address is shown in the
10 | # development section of the settings menu of the BlackBerry browser.
11 | my $driver = Selenium::WebDriver::BlackBerry.new('169.254.0.1');
12 |
13 | # Navigate to google.com
14 | $driver.url( "http://google.com" );
15 | ok $driver.title ~~ / 'Google' /, "Google in title";
16 | ok $driver.url ~~ / ^ 'http://' .+? 'google'/, "google.com in url";
17 |
18 | # Find search box and then type "Perl 6" in it
19 | my $search-box = $driver.element-by-name( 'q' );
20 | $search-box.send-keys( "Perl 6" );
21 |
22 | ok $search-box.tag-name.lc eq 'input', "Search box must be an ";
23 | ok $search-box.enabled, "Search box is enabled";
24 |
25 | # Submit form
26 | $search-box.submit;
27 |
28 | # Take a screenshot
29 | my $filename = 'output.png';
30 | $driver.save-screenshot( $filename );
31 | ok $filename.IO ~~ :e, "$filename exists";
32 |
--------------------------------------------------------------------------------
/examples/chrome.rakutest:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env raku
2 |
3 | use v6;
4 |
5 | use Test;
6 | plan 5;
7 | use Selenium::WebDriver::Chrome;
8 |
9 | # Create new Chrome webdriver. Please note chromedriver must be already
10 | # installed and configured in PATH
11 | my $driver = Selenium::WebDriver::Chrome.new;
12 |
13 | # Navigate to google.com
14 | $driver.url( "http://google.com" );
15 | ok $driver.title ~~ / 'Google' /, "Google in title";
16 | ok $driver.url ~~ / ^ 'http://' .+? 'google'/, "google.com in url";
17 |
18 | # Find search box and then type "Perl 6" in it
19 | my $search-box = $driver.element-by-name( 'q' );
20 | $search-box.send-keys( "Perl 6" );
21 |
22 | ok $search-box.tag-name.lc eq 'input', "Search box must be an ";
23 | ok $search-box.enabled, "Search box is enabled";
24 |
25 | # Submit form
26 | $search-box.submit;
27 |
28 | # Take a screenshot
29 | my $filename = 'output.png';
30 | $driver.save-screenshot( $filename );
31 | ok $filename.IO ~~ :e, "$filename exists";
32 |
33 | LEAVE {
34 | diag 'WebDriver cleanup';
35 | $driver.quit if $driver.defined;
36 | }
37 |
--------------------------------------------------------------------------------
/examples/firefox.rakutest:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env raku
2 |
3 | use v6;
4 |
5 | use Test;
6 | plan 5;
7 | use Selenium::WebDriver::Firefox;
8 |
9 | # Create new firefox webdriver. Please note firefox must be already
10 | # installed and configured in PATH
11 | my $driver = Selenium::WebDriver::Firefox.new;
12 |
13 | # Navigate to google.com
14 | $driver.url( "http://google.com" );
15 | ok $driver.title ~~ / 'Google' /, "Google in title";
16 | ok $driver.url ~~ / ^ 'http' 's'? '://' .+? 'google'/, "google.com in url";
17 |
18 | # Find search box and then type "Perl 6" in it
19 | my $search-box = $driver.element-by-name( 'q' );
20 | $search-box.send-keys( "Perl 6" );
21 |
22 | ok $search-box.tag-name.lc eq 'input', "Search box must be an ";
23 | ok $search-box.enabled, "Search box is enabled";
24 |
25 | # Submit form
26 | $search-box.submit;
27 |
28 | # Take a screenshot
29 | my $filename = 'output.png';
30 | $driver.save-screenshot( $filename );
31 | ok $filename.IO ~~ :e, "$filename exists";
32 |
33 | LEAVE {
34 | diag 'WebDriver cleanup';
35 | $driver.quit if $driver.defined;
36 | }
37 |
--------------------------------------------------------------------------------
/examples/phantomjs-multiple.rakutest:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env raku
2 |
3 | use v6;
4 |
5 | use Test;
6 | use Selenium::WebDriver::PhantomJS;
7 |
8 | # Number of simulatenous drivers to test port collision
9 | constant NUM_OF_DRIVERS = 5;
10 | plan 3 * NUM_OF_DRIVERS;
11 |
12 | # Create two phantomjs webdriver
13 | # Please note phantomjs must be already installed
14 | my @drivers;
15 | for 1..NUM_OF_DRIVERS {
16 | diag "Driver #$_ starting up";
17 | my $driver = Selenium::WebDriver::PhantomJS.new;
18 | ok($driver.defined, "WebDriver is defined");
19 |
20 | @drivers.push($driver);
21 |
22 | # Navigate to google.com
23 | $driver.url( "http://google.com" );
24 |
25 | ok($driver.title ~~ / 'Google' /, "Google in title");
26 | ok($driver.url ~~ / ^ 'http://' .+? 'google'/, "google.com in url");
27 | }
28 |
29 | LEAVE {
30 | diag 'WebDriver(s) cleanup';
31 | $_.quit for @drivers;
32 | }
33 |
--------------------------------------------------------------------------------
/examples/phantomjs.rakutest:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env raku
2 |
3 | use v6;
4 |
5 | use Test;
6 | plan 5;
7 | use Selenium::WebDriver::PhantomJS;
8 |
9 | # Create new phantomjs webdriver. Please note phantomjs must be already
10 | # installed and configured in PATH
11 | my $driver = Selenium::WebDriver::PhantomJS.new;
12 |
13 | # Navigate to google.com
14 | $driver.url( "http://google.com" );
15 | ok $driver.title ~~ / 'Google' /, "Google in title";
16 | ok $driver.url ~~ / ^ 'http://' .+? 'google'/, "google.com in url";
17 |
18 | # Find search box and then type "Perl 6" in it
19 | my $search-box = $driver.element-by-name( 'q' );
20 | $search-box.send-keys( "Perl 6" );
21 |
22 | ok $search-box.tag-name.lc eq 'input', "Search box must be an ";
23 | ok $search-box.enabled, "Search box is enabled";
24 |
25 | # Submit form
26 | $search-box.submit;
27 |
28 | # Take a screenshot
29 | my $filename = 'output.png';
30 | $driver.save-screenshot( $filename );
31 | ok $filename.IO ~~ :e, "$filename exists";
32 |
33 | LEAVE {
34 | diag 'WebDriver cleanup';
35 | $driver.quit if $driver.defined;
36 | }
37 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | use Selenium::WebDriver::PhantomJS;
5 | # use Selenium::WebDriver::Edge;
6 | use Selenium::WebDriver::Firefox;
7 | use Selenium::WebDriver::Chrome;
8 | # use Selenium::WebDriver::InternetExplorer;
9 |
10 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/BlackBerry.rakumod:
--------------------------------------------------------------------------------
1 | use v6;
2 | use Selenium::WebDriver::Wire;
3 |
4 | unit class Selenium::WebDriver::BlackBerry is Selenium::WebDriver::Wire;
5 |
6 | method new(
7 | Str $host,
8 | Int :$port = 1338,
9 | Bool :$debug = False )
10 | {
11 | self.bless: :$host, :$port, :$debug
12 | }
13 |
14 | method start {
15 | say "Launching BlackBerry Driver on $.host():$.port()" if self.debug;
16 | }
17 |
18 | method stop {
19 | }
20 |
21 | # GET /sessions
22 | method sessions {
23 | my $result = self._execute-command( "GET", "/sessions" );
24 |
25 | return unless $result.defined;
26 | return $result;
27 | }
28 |
29 | # We just pick the first active session here, or create a new one.
30 | method _new-session {
31 | my $sessions = self.sessions;
32 | if $sessions -> [$s] {
33 | return { sessionId => ~$s }
34 | }
35 |
36 | return self._execute-command(
37 | "POST",
38 | "/session",
39 | {
40 | "desiredCapabilities" => {},
41 | "requiredCapabilities" => {},
42 | }
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/Chrome.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | use File::Which;
5 | use Selenium::WebDriver::Wire;
6 |
7 | unit class Selenium::WebDriver::Chrome is Selenium::WebDriver::Wire;
8 |
9 | has Proc::Async $.process is rw;
10 |
11 | method start {
12 | say "Launching Chrome Driver" if self.debug;
13 |
14 | # Find process in PATH
15 | my $chrome-driver = which("chromedriver");
16 | die "Cannot find chromedriver in your PATH" unless $chrome-driver.defined;
17 |
18 | # Run it
19 | say "Chrome found at '$chrome-driver'" if self.debug;
20 | my $p = Proc::Async.new($chrome-driver, "--port=" ~ self.port);
21 | $p.start;
22 |
23 | self.process = $p;
24 | }
25 |
26 | method stop {
27 | self.process.kill if self.process.defined;
28 | }
29 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/Edge.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | !!!
5 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/Firefox.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | use Selenium::WebDriver::Wire;
5 | use File::Which;
6 | use File::Temp;
7 | use File::Zip;
8 | use JSON::Tiny;
9 |
10 | unit class Selenium::WebDriver::Firefox is Selenium::WebDriver::Wire;
11 |
12 | has Proc::Async $.process is rw;
13 |
14 | method start {
15 | # firefox webdriver extension needs this weird url prefix
16 | self.url-prefix = "/hub";
17 |
18 | # Find webdriver extension resources in our module installed location
19 | say 'Finding webdriver location using %?RESOURCES' if self.debug;
20 | my $webdriver-xpi = %?RESOURCES{'firefox_extension/webdriver.xpi'};
21 | fail "Cannot find webdriver.xpi" unless $webdriver-xpi.defined;
22 | my $firefox-prefs = %?RESOURCES{'firefox_extension/prefs.json'};
23 | fail "Cannot find prefs.json" unless $firefox-prefs.defined;
24 |
25 | # Create a temporary folder for our temporary firefox profile
26 | my ($directory, $dirhandle) = tempdir;
27 |
28 | # unzip webdriver.xpi
29 | my $profile-path = "$directory/perl6-selenium-webdriver";
30 | my $extension-path = "$profile-path/extensions/fxdriver@googlecode.com";
31 | my $prefs-file-name = "$profile-path/user.js";
32 |
33 | # Read firefox json-formatted preferences
34 | my $prefs = from-json($firefox-prefs.IO.slurp);
35 |
36 | # Create temporary profile path
37 | $profile-path.IO.mkdir;
38 |
39 | # Modify mutable port that were loaded from prefs.json
40 | $prefs = self.port;
41 |
42 | # Write a user.js file in profile path
43 | my $fh = $prefs-file-name.IO.open(:w);
44 | for $prefs.kv -> $k, $v {
45 | my $value = to-json($v);
46 | $fh.say(qq{user_pref("$k", $value);});
47 | }
48 | for $prefs.kv -> $k, $v {
49 | my $value = to-json($v);
50 | $fh.say(qq{user_pref("$k", $value);});
51 | }
52 | $fh.close;
53 |
54 | $extension-path.IO.mkdir;
55 |
56 | # Unzip the webdriver extension (XPI file format is simply a ZIP archive)
57 | say "unzipping $webdriver-xpi into $extension-path";
58 | my $zip-file = File::Zip.new(file-name => ~$webdriver-xpi);
59 | $zip-file.unzip(directory => $extension-path);
60 |
61 | # Setup firefox environment
62 | # %*ENV = "firefox.log";
63 | %*ENV = $profile-path;
64 | %*ENV = "1";
65 | %*ENV = "1";
66 | %*ENV = "1";
67 |
68 | say "Launching firefox" if self.debug;
69 |
70 | # Find firefox process in PATH
71 | my $firefox = which("firefox");
72 | die "Cannot find firefox in your PATH" unless $firefox.defined;
73 |
74 | # Run it asynchrously
75 | say "Firefox found at '$firefox'" if self.debug;
76 | my $p = Proc::Async.new($firefox);
77 | $p.start;
78 |
79 | # And store the process to be able to kill it when we're done
80 | self.process = $p;
81 | }
82 |
83 | method stop {
84 | self.process.kill if self.process.defined;
85 | }
86 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/InternetExplorer.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | !!!
5 |
6 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/Keys.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | unit module Selenium::WebDriver::Keys;
5 |
6 | our constant %Keys is export = {
7 | 'NULL' => "\x000",
8 | 'CANCEL' => "\x001",
9 | 'HELP' => "\x002",
10 | 'BACK-SPACE' => "\x003",
11 | 'TAB' => "\x004",
12 | 'CLEAR' => "\x005",
13 | 'RETURN' => "\x006",
14 | 'ENTER' => "\x007",
15 | 'SHIFT' => "\x008",
16 | 'CONTROL' => "\x009",
17 | 'ALT' => "\x00A",
18 | 'PAUSE' => "\x00B",
19 | 'ESCAPE' => "\x00C",
20 | 'SPACE' => "\x00D",
21 | 'PAGE-UP' => "\x00E",
22 | 'PAGE-DOWN' => "\x00F",
23 | 'END' => "\x010",
24 | 'HOME' => "\x011",
25 | 'LEFT-ARROW' => "\x012",
26 | 'UP-ARROW' => "\x013",
27 | 'RIGHT-ARROW' => "\x014",
28 | 'DOWN-ARROW' => "\x015",
29 | 'INSERT' => "\x016",
30 | 'DELETE' => "\x017",
31 | 'SEMICOLON' => "\x018",
32 | 'EQUALS' => "\x019",
33 | 'NUMPAD0' => "\x01A",
34 | 'NUMPAD1' => "\x01B",
35 | 'NUMPAD2' => "\x01C",
36 | 'NUMPAD3' => "\x01D",
37 | 'NUMPAD4' => "\x01E",
38 | 'NUMPAD5' => "\x01F",
39 | 'NUMPAD6' => "\x020",
40 | 'NUMPAD7' => "\x021",
41 | 'NUMPAD8' => "\x022",
42 | 'NUMPAD9' => "\x023",
43 | 'MULTIPLY' => "\x024",
44 | 'ADD' => "\x025",
45 | 'SEPARATOR' => "\x026",
46 | 'SUBTRACT' => "\x027",
47 | 'DECIMAL' => "\x028",
48 | 'DIVIDE' => "\x029",
49 | 'F1' => "\x031",
50 | 'F2' => "\x032",
51 | 'F3' => "\x033",
52 | 'F4' => "\x034",
53 | 'F5' => "\x035",
54 | 'F6' => "\x036",
55 | 'F7' => "\x037",
56 | 'F8' => "\x038",
57 | 'F9' => "\x039",
58 | 'F10' => "\x03A",
59 | 'F11' => "\x03B",
60 | 'F12' => "\x03C",
61 | 'COMMAND-META' => "\x03D",
62 | };
63 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/Opera.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | !!!
5 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/PhantomJS.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | use Selenium::WebDriver::Wire;
5 | use File::Which;
6 |
7 | unit class Selenium::WebDriver::PhantomJS is Selenium::WebDriver::Wire;
8 |
9 | has Proc::Async $.process is rw;
10 |
11 | method start {
12 | say "Starting phantomjs process" if $.debug;
13 |
14 | # Find process in PATH
15 | my $path = which( 'phantomjs' );
16 | die "Cannot find phantomjs in your PATH" unless $path.defined;
17 |
18 | say "phantomjs path: $path" if $.debug;
19 | say "phantomjs port: $.port" if $.debug;
20 | my $process = Proc::Async.new(
21 | $path,
22 | "--webdriver=" ~ $.port,
23 | "--webdriver-loglevel=" ~ ($.debug ?? "DEBUG" !! "WARN"),
24 | );
25 | my $p = $process.start;
26 | say("phantomjs returned Proc::Async promise: " ~ $p.perl) if $.debug;
27 |
28 | self.process = $process;
29 | }
30 |
31 | method stop {
32 | self.process.kill if self.process.defined;
33 | }
34 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/Safari.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | !!!
5 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/WebElement.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | unit class Selenium::WebDriver::WebElement;
5 |
6 | has Str $.id is rw;
7 | has $.driver is rw;
8 |
9 | # POST /session/:sessionId/element/:id/click
10 | method click {
11 | return $.driver._post( "element/$.id/click" );
12 | }
13 |
14 | # POST /session/:sessionId/element/:id/value
15 | method send-keys(Str $keys) {
16 | return $.driver._post(
17 | "element/$.id/value",
18 | {
19 | "value" => $keys.comb;
20 | }
21 | );
22 | }
23 |
24 | # GET /session/:sessionId/element/:id/name
25 | method tag-name {
26 | return $.driver._get( "element/$.id/name" );
27 | }
28 |
29 | # GET /session/:sessionId/element/:id/selected
30 | method selected {
31 | return $.driver._get( "element/$.id/selected" );
32 | }
33 |
34 | # GET /session/:sessionId/element/:id/enabled
35 | method enabled {
36 | return $.driver._get( "element/$.id/enabled" );
37 | }
38 |
39 | # GET /session/:sessionId/element/:id/attribute/:name
40 | method attr(Str $name) {
41 | return $.driver._get( "element/$.id/attribute/$name" );
42 | }
43 |
44 | # GET /session/:sessionId/element/:id/equals/:other
45 | method equals-by-id(Str $id) {
46 | return $.driver._get( "element/$.id/equals/$id" );
47 | }
48 |
49 | # GET /session/:sessionId/element/:id/displayed
50 | method displayed {
51 | return $.driver._get( "element/$.id/displayed" );
52 | }
53 |
54 | # GET /session/:sessionId/element/:id/location
55 | method location {
56 | return $.driver._get( "element/$.id/location" );
57 | }
58 |
59 | # GET /session/:sessionId/element/:id/location_in_view
60 | method location-in-view {
61 | return $.driver._get( "element/$.id/location_in_view" );
62 | }
63 |
64 | # GET /session/:sessionId/element/:id/size
65 | method size(Str $id) {
66 | return $.driver._get( "element/$.id/size" );
67 | }
68 |
69 | # GET /session/:sessionId/element/:id/css/:propertyName
70 | method css(Str $property-name) {
71 | return $.driver._get( "element/$.id/css/$property-name");
72 | }
73 |
74 | # POST /session/:sessionId/element/:id/submit
75 | method submit {
76 | return $.driver._post( "element/$.id/submit" );
77 | }
78 |
79 | # GET /session/:sessionId/element/:id/text
80 | method text {
81 | return $.driver._get( "element/$.id/text" );
82 | }
83 |
84 | method clear {
85 | return $.driver._post( "element/$.id/clear" );
86 | }
87 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/WebWindow.rakumod:
--------------------------------------------------------------------------------
1 | use v6;
2 |
3 | unit class Selenium::WebDriver::WebWindow;
4 |
5 | has Str $.handle is rw;
6 | has $.driver is rw;
7 |
8 | # POST /session/:sessionId/window
9 | method current(Str $name) {
10 | return $.driver._post( 'window', { name => $name } );
11 | }
12 |
13 | # DELETE /session/:sessionId/window
14 | method close-current {
15 | return $.driver._delete( 'window' );
16 | }
17 |
18 | # POST /session/:sessionId/window/:windowHandle/size
19 | multi method size(Int $width, Int $height) {
20 | return $.driver._post(
21 | "window/$.handle/size",
22 | { width => $width, height => $height }
23 | );
24 | }
25 |
26 | # GET /session/:sessionId/window/:windowHandle/size
27 | multi method size returns Hash {
28 | return $.driver._get( "window/$.handle/size" );
29 | }
30 |
31 | # POST /session/:sessionId/window/:windowHandle/position
32 | multi method position(Int $x, Int $y) returns Hash {
33 | return $.driver._post( "window/$.handle/position", { x => $x, y => $y } );
34 | }
35 |
36 | # GET /session/:sessionId/window/:windowHandle/position
37 | multi method position returns Hash {
38 | return $.driver._get( "window/$.handle/position" );
39 | }
40 |
41 | # POST /session/:sessionId/window/:windowHandle/maximize
42 | multi method maximize {
43 | return $.driver._post( "window/$.handle/maximize" );
44 | }
45 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/Wire.rakumod:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # JSON Wire Protocol Perl 6 implementation
4 | # Please see
5 | # https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
6 | #
7 |
8 | use v6;
9 |
10 | =begin markdown
11 | =end markdown
12 | unit class Selenium::WebDriver::Wire;
13 |
14 | use HTTP::UserAgent;
15 | use JSON::Tiny;
16 | use MIME::Base64;
17 | use URI::Encode;
18 | use Selenium::WebDriver::WebElement;
19 | use Selenium::WebDriver::WebWindow;
20 | use Selenium::WebDriver::X::Error;
21 |
22 | has Bool $.debug is rw;
23 | has Str $.host is rw;
24 | has Int $.port is rw;
25 | has Str $.url-prefix is rw;
26 | has Str $.session-id is rw;
27 |
28 | =begin markdown
29 | =end markdown
30 | submethod BUILD(
31 | Str :$host = '127.0.0.1',
32 | Int :$port = -1,
33 | Str :$url-prefix = '',
34 | Bool :$debug = False,
35 | Int :$max-attempts where $_ >= 1 = 10 )
36 | {
37 |
38 | # Attributes
39 | self.debug = $debug;
40 | self.host = $host;
41 | self.url-prefix = $url-prefix;
42 |
43 | # We need to find an empty port given that no port was given
44 | self.port = $port == -1 ?? self._empty-port !! $port;
45 |
46 | # Start behavior (normally implemented in children)
47 | self.start;
48 |
49 | # Try to create a session for n times
50 | my $session;
51 | for 1..$max-attempts {
52 | # Try to create session
53 | $session = self._new-session;
54 | last if $session.defined;
55 |
56 | CATCH {
57 | default {
58 | # Retry session creation failure after timeout
59 | say "Attempt $_ to create session" if self.debug;
60 | sleep 1;
61 | }
62 | }
63 | }
64 |
65 | # No session could be created
66 | die "Cannot obtain a session after $max-attempts attempts" unless $session.defined;
67 |
68 | self.session-id = $session;
69 | die "Session id is not defined" unless self.session-id.defined;
70 | }
71 |
72 | # Normally implemented in subclasses
73 | method start { }
74 |
75 | # Normally implemented in subclasses
76 | method stop { }
77 |
78 | =begin markdown
79 | =end markdown
80 | # POST /session
81 | method _new-session {
82 | return self._execute-command(
83 | "POST",
84 | "/session",
85 | {
86 | "desiredCapabilities" => {},
87 | "requiredCapabilities" => {},
88 | }
89 | );
90 | }
91 |
92 | =begin markdown
93 | =end markdown
94 | # GET /sessions
95 | method sessions {
96 | my $result = self._execute-command( "GET", "/sessions" );
97 |
98 | return unless $result.defined;
99 | return $result;
100 | }
101 |
102 | # GET /session/:sessionId
103 | method capabilities {
104 | return self._execute-command( "GET", "/session/$(self.session-id)" );
105 | }
106 |
107 | # DELETE /session/:sessionId
108 | method _delete_session {
109 | return self._execute-command( "DELETE", "/session/$(self.session-id)" );
110 | }
111 |
112 | # POST /session/:sessionId/timeouts
113 | method _timeouts(Str $type, Int $timeout_millis) {
114 | return self._post(
115 | 'timeouts',
116 | {
117 | type => $type,
118 | ms => $timeout_millis
119 | }
120 | );
121 | }
122 |
123 | method script-timeout(Int $timeout_millis) {
124 | return self._timeouts( 'script', $timeout_millis );
125 | }
126 |
127 | method implicit-timeout(Int $timeout_millis) {
128 | return self._timeouts( 'implicit', $timeout_millis );
129 | }
130 |
131 | method page-load-timeout(Int $timeout_millis) {
132 | return self._timeouts( 'page load', $timeout_millis );
133 | }
134 |
135 | # POST /session/:sessionId/timeouts/async_script
136 | method async-script-timeout(Int $timeout_millis) {
137 | return self._post( 'timeouts/async_script', { ms => $timeout_millis } );
138 | }
139 |
140 | # POST /session/:sessionId/timeouts/implicit_wait
141 | method implicit-wait-timeout(Int $timeout_millis) {
142 | return self._post( 'timeouts/implicit_wait', { ms => $timeout_millis } );
143 | }
144 |
145 | # GET /session/:sessionId/window_handle
146 | method current-window returns Selenium::WebDriver::WebWindow {
147 | my $handle = self._get( 'window_handle' );
148 | return Selenium::WebDriver::WebWindow.new( :handle($handle), :driver(self) );
149 | }
150 |
151 | # GET /session/:sessionId/window_handles
152 | method windows returns Array[Selenium::WebDriver::WebWindow] {
153 | my @handles = @( self._get( 'window_handles' ) );
154 | my Selenium::WebDriver::WebWindow @results = gather {
155 | take Selenium::WebDriver::WebWindow.new(
156 | :handle($_),
157 | :driver(self)
158 | ) for @handles;
159 | };
160 |
161 | return @results;
162 | }
163 |
164 | # GET /status
165 | method status returns Hash {
166 | return self._get( 'status' );
167 | }
168 |
169 | # POST /session/:sessionId/execute
170 | =begin markdown
171 |
172 | my $script = q{
173 | return arguments[0] + arguments[1];
174 | };
175 | my $result = $driver.execute( $script, [1, 2] );
176 |
177 | =end markdown
178 | method execute(Str $script, Array $args = []) {
179 | my $result = self._post( 'execute', { script => $script, args => $args } );
180 | return unless $result.defined;
181 | return $result;
182 | }
183 |
184 | # POST /session/:sessionId/execute_async
185 | =begin markdown
186 |
187 | my $script = q{
188 | var callback = arguments[arguments.length-1];
189 | callback( arguments[0] + arguments[1] );
190 | };
191 | my $result = $driver.execute-async( $script, [1, 2] );
192 |
193 | =end markdown
194 | method execute-async(Str $script, Array $args = []) {
195 | my $result = self._post( 'execute_async', { script => $script, args => $args } );
196 | return unless $result.defined;
197 | return $result;
198 | }
199 |
200 | # GET /session/:sessionId/ime/available_engines
201 | method ime-available-engines returns Array {
202 | return self._get( 'ime/available_engines' );
203 | }
204 |
205 | # GET /session/:sessionId/ime/active_engine
206 | method ime-active-engine returns Str {
207 | return self._get( 'ime/active_engine' );
208 | }
209 |
210 | # GET /session/:sessionId/ime/activated
211 | method ime-activated returns Bool {
212 | return self._get( 'ime/activated' );
213 | }
214 |
215 | # POST /session/:sessionId/ime/deactivate
216 | method ime-deactivate {
217 | return self._post( 'ime/deactivate' );
218 | }
219 |
220 | # POST /session/:sessionId/ime/activate
221 | method ime-activate(Str $engine) {
222 | return self._post( 'ime/activate', { engine => $engine } );
223 | }
224 |
225 | # POST /session/:sessionId/frame
226 | method _frame(Any $id) {
227 | return self._post( 'frame', { id => $id } );
228 | }
229 |
230 | multi method frame(Str $id) {
231 | return self._frame($id);
232 | }
233 |
234 | multi method frame(Int $id) {
235 | return self._frame($id);
236 | }
237 |
238 | multi method frame(Selenium::WebDriver::WebElement $id) {
239 | return self._frame($id);
240 | }
241 |
242 | # POST /session/:sessionId/frame/parent
243 | multi method frame-parent {
244 | return self._post( 'frame/parent' );
245 | }
246 |
247 | # GET /session/:sessionId/cookie
248 | method cookies returns Array {
249 | return self._get( 'cookie' );
250 | }
251 |
252 | # POST /session/:sessionId/cookie
253 | =begin markdown
254 |
255 | my $cookie = {
256 | name => 'Name',
257 | value => 'Value',
258 | path => '/',
259 | domain => 'domain.com',
260 | };
261 | $driver.cookie( $cookie );
262 |
263 | =end markdown
264 | multi method cookie(Hash $cookie) {
265 | return self._post( 'cookie', { cookie => $cookie } );
266 | }
267 |
268 | # DELETE /session/:sessionId/cookie
269 | method delete-all-cookies {
270 | return self._delete( 'cookie' );
271 | }
272 |
273 | # DELETE /session/:sessionId/cookie/:name
274 | method delete-cookie(Str $name) {
275 | return self._delete( "cookie/$name" );
276 | }
277 |
278 | =begin markdown
279 | =end markdown
280 | # POST /session/:sessionId/url
281 | multi method url(Str $url) {
282 | return self._post( "url", { url => $url } );
283 | }
284 |
285 | =begin markdown
286 | =end markdown
287 | # GET /session/:sessionId/url
288 | multi method url {
289 | return self._get( 'url' );
290 | }
291 |
292 | =begin markdown
293 | =end markdown
294 | # GET /session/:sessionId/title
295 | method title {
296 | return self._get( 'title' );
297 | }
298 |
299 | =begin markdown
300 | =end markdown
301 | # GET /session/:sessionId/source
302 | method source {
303 | return self._get( 'source' );
304 | }
305 |
306 | =begin markdown
307 | =end markdown
308 | # POST /session/:sessionId/moveto
309 | method move-to(Str $element, Int $xoffset, Int $yoffset) {
310 | return self._post(
311 | "moveto",
312 | {
313 | element => $element,
314 | xoffset => $xoffset,
315 | yoffset => $yoffset,
316 | }
317 | );
318 | }
319 |
320 | =begin markdown
321 | =end markdown
322 | # POST /session/:sessionId/click
323 | method click {
324 | return self._post( "click" );
325 | }
326 |
327 | =begin markdown
328 | =end markdown
329 | method quit {
330 | self._delete_session if self.session-id.defined;
331 | LEAVE {
332 | self.stop;
333 | }
334 | };
335 |
336 | =begin markdown
337 | =end markdown
338 | # GET /session/:sessionId/screenshot
339 | method screenshot {
340 | return self._get('screenshot');
341 | }
342 |
343 | =begin markdown
344 | =end markdown
345 | method save-screenshot(Str $filename) {
346 | my $result = self.screenshot;
347 | $filename.IO.spurt(MIME::Base64.decode( $result ));
348 | }
349 |
350 |
351 | =begin markdown
352 | =end markdown
353 | # POST /session/:sessionId/forward
354 | method forward {
355 | return self._post( "forward" );
356 | }
357 |
358 | =begin markdown
359 | =end markdown
360 | # POST /session/:sessionId/back
361 | method back {
362 | return self._post( "back" );
363 | }
364 |
365 | =begin markdown
366 | =end markdown
367 | # POST /session/:sessionId/refresh
368 | method refresh {
369 | return self._post( "refresh" );
370 | }
371 |
372 | =begin markdown
373 | =end markdown
374 | # POST /session/:sessionId/element
375 | method _element(Str $using, Str $value) {
376 | my $result = self._post(
377 | "element",
378 | {
379 | 'using' => $using,
380 | 'value' => $value,
381 | }
382 | );
383 |
384 | return unless $result.defined;
385 | return Selenium::WebDriver::WebElement.new(
386 | :id( $result ),
387 | :driver( self )
388 | );
389 | }
390 |
391 | =begin markdown
392 | =end markdown
393 | method element-by-class(Str $class) {
394 | return self._element( 'class name', $class );
395 | }
396 |
397 | =begin markdown
398 | =end markdown
399 | method element-by-css(Str $selector) {
400 | return self._element( 'css selector', $selector );
401 | }
402 |
403 | =begin markdown
404 | =end markdown
405 | method element-by-id(Str $id) {
406 | return self._element( 'id', $id );
407 | }
408 |
409 | =begin markdown
410 | =end markdown
411 | method element-by-name(Str $name) {
412 | return self._element( 'name', $name );
413 | }
414 |
415 | =begin markdown
416 | =end markdown
417 | method element-by-link-text(Str $link-text) {
418 | return self._element( 'link text', $link-text );
419 | }
420 |
421 | =begin markdown
422 | =end markdown
423 | method element-by-partial-link-text(Str $partial-link-text) {
424 | return self._element( 'partial link text', $partial-link-text );
425 | }
426 |
427 | =begin markdown
428 | =end markdown
429 | method element-by-tag-name(Str $tag-name) {
430 | return self._element( 'tag name', $tag-name );
431 | }
432 |
433 | =begin markdown
434 | =end markdown
435 | method element-by-xpath(Str $xpath) {
436 | return self._element( 'xpath', $xpath );
437 | }
438 |
439 | =begin markdown
440 | =end markdown
441 | # POST /session/:sessionId/elements
442 | method _elements(Str $using, Str $value) {
443 | my $elements = self._post(
444 | "elements",
445 | {
446 | 'using' => $using,
447 | 'value' => $value,
448 | }
449 | );
450 |
451 | return unless $elements.defined;
452 | my @results = $elements.map({
453 | Selenium::WebDriver::WebElement.new(
454 | :id( $_ ),
455 | :driver( self )
456 | )
457 | });
458 |
459 | return @results;
460 | }
461 |
462 | =begin markdown
463 | =end markdown
464 | method elements-by-class(Str $class) {
465 | return self._elements( 'class name', $class );
466 | }
467 |
468 | =begin markdown
469 | =end markdown
470 | method elements-by-css(Str $selector) {
471 | return self._elements( 'css selector', $selector );
472 | }
473 |
474 | =begin markdown
475 | =end markdown
476 | method elements-by-id(Str $id) {
477 | return self._elements( 'id', $id );
478 | }
479 |
480 | =begin markdown
481 | =end markdown
482 | method elements-by-name(Str $name) {
483 | return self._elements( 'name', $name );
484 | }
485 |
486 | =begin markdown
487 | =end markdown
488 | method elements-by-link-text(Str $link-text) {
489 | return self._elements( 'link text', $link-text );
490 | }
491 |
492 | =begin markdown
493 | =end markdown
494 | method elements-by-partial-link-text(Str $partial-link-text) {
495 | return self._elements( 'partial link text', $partial-link-text );
496 | }
497 |
498 | =begin markdown
499 | =end markdown
500 | method elements-by-tag-name(Str $tag-name) {
501 | return self._elements( 'tag name', $tag-name );
502 | }
503 |
504 | =begin markdown
505 | =end markdown
506 | method elements-by-xpath(Str $xpath) {
507 | return self._elements( 'xpath', $xpath );
508 | }
509 |
510 | =begin markdown
511 | =end markdown
512 | # POST /session/:sessionId/keys
513 | method send-keys-to-active-element(Str $keys) {
514 | return self._post( "keys", value => $keys.comb );
515 | }
516 |
517 | # GET /session/:sessionId/orientation
518 | multi method orientation {
519 | return self._get( 'orientation' );
520 | }
521 |
522 | # POST /session/:sessionId/orientation
523 | multi method orientation(Str $orientation) {
524 | return self._post( 'orientation', orientation => $orientation );
525 | }
526 |
527 | # GET /session/:sessionId/alert_text
528 | multi method alert-text {
529 | return self._get( 'alert_text' );
530 | }
531 |
532 | # POST /session/:sessionId/alert_text
533 | multi method alert-text(Str $text) {
534 | return self._post( 'alert_text', text => $text );
535 | }
536 |
537 | # POST /session/:sessionId/accept_alert
538 | method accept-alert {
539 | return self._post( 'accept_alert' );
540 | }
541 |
542 | # POST /session/:sessionId/dismiss_alert
543 | method dismiss-alert {
544 | return self._post( 'dismiss_alert' );
545 | }
546 |
547 | # POST /session/:sessionId/buttondown
548 | method button-down(Int $button where $_ eq any(0..2)) {
549 | return self._post( 'buttondown', button => $button );
550 | }
551 |
552 | # POST /session/:sessionId/buttonup
553 | method button-up(Int $button where $_ eq any(0..2)) {
554 | return self._post( 'buttonup', button => $button );
555 | }
556 |
557 | # POST /session/:sessionId/doubleclick
558 | method double-click {
559 | return self._post( 'doubleclick' );
560 | }
561 |
562 | # POST /session/:sessionId/touch/click
563 | method touch-click(Str $element) {
564 | return self._post( 'touch/click', { element => $element} );
565 | }
566 |
567 | # POST /session/:sessionId/touch/down
568 | method touch-down(Int $x, Int $y) {
569 | return self._post( 'touch/down', { x => $x, y => $y } );
570 | }
571 |
572 | # POST /session/:sessionId/touch/up
573 | method touch-up(Int $x, Int $y) {
574 | return self._post( 'touch/up', { x => $x, y => $y } );
575 | }
576 |
577 | # POST session/:sessionId/touch/move
578 | method touch-move(Int $x, Int $y) {
579 | return self._post( 'touch/move', { x => $x, y => $y } );
580 | }
581 |
582 | # POST session/:sessionId/touch/scroll
583 | multi method touch-scroll(Str $element, Int $x-offset, Int $y-offset) {
584 | return self._post(
585 | 'touch/scroll',
586 | { element => $element, xoffset => $x-offset, yoffset => $y-offset }
587 | );
588 | }
589 |
590 | multi method touch-scroll(Int $x-offset, Int $y-offset) {
591 | return self._post(
592 | 'touch/scroll',
593 | { xoffset => $x-offset, yoffset => $y-offset }
594 | );
595 | }
596 |
597 | # POST session/:sessionId/touch/doubleclick
598 | method touch-double-click(Str $element) {
599 | return self._post( 'touch/doubleclick', { element => $element} );
600 | }
601 |
602 | # POST session/:sessionId/touch/longclick
603 | method touch-long-click(Str $element) {
604 | return self._post( 'touch/longclick', { element => $element} );
605 | }
606 |
607 | # POST session/:sessionId/touch/flick
608 | multi method touch-flick(
609 | Str $element,
610 | Int $x-offset,
611 | Int $y-offset,
612 | Int $speed)
613 | {
614 | return self._post(
615 | 'touch/flick',
616 | {
617 | element => $element,
618 | xoffset => $x-offset,
619 | yoffset => $y-offset,
620 | speed => $speed
621 | }
622 | );
623 | }
624 |
625 | multi method touch-flick(Int $x-speed, Int $y-speed) {
626 | return self._post(
627 | 'touch/flick',
628 | {
629 | xspeed => $x-speed,
630 | yspeed => $y-speed
631 | }
632 | );
633 | }
634 |
635 | # GET /session/:sessionId/location
636 | multi method location {
637 | return self._get( 'location' );
638 | }
639 |
640 | # POST /session/:sessionId/location
641 | multi method location(Hash $location) {
642 | return self._post( 'location', location => $location );
643 | }
644 |
645 | # GET /session/:sessionId/local_storage
646 | method local-storage {
647 | return self._get( 'local_storage' );
648 | }
649 |
650 | # POST /session/:sessionId/local_storage
651 | method add-to-local-storage(Str $key, Str $value) {
652 | return self._post( 'local_storage', key => $key, value => $value );
653 | }
654 |
655 | # DELETE /session/:sessionId/local_storage
656 | method clear-local-storage {
657 | return self._delete( 'local_storage' );
658 | }
659 |
660 | # GET /session/:sessionId/local_storage/key/:key
661 | method get-from-local-storage(Str $key) {
662 | return self._get( "local_storage/key/$key" );
663 | }
664 |
665 | # DELETE /session/:sessionId/local_storage/key/:key
666 | method delete-from-local-storage(Str $key) {
667 | return self._delete( "local_storage/key/$key" );
668 | }
669 |
670 | # GET /session/:sessionId/local_storage/size
671 | method local-storage-size(Str $key) returns Int {
672 | return self._get( 'local_storage/size' );
673 | }
674 |
675 | # GET /session/:sessionId/session_storage
676 | method session-storage {
677 | return self._get( 'session_storage' );
678 | }
679 |
680 | # POST /session/:sessionId/session_storage
681 | method add-to-session-storage(Str $key, Str $value) {
682 | return self._post( 'session_storage', key => $key, value => $value );
683 | }
684 |
685 | # DELETE /session/:sessionId/session_storage
686 | method clear-session-storage {
687 | return self._delete( 'session_storage' );
688 | }
689 |
690 | # GET /session/:sessionId/session_storage/key/:key
691 | method get-from-session-storage(Str $key) {
692 | return self._get( 'session_storage/key/$key' );
693 | }
694 |
695 | # DELETE /session/:sessionId/session_storage/key/:key
696 | method delete-from-session-storage(Str $key) {
697 | return self._delete( 'session_storage/key/$key' );
698 | }
699 |
700 | # GET /session/:sessionId/session_storage/size
701 | method session-storage-size(Str $key) returns Int {
702 | return self._get( 'session_storage/size' );
703 | }
704 |
705 | # POST /session/:sessionId/log
706 | method log(Str $type) {
707 | return self._post( 'log', type => $type );
708 | }
709 |
710 | # GET /session/:sessionId/log/types
711 | method log-types {
712 | return self._get( 'log/types' );
713 | }
714 |
715 | # GET /session/:sessionId/application_cache/status
716 | method application-cache-status {
717 | return self._get( 'application_cache/status' );
718 | }
719 |
720 | method _die(Str $method, Str $command, $response) {
721 | say "Died while doing '$method $command', content:\n";
722 | say $response.content if $response.defined;
723 | say "end of content\n";
724 | return unless $response.defined;
725 | my $o = from-json($response.content);
726 |
727 | my $error = $o;
728 | Selenium::WebDriver::X::Error.new(
729 | reason => $error // '',
730 | screenshot => $error,
731 | class => $error // ''
732 | ).throw;
733 | }
734 | =begin markdown
735 | =end markdown
736 | method _execute-command(Str $method, Str $command, Hash $params = {}) {
737 | say "$method $command with params " ~ $params.perl if self.debug;
738 |
739 | my $ua = HTTP::UserAgent.new;
740 | $ua.timeout = 5;
741 | my $url = uri_encode("http://" ~ self.host ~ ":" ~ self.port ~ self.url-prefix ~ $command);
742 | my $response;
743 | if ( $method eq 'POST' ) {
744 | my $content = to-json($params);
745 | my $request = HTTP::Request.new(
746 | :POST($url),
747 | :Content-Length($content.chars),
748 | :Content-Type("application/json;charset=UTF-8"),
749 | :Connection("close"),
750 | );
751 | $request.add-content($content);
752 | $response = $ua.request($request);
753 | }
754 | elsif ( $method eq 'GET' ) {
755 | $response = $ua.get( $url );
756 | }
757 | elsif ( $method eq 'DELETE' ) {
758 | my $request = HTTP::Request.new(
759 | :DELETE($url),
760 | :Content-Type("application/json;charset=UTF-8"),
761 | :Connection("close"),
762 | );
763 | $response = $ua.request($request);
764 | }
765 | else {
766 | die qq{Unknown method "$method"};
767 | }
768 |
769 | # Since we didnt get a response here, return nothing
770 | return unless $response.defined;
771 |
772 | my $result;
773 | if ( $response.is-success ) {
774 | $result = from-json( $response.content );
775 | }
776 | else {
777 | self._die($method, $command, $response);
778 | warn "FAILED: " ~ $response.status-line if self.debug;
779 | }
780 |
781 | return $result;
782 | }
783 |
784 | method _get(Str $command) {
785 | my $result = self._execute-command(
786 | "GET",
787 | "/session/$(self.session-id)/$command",
788 | );
789 |
790 | return unless $result.defined;
791 | return $result;
792 | }
793 |
794 | method _post(Str $command, Hash $params = {}) {
795 | return self._execute-command(
796 | "POST",
797 | "/session/$(self.session-id)/$command",
798 | $params
799 | );
800 | }
801 |
802 | method _delete(Str $command, Hash $params = {}) {
803 | return self._execute-command(
804 | "DELETE",
805 | "/session/$(self.session-id)/$command",
806 | $params
807 | );
808 | }
809 |
810 | =begin markdown
811 | ### _empty-port
812 |
813 | Find a random port in the dynamic/private range
814 |
815 | According to [IANA](http://www.iana.org/assignments/port-numbers), dynamic
816 | and/or private are in the range 49152 to 65535
817 |
818 | =end markdown
819 | method _empty-port {
820 | while 1 {
821 | # Find a random port in the dynamic/private range
822 | my $port = (49152..65535).pick;
823 |
824 | # Open and close a TCP connection to it
825 | my $socket = IO::Socket::INET.new( :host('127.0.0.1'), :port($port) );
826 | $socket.close;
827 |
828 | CATCH {
829 | default {
830 | # If it fails, it may mean that this port is not bound
831 | return $port;
832 | }
833 | }
834 | }
835 | }
836 |
--------------------------------------------------------------------------------
/lib/Selenium/WebDriver/X/Error.rakumod:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | unit class Selenium::WebDriver::X::Error is Exception;
5 |
6 | has $.reason is rw;
7 | has $.screenshot is rw;
8 | has $.class is rw;
9 |
10 | method message {
11 | return
12 | "\n" ~ ("-" x 80) ~ "\n" ~
13 | "Error:\n" ~
14 | "Reason: \n" ~ $.reason ~ "\n" ~
15 | "Type: \n" ~ $.class ~ "\n" ~
16 | "Has a screenshot:\n " ~ $.screenshot.defined ~ "\n" ~
17 | ("-" x 80);
18 | }
19 |
--------------------------------------------------------------------------------
/logotype/logo_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azawawi/raku-selenium-webdriver/ec6e0991800a5e5daf56ae6cfa191d3efa63856d/logotype/logo_32x32.png
--------------------------------------------------------------------------------
/resources/firefox_extension/prefs.json:
--------------------------------------------------------------------------------
1 | {
2 | "frozen": {
3 | "app.update.auto": false,
4 | "app.update.enabled": false,
5 | "browser.displayedE10SNotice": 4,
6 | "browser.download.manager.showWhenStarting": false,
7 | "browser.EULA.override": true,
8 | "browser.EULA.3.accepted": true,
9 | "browser.link.open_external": 2,
10 | "browser.link.open_newwindow": 2,
11 | "browser.offline": false,
12 | "browser.reader.detectedFirstArticle": true,
13 | "browser.safebrowsing.enabled": false,
14 | "browser.safebrowsing.malware.enabled": false,
15 | "browser.search.update": false,
16 | "browser.selfsupport.url" : "",
17 | "browser.sessionstore.resume_from_crash": false,
18 | "browser.shell.checkDefaultBrowser": false,
19 | "browser.tabs.warnOnClose": false,
20 | "browser.tabs.warnOnOpen": false,
21 | "datareporting.healthreport.service.enabled": false,
22 | "datareporting.healthreport.uploadEnabled": false,
23 | "datareporting.healthreport.service.firstRun": false,
24 | "datareporting.healthreport.logging.consoleEnabled": false,
25 | "datareporting.policy.dataSubmissionEnabled": false,
26 | "datareporting.policy.dataSubmissionPolicyAccepted": false,
27 | "devtools.errorconsole.enabled": true,
28 | "dom.disable_open_during_load": false,
29 | "extensions.autoDisableScopes": 10,
30 | "extensions.blocklist.enabled": false,
31 | "extensions.logging.enabled": true,
32 | "extensions.update.enabled": false,
33 | "extensions.update.notifyUser": false,
34 | "javascript.enabled": true,
35 | "network.manage-offline-status": false,
36 | "network.http.phishy-userpass-length": 255,
37 | "offline-apps.allow_by_default": true,
38 | "prompts.tab_modal.enabled": false,
39 | "security.csp.enable": false,
40 | "security.fileuri.origin_policy": 3,
41 | "security.fileuri.strict_origin_policy": false,
42 | "security.warn_entering_secure": false,
43 | "security.warn_entering_secure.show_once": false,
44 | "security.warn_entering_weak": false,
45 | "security.warn_entering_weak.show_once": false,
46 | "security.warn_leaving_secure": false,
47 | "security.warn_leaving_secure.show_once": false,
48 | "security.warn_submit_insecure": false,
49 | "security.warn_viewing_mixed": false,
50 | "security.warn_viewing_mixed.show_once": false,
51 | "signon.rememberSignons": false,
52 | "toolkit.networkmanager.disable": true,
53 | "toolkit.telemetry.prompted": 2,
54 | "toolkit.telemetry.enabled": false,
55 | "toolkit.telemetry.rejected": true,
56 | "xpinstall.signatures.required": false
57 | },
58 | "mutable": {
59 | "browser.dom.window.dump.enabled": true,
60 | "browser.newtab.url": "about:blank",
61 | "browser.newtabpage.enabled": false,
62 | "browser.startup.page": 0,
63 | "browser.startup.homepage": "about:blank",
64 | "dom.max_chrome_script_run_time": 30,
65 | "dom.max_script_run_time": 30,
66 | "dom.report_all_js_exceptions": true,
67 | "javascript.options.showInConsole": true,
68 | "network.http.max-connections-per-server": 10,
69 | "startup.homepage_welcome_url": "about:blank",
70 | "webdriver_accept_untrusted_certs": true,
71 | "webdriver_assume_untrusted_issuer": true
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/resources/firefox_extension/webdriver.xpi:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azawawi/raku-selenium-webdriver/ec6e0991800a5e5daf56ae6cfa191d3efa63856d/resources/firefox_extension/webdriver.xpi
--------------------------------------------------------------------------------
/t/01-load.rakutest:
--------------------------------------------------------------------------------
1 | use v6;
2 |
3 | use Test;
4 | use lib 'lib';
5 |
6 | plan 1;
7 |
8 | use Selenium::WebDriver;
9 | ok 1, "'use Selenium::WebDriver' worked!";
10 |
--------------------------------------------------------------------------------
/t/02-phantomjs.rakutest:
--------------------------------------------------------------------------------
1 | use v6;
2 |
3 | use Test;
4 | use lib 'lib';
5 |
6 | # Methods to test
7 | my @methods = 'url', 'source', 'move-to', 'click', 'quit',
8 | 'screenshot', 'save-screenshot', 'forward', 'back', 'refresh',
9 | 'element-by-class', 'element-by-css', 'element-by-id',
10 | 'element-by-name', 'element-by-link-text', 'element-by-partial-link-text',
11 | 'element-by-tag-name', 'element-by-xpath', 'elements-by-class',
12 | 'elements-by-css', 'elements-by-id', 'elements-by-name',
13 | 'elements-by-link-text', 'elements-by-partial-link-text',
14 | 'elements-by-tag-name', 'elements-by-xpath', 'sessions', 'capabilities',
15 | 'script-timeout', 'implicit-timeout', 'page-load-timeout',
16 | 'current-window', 'windows', 'status',
17 | 'async-script-timeout', 'implicit-wait-timeout', 'execute', 'execute-async',
18 | 'ime-available-engines', 'ime-active-engine', 'ime-activated',
19 | 'ime-activated', 'ime-activated', 'frame', 'frame-parent',
20 | 'cookies', 'cookie', 'delete-all-cookies', 'delete-cookie',
21 | 'send-keys-to-active-element', 'orientation', 'alert-text', 'accept-alert',
22 | 'dismiss-alert', 'button-down', 'button-up', 'double-click', 'touch-click',
23 | 'touch-down', 'touch-up', 'touch-move', 'touch-scroll', 'touch-double-click',
24 | 'touch-long-click', 'touch-flick', 'location',
25 | 'local-storage', 'add-to-local-storage', 'clear-local-storage',
26 | 'get-from-local-storage', 'delete-from-local-storage', 'local-storage-size',
27 | 'session-storage', 'add-to-session-storage', 'clear-session-storage',
28 | 'get-from-session-storage', 'delete-from-session-storage',
29 | 'session-storage-size', 'log', 'log-types', 'application-cache-status';
30 |
31 | plan @methods.elems + 14;
32 |
33 | use Selenium::WebDriver::PhantomJS;
34 | ok 1, "'use Selenium::WebDriver::PhantomJS' worked!";
35 |
36 | {
37 | # Skip tests if phantomjs is not found
38 | use File::Which;
39 | unless which('phantomjs') {
40 | skip-rest("phantomjs is not installed. skipping tests...");
41 | exit;
42 | }
43 | }
44 |
45 | my $driver = Selenium::WebDriver::PhantomJS.new(:debug, :max-attempts(30), :port(5555));
46 | ok $driver, "Selenium::WebDriver::PhantomJS.new worked";
47 |
48 | for @methods -> $method {
49 | ok Selenium::WebDriver::PhantomJS.can($method),
50 | "Selenium::WebDriver::PhantomJS.$method is found";
51 | }
52 |
53 | {
54 | my $sessions = $driver.sessions;
55 | ok $sessions.defined, "Sessions returned a defined value";
56 | ok $sessions ~~ Array, "Sessions is an array";
57 | ok $sessions.elems == 1, "Only One session should be there";
58 | ok $sessions[0] ~~ Str, "And we have a sessionId";
59 | }
60 |
61 | {
62 | my $capabilities = $driver.capabilities;
63 | ok $capabilities.defined, "capabilities returned a defined value";
64 | ok $capabilities ~~ Hash, "capabilities is a hash";
65 | ok $capabilities ~~ Str, "And we have a sessionId";
66 | }
67 |
68 | {
69 | my $current-window = $driver.current-window;
70 | ok $current-window.defined, "current-window returned a defined value";
71 | ok $current-window.handle ~~ Str, "current-window handle is a string";
72 | }
73 |
74 | {
75 | my @windows = $driver.windows;
76 | ok @windows.defined, "windows returned a defined value";
77 | ok @windows.elems > 0, "windows has at least one active window";
78 | ok @windows[0] ~~ Selenium::WebDriver::WebWindow, "first element is a window";
79 | }
80 |
81 | LEAVE {
82 | $driver.quit if $driver.defined;
83 | }
84 |
--------------------------------------------------------------------------------
/t/03-webelement.rakutest:
--------------------------------------------------------------------------------
1 | use v6;
2 |
3 | use Test;
4 | use lib 'lib';
5 |
6 | # Methods to test
7 | my @methods = 'click', 'send-keys', 'tag-name', 'selected', 'enabled', 'attr',
8 | 'equals-by-id', 'displayed', 'location', 'location-in-view', 'size' ,
9 | 'css', 'submit', 'text', 'clear';
10 |
11 | plan @methods.elems + 2;
12 |
13 | use Selenium::WebDriver::WebElement;
14 | ok 1, "'use Selenium::WebDriver::WebElement' worked!";
15 |
16 | my $element = Selenium::WebDriver::WebElement.new;
17 | ok $element, "Selenium::WebDriver::WebElement.new worked";
18 |
19 | for @methods -> $method {
20 | ok Selenium::WebDriver::WebElement.can($method),
21 | "Selenium::WebDriver::WebElement.$method is found";
22 | }
23 |
--------------------------------------------------------------------------------
/t/04-webwindow.rakutest:
--------------------------------------------------------------------------------
1 | use v6;
2 |
3 | use Test;
4 | use lib 'lib';
5 |
6 | # Methods to test
7 | my @methods = 'current', 'close-current', 'size', 'position', 'maximize';
8 |
9 | plan @methods.elems + 2;
10 |
11 | use Selenium::WebDriver::WebWindow;
12 | ok 1, "'use Selenium::WebDriver::WebWindow' worked!";
13 |
14 | my $element = Selenium::WebDriver::WebWindow.new;
15 | ok $element, "Selenium::WebDriver::WebWindow.new worked";
16 |
17 | for @methods -> $method {
18 | ok Selenium::WebDriver::WebWindow.can($method),
19 | "Selenium::WebDriver::WebWindow.$method is found";
20 | }
21 |
--------------------------------------------------------------------------------
/t/05-firefox.rakutest:
--------------------------------------------------------------------------------
1 | use v6;
2 |
3 | use Test;
4 | use lib 'lib';
5 |
6 | # Methods to test
7 | my @methods = 'url', 'source', 'move-to', 'click', 'quit',
8 | 'screenshot', 'save-screenshot', 'forward', 'back', 'refresh',
9 | 'element-by-class', 'element-by-css', 'element-by-id',
10 | 'element-by-name', 'element-by-link-text', 'element-by-partial-link-text',
11 | 'element-by-tag-name', 'element-by-xpath', 'elements-by-class',
12 | 'elements-by-css', 'elements-by-id', 'elements-by-name',
13 | 'elements-by-link-text', 'elements-by-partial-link-text',
14 | 'elements-by-tag-name', 'elements-by-xpath', 'sessions', 'capabilities',
15 | 'script-timeout', 'implicit-timeout', 'page-load-timeout',
16 | 'current-window', 'windows', 'status',
17 | 'async-script-timeout', 'implicit-wait-timeout', 'execute', 'execute-async',
18 | 'ime-available-engines', 'ime-active-engine', 'ime-activated',
19 | 'ime-activated', 'ime-activated', 'frame', 'frame-parent',
20 | 'cookies', 'cookie', 'delete-all-cookies', 'delete-cookie',
21 | 'send-keys-to-active-element', 'orientation', 'alert-text', 'accept-alert',
22 | 'dismiss-alert', 'button-down', 'button-up', 'double-click', 'touch-click',
23 | 'touch-down', 'touch-up', 'touch-move', 'touch-scroll', 'touch-double-click',
24 | 'touch-long-click', 'touch-flick', 'location',
25 | 'local-storage', 'add-to-local-storage', 'clear-local-storage',
26 | 'get-from-local-storage', 'delete-from-local-storage', 'local-storage-size',
27 | 'session-storage', 'add-to-session-storage', 'clear-session-storage',
28 | 'get-from-session-storage', 'delete-from-session-storage',
29 | 'session-storage-size', 'log', 'log-types', 'application-cache-status';
30 |
31 | plan @methods.elems + 10;
32 |
33 | use Selenium::WebDriver::Firefox;
34 | ok 1, "'use Selenium::WebDriver::Firefox' worked!";
35 |
36 | {
37 | # Skip tests if firefox is not found
38 | use File::Which;
39 | unless which('firefox') {
40 | skip-rest("Firefox is not installed. skipping tests...");
41 | exit;
42 | }
43 | }
44 |
45 | my $driver = Selenium::WebDriver::Firefox.new;
46 | ok $driver, "Selenium::WebDriver::Firefox.new worked";
47 |
48 | for @methods -> $method {
49 | ok Selenium::WebDriver::Firefox.can($method),
50 | "Selenium::WebDriver::Firefox.$method is found";
51 | }
52 |
53 | #{
54 | # my $sessions = $driver.sessions;
55 | # ok $sessions.defined, "Sessions returned a defined value";
56 | # ok $sessions ~~ Array, "Sessions is an array";
57 | # ok $sessions.elems == 1, "Only One session should be there";
58 | # ok $sessions[0] ~~ Str, "And we have a sessionId";
59 | #}
60 |
61 | {
62 | my $capabilities = $driver.capabilities;
63 | ok $capabilities.defined, "capabilities returned a defined value";
64 | ok $capabilities ~~ Hash, "capabilities is a hash";
65 | ok $capabilities ~~ Str, "And we have a sessionId";
66 | }
67 |
68 | {
69 | my $current-window = $driver.current-window;
70 | ok $current-window.defined, "current-window returned a defined value";
71 | ok $current-window.handle ~~ Str, "current-window handle is a string";
72 | }
73 |
74 | {
75 | my @windows = $driver.windows;
76 | ok @windows.defined, "windows returned a defined value";
77 | ok @windows.elems > 0, "windows has at least one active window";
78 | ok @windows[0] ~~ Selenium::WebDriver::WebWindow, "first element is a window";
79 | }
80 |
81 | LEAVE {
82 | $driver.quit if $driver.defined;
83 | }
84 |
--------------------------------------------------------------------------------
/t/06-chrome.rakutest:
--------------------------------------------------------------------------------
1 | use v6;
2 |
3 | use Test;
4 | use lib 'lib';
5 |
6 | # Methods to test
7 | my @methods = 'url', 'source', 'move-to', 'click', 'quit',
8 | 'screenshot', 'save-screenshot', 'forward', 'back', 'refresh',
9 | 'element-by-class', 'element-by-css', 'element-by-id',
10 | 'element-by-name', 'element-by-link-text', 'element-by-partial-link-text',
11 | 'element-by-tag-name', 'element-by-xpath', 'elements-by-class',
12 | 'elements-by-css', 'elements-by-id', 'elements-by-name',
13 | 'elements-by-link-text', 'elements-by-partial-link-text',
14 | 'elements-by-tag-name', 'elements-by-xpath', 'sessions', 'capabilities',
15 | 'script-timeout', 'implicit-timeout', 'page-load-timeout',
16 | 'current-window', 'windows', 'status',
17 | 'async-script-timeout', 'implicit-wait-timeout', 'execute', 'execute-async',
18 | 'ime-available-engines', 'ime-active-engine', 'ime-activated',
19 | 'ime-activated', 'ime-activated', 'frame', 'frame-parent',
20 | 'cookies', 'cookie', 'delete-all-cookies', 'delete-cookie',
21 | 'send-keys-to-active-element', 'orientation', 'alert-text', 'accept-alert',
22 | 'dismiss-alert', 'button-down', 'button-up', 'double-click', 'touch-click',
23 | 'touch-down', 'touch-up', 'touch-move', 'touch-scroll', 'touch-double-click',
24 | 'touch-long-click', 'touch-flick', 'location',
25 | 'local-storage', 'add-to-local-storage', 'clear-local-storage',
26 | 'get-from-local-storage', 'delete-from-local-storage', 'local-storage-size',
27 | 'session-storage', 'add-to-session-storage', 'clear-session-storage',
28 | 'get-from-session-storage', 'delete-from-session-storage',
29 | 'session-storage-size', 'log', 'log-types', 'application-cache-status';
30 |
31 | plan @methods.elems + 14;
32 |
33 | use Selenium::WebDriver::Chrome;
34 | ok 1, "'use Selenium::WebDriver::Chrome' worked!";
35 |
36 | {
37 | # Skip tests if chromedriver is not found
38 | use File::Which;
39 | unless which('chromedriver') {
40 | skip-rest("chromedriver is not installed. skipping tests...");
41 | exit;
42 | }
43 | }
44 |
45 | my $driver = Selenium::WebDriver::Chrome.new;
46 | ok $driver, "Selenium::WebDriver::Chrome.new worked";
47 |
48 | for @methods -> $method {
49 | ok Selenium::WebDriver::Chrome.can($method),
50 | "Selenium::WebDriver::Chrome.$method is found";
51 | }
52 |
53 | {
54 | my $sessions = $driver.sessions;
55 | ok $sessions.defined, "Sessions returned a defined value";
56 | ok $sessions ~~ Array, "Sessions is an array";
57 | ok $sessions.elems == 1, "Only One session should be there";
58 | ok $sessions[0] ~~ Str, "And we have a sessionId";
59 | }
60 |
61 | {
62 | my $capabilities = $driver.capabilities;
63 | ok $capabilities.defined, "capabilities returned a defined value";
64 | ok $capabilities ~~ Hash, "capabilities is a hash";
65 | ok $capabilities ~~ Str, "And we have a sessionId";
66 | }
67 |
68 | {
69 | my $current-window = $driver.current-window;
70 | ok $current-window.defined, "current-window returned a defined value";
71 | ok $current-window.handle ~~ Str, "current-window handle is a string";
72 | }
73 |
74 | {
75 | my @windows = $driver.windows;
76 | ok @windows.defined, "windows returned a defined value";
77 | ok @windows.elems > 0, "windows has at least one active window";
78 | ok @windows[0] ~~ Selenium::WebDriver::WebWindow, "first element is a window";
79 | }
80 |
81 | LEAVE {
82 | $driver.quit if $driver.defined;
83 | }
84 |
--------------------------------------------------------------------------------
/t/07-blackberry.rakutest:
--------------------------------------------------------------------------------
1 | use v6;
2 |
3 | use Test;
4 | use lib 'lib';
5 |
6 | # Methods to test
7 | my @methods = 'url', 'source', 'move-to', 'click', 'quit',
8 | 'screenshot', 'save-screenshot', 'forward', 'back', 'refresh',
9 | 'element-by-class', 'element-by-css', 'element-by-id',
10 | 'element-by-name', 'element-by-link-text', 'element-by-partial-link-text',
11 | 'element-by-tag-name', 'element-by-xpath', 'elements-by-class',
12 | 'elements-by-css', 'elements-by-id', 'elements-by-name',
13 | 'elements-by-link-text', 'elements-by-partial-link-text',
14 | 'elements-by-tag-name', 'elements-by-xpath', 'sessions', 'capabilities',
15 | 'script-timeout', 'implicit-timeout', 'page-load-timeout',
16 | 'current-window', 'windows', 'status',
17 | 'async-script-timeout', 'implicit-wait-timeout', 'execute', 'execute-async',
18 | 'ime-available-engines', 'ime-active-engine', 'ime-activated',
19 | 'ime-activated', 'ime-activated', 'frame', 'frame-parent',
20 | 'cookies', 'cookie', 'delete-all-cookies', 'delete-cookie',
21 | 'send-keys-to-active-element', 'orientation', 'alert-text', 'accept-alert',
22 | 'dismiss-alert', 'button-down', 'button-up', 'double-click', 'touch-click',
23 | 'touch-down', 'touch-up', 'touch-move', 'touch-scroll', 'touch-double-click',
24 | 'touch-long-click', 'touch-flick', 'location',
25 | 'local-storage', 'add-to-local-storage', 'clear-local-storage',
26 | 'get-from-local-storage', 'delete-from-local-storage', 'local-storage-size',
27 | 'session-storage', 'add-to-session-storage', 'clear-session-storage',
28 | 'get-from-session-storage', 'delete-from-session-storage',
29 | 'session-storage-size', 'log', 'log-types', 'application-cache-status';
30 |
31 | plan @methods.elems + 14;
32 |
33 | use Selenium::WebDriver::BlackBerry;
34 | ok 1, "'use Selenium::WebDriver::BlackBerry' worked!";
35 |
36 | unless %*ENV {
37 | skip-rest("Environment variable SELENIUM_BLACKBERRY_IP is not set. skipping tests...");
38 | exit;
39 | }
40 |
41 | my $driver = Selenium::WebDriver::BlackBerry.new(%*ENV);
42 | ok $driver, "Selenium::WebDriver::BlackBerry.new worked";
43 |
44 | for @methods -> $method {
45 | ok Selenium::WebDriver::BlackBerry.can($method),
46 | "Selenium::WebDriver::BlackBerry.$method is found";
47 | }
48 |
49 | {
50 | my $sessions = $driver.sessions;
51 | ok $sessions.defined, "Sessions returned a defined value";
52 | ok $sessions ~~ Array, "Sessions is an array";
53 | ok $sessions.elems == 1, "Only One session should be there";
54 | ok $sessions[0] ~~ Int, "And we have a sessionId";
55 | }
56 |
57 | {
58 | my $capabilities = $driver.capabilities;
59 | ok $capabilities.defined, "capabilities returned a defined value";
60 | ok $capabilities ~~ Hash, "capabilities is a hash";
61 | ok $capabilities ~~ Str, "And we have a sessionId";
62 | }
63 |
64 | {
65 | my $current-window = $driver.current-window;
66 | ok $current-window.defined, "current-window returned a defined value";
67 | ok $current-window.handle ~~ Str, "current-window handle is a string";
68 | }
69 |
70 | {
71 | my @windows = $driver.windows;
72 | ok @windows.defined, "windows returned a defined value";
73 | ok @windows.elems > 0, "windows has at least one active window";
74 | ok @windows[0] ~~ Selenium::WebDriver::WebWindow, "first element is a window";
75 | }
76 |
77 | # This would make the BlackBerry Browser quit. So we avoid that.
78 | # LEAVE {
79 | # $driver.quit if $driver.defined;
80 | # }
81 |
--------------------------------------------------------------------------------
/t/99-author-meta.rakutest:
--------------------------------------------------------------------------------
1 |
2 | use v6;
3 |
4 | use Test;
5 |
6 | plan 1;
7 |
8 | if ?%*ENV {
9 | require Test::META <&meta-ok>;
10 | meta-ok;
11 | done-testing;
12 | } else {
13 | skip-rest "Skipping author test";
14 | exit;
15 | }
16 |
--------------------------------------------------------------------------------