BehatGeneral.php 56.6 KB
Newer Older
Son Nguyen's avatar
Son Nguyen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
/**
 * @package    mahara
 * @subpackage test/behat
 * @author     Son Nguyen, Catalyst IT Ltd
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
 * @copyright  portions from Moodle Behat, 2013 David Monllaó
 *
 */

/**
 * General use steps definitions.
 *
 */

Son Nguyen's avatar
Son Nguyen committed
17
require_once(__DIR__ . '/BehatBase.php');
Son Nguyen's avatar
Son Nguyen committed
18
19
20
21
22

use Behat\Mink\Exception\ExpectationException as ExpectationException,
    Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
    Behat\Mink\Exception\DriverException as DriverException,
    WebDriver\Exception\NoSuchElement as NoSuchElement,
Son Nguyen's avatar
Son Nguyen committed
23
    WebDriver\Exception\StaleElementReference as StaleElementReference;
Son Nguyen's avatar
Son Nguyen committed
24
25
26
27
28
29
30
31
32
33
34
35
36

/**
 * Cross plugin steps definitions.
 *
 * Basic web application definitions from MinkExtension and
 * BehatchExtension. Definitions modified according to our needs
 * when necessary and including only the ones we need to avoid
 * overlapping and confusion.
 *
 */
class BehatGeneral extends BehatBase {

    /**
Son Nguyen's avatar
Son Nguyen committed
37
     * Login as a mahara user
Son Nguyen's avatar
Son Nguyen committed
38
     *
Son Nguyen's avatar
Son Nguyen committed
39
     * @Given /^I log in as "(?P<username>(?:[^"]|\\")*)" with password "(?P<password>(?:[^"]|\\")*)"$/
Son Nguyen's avatar
Son Nguyen committed
40
     */
Son Nguyen's avatar
Son Nguyen committed
41
    public function i_login_as($username, $password) {
Son Nguyen's avatar
Son Nguyen committed
42
43
44
45
46
47
48
49
50
        $this->visitPath("/");
        $this->wait_until_the_page_is_ready();
        $this->getSession()->getPage()->fillField(
            "login_username",
            $username
        );
        $this->getSession()->getPage()->fillField(
            "login_password",
            $password
Son Nguyen's avatar
Son Nguyen committed
51
        );
Son Nguyen's avatar
Son Nguyen committed
52
        $this->getSession()->getPage()->pressButton("Login");
Son Nguyen's avatar
Son Nguyen committed
53
54
    }

Son Nguyen's avatar
Son Nguyen committed
55
56
57
58
59
60
    /**
     * Log out of Mahara
     *
     * @Given /^I log out$/
     */
    public function i_logout() {
Son Nguyen's avatar
Son Nguyen committed
61
62
        $this->visitPath("/");
        $this->wait_until_the_page_is_ready();
63
        $this->i_click_on("Show User Menu");
Son Nguyen's avatar
Son Nguyen committed
64
        $this->i_follow_in_the("Logout", "//header//li[contains(concat(' ', normalize-space(@class), ' '), ' btn-logout ')]", "xpath_element");
Son Nguyen's avatar
Son Nguyen committed
65
66
    }

Son Nguyen's avatar
Son Nguyen committed
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    /**
     * Follows the page redirection. Use this step after any action that shows a message and waits for a redirection
     *
     * @Given /^I wait to be redirected$/
     */
    public function i_wait_to_be_redirected() {

        // Xpath and processes based on core_renderer::redirect_message(), core_renderer::$metarefreshtag and
        // moodle_page::$periodicrefreshdelay possible values.
        if (!$metarefresh = $this->getSession()->getPage()->find('xpath', "//head/descendant::meta[@http-equiv='refresh']")) {
            // We don't fail the scenario if no redirection with message is found to avoid race condition false failures.
            return true;
        }

        // Wrapped in try & catch in case the redirection has already been executed.
        try {
            $content = $metarefresh->getAttribute('content');
Son Nguyen's avatar
Son Nguyen committed
84
85
        }
        catch (NoSuchElement $e) {
Son Nguyen's avatar
Son Nguyen committed
86
            return true;
Son Nguyen's avatar
Son Nguyen committed
87
88
        }
        catch (StaleElementReference $e) {
Son Nguyen's avatar
Son Nguyen committed
89
90
91
92
93
94
95
96
97
98
99
            return true;
        }

        // Getting the refresh time and the url if present.
        if (strstr($content, 'url') != false) {

            list($waittime, $url) = explode(';', $content);

            // Cleaning the URL value.
            $url = trim(substr($url, strpos($url, 'http')));

Son Nguyen's avatar
Son Nguyen committed
100
101
        }
        else {
Son Nguyen's avatar
Son Nguyen committed
102
103
104
105
106
107
108
109
110
            // Just wait then.
            $waittime = $content;
        }


        // Wait until the URL change is executed.
        if ($this->running_javascript()) {
            $this->getSession()->wait($waittime * 1000, false);

Son Nguyen's avatar
Son Nguyen committed
111
112
        }
        else if (!empty($url)) {
Son Nguyen's avatar
Son Nguyen committed
113
114
115
            // We redirect directly as we can not wait for an automatic redirection.
            $this->getSession()->getDriver()->getClient()->request('get', $url);

Son Nguyen's avatar
Son Nguyen committed
116
117
        }
        else {
Son Nguyen's avatar
Son Nguyen committed
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
            // Reload the page if no URL was provided.
            $this->getSession()->getDriver()->reload();
        }
    }

    /**
     * Switches to the specified iframe.
     *
     * @Given /^I switch to "(?P<iframe_name_string>(?:[^"]|\\")*)" iframe$/
     * @param string $iframename
     */
    public function switch_to_iframe($iframename) {

        // We spin to give time to the iframe to be loaded.
        // Using extended timeout as we don't know about which
        // kind of iframe will be loaded.
        $this->spin(
            function($context, $iframename) {
                $context->getSession()->switchToIFrame($iframename);

                // If no exception we are done.
                return true;
            },
            $iframename,
            self::EXTENDED_TIMEOUT
        );
    }

    /**
     * Switches to the main Moodle frame.
     *
     * @Given /^I switch to the main frame$/
     */
    public function switch_to_the_main_frame() {
        $this->getSession()->switchToIFrame();
    }

    /**
     * Switches to the specified window. Useful when interacting with popup windows.
     *
     * @Given /^I switch to "(?P<window_name_string>(?:[^"]|\\")*)" window$/
     * @param string $windowname
     */
    public function switch_to_window($windowname) {
        $this->getSession()->switchToWindow($windowname);
    }

    /**
     * Switches to the main Moodle window. Useful when you finish interacting with popup windows.
     *
     * @Given /^I switch to the main window$/
     */
    public function switch_to_the_main_window() {
        $this->getSession()->switchToWindow();
    }

    /**
     * Accepts the currently displayed alert dialog. This step does not work in all the browsers, consider it experimental.
Son Nguyen's avatar
Son Nguyen committed
176
     * @When /^I accept the alert popup$/
Son Nguyen's avatar
Son Nguyen committed
177
     */
Son Nguyen's avatar
Son Nguyen committed
178
    public function i_accept_alert_popup() {
Son Nguyen's avatar
Son Nguyen committed
179
180
181
182
        $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
    }

    /**
Son Nguyen's avatar
Son Nguyen committed
183
184
185
186
187
188
189
190
191
192
     * Confirm the currently displayed confirm dialog. This step does not work in all the browsers, consider it experimental.
     * @When /^I accept the confirm popup$/
     */
    public function i_accept_confirm_popup() {
        $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
    }

    /**
     * Cancel the currently displayed confirm dialog. This step does not work in all the browsers, consider it experimental.
     * @When /^I cancel the confirm popup$/
Son Nguyen's avatar
Son Nguyen committed
193
     */
Son Nguyen's avatar
Son Nguyen committed
194
195
196
    public function i_cancel_confirm_popup() {
        $this->getSession()->getDriver()->getWebDriverSession()->dismiss_alert();
    }
Son Nguyen's avatar
Son Nguyen committed
197

Son Nguyen's avatar
Son Nguyen committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
    /**
     * Fill the text in prompt popup window. This step does not work in all the browsers, consider it experimental.
     * @When /^I fill in "(?P<text>(?:[^"]|\\")*)" for popup$/
     * @param string $text
     */
    public function i_fill_in_for_popup($text) {
        $this->getSession()->getDriver()->getWebDriverSession()->postAlert_text($text);
    }

    /**
     * Assert the text in popup window. This step does not work in all the browsers, consider it experimental.
     * @Then /^I should see "(?P<text>(?:[^"]|\\")*)" in popup$/
     * @param string $text
     * @return bool
     */
    public function i_should_see_in_popup($text) {
        return $text == $this->getSession()->getDriver()->getWebDriverSession()->getAlert_text();
Son Nguyen's avatar
Son Nguyen committed
215
216
217
218
219
    }

    /**
     * Waits X seconds. Required after an action that requires data from an AJAX request.
     *
Son Nguyen's avatar
Son Nguyen committed
220
     * @Given /^I wait "(?P<seconds_number>\d+)" seconds$/
Son Nguyen's avatar
Son Nguyen committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
     * @param int $seconds
     */
    public function i_wait_seconds($seconds) {

        if (!$this->running_javascript()) {
            throw new DriverException('Waits are disabled in scenarios without Javascript support');
        }

        $this->getSession()->wait($seconds * 1000, false);
    }

    /**
     * Waits until the page is completely loaded. This step is auto-executed after every step.
     *
     * @Given /^I wait until the page is ready$/
     */
    public function wait_until_the_page_is_ready() {

        if (!$this->running_javascript()) {
            throw new DriverException('Waits are disabled in scenarios without Javascript support');
        }

        $this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
    }

    /**
     * Waits until the provided element selector exists in the DOM
     *
     * Using the protected method as this method will be usually
     * called by other methods which are not returning a set of
     * steps and performs the actions directly, so it would not
     * be executed if it returns another step.

     * @Given /^I wait until "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" exists$/
     * @param string $element
     * @param string $selector
     * @return void
     */
    public function wait_until_exists($element, $selectortype) {
        $this->ensure_element_exists($element, $selectortype);
    }

    /**
     * Waits until the provided element does not exist in the DOM
     *
     * Using the protected method as this method will be usually
     * called by other methods which are not returning a set of
     * steps and performs the actions directly, so it would not
     * be executed if it returns another step.

     * @Given /^I wait until "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" does not exist$/
     * @param string $element
     * @param string $selector
     * @return void
     */
    public function wait_until_does_not_exists($element, $selectortype) {
        $this->ensure_element_does_not_exist($element, $selectortype);
    }

    /**
     * Generic mouse over action. Mouse over a element of the specified type.
     *
     * @When /^I hover "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)"$/
     * @param string $element Element we look for
     * @param string $selectortype The type of what we look for
     */
    public function i_hover($element, $selectortype) {

        // Gets the node based on the requested selector type and locator.
        $node = $this->get_selected_node($selectortype, $element);
        $node->mouseOver();
    }

    /**
Son Nguyen's avatar
Son Nguyen committed
295
     * Click on the link or button.
Son Nguyen's avatar
Son Nguyen committed
296
     *
Son Nguyen's avatar
Son Nguyen committed
297
298
     * @When /^I click on "(?P<link_or_button>(?:[^"]|\\")*)"$/
     * @param string $link_or_button we look for
Son Nguyen's avatar
Son Nguyen committed
299
     */
Son Nguyen's avatar
Son Nguyen committed
300
    public function i_click_on($link_or_button) {
Son Nguyen's avatar
Son Nguyen committed
301
302

        // Gets the node based on the requested selector type and locator.
Son Nguyen's avatar
Son Nguyen committed
303
        $node = $this->get_selected_node('link_or_button', $link_or_button);
Son Nguyen's avatar
Son Nguyen committed
304
        $this->ensure_node_is_visible($node);
Son Nguyen's avatar
Son Nguyen committed
305
306
307
308
309
310
311
//         if ($node->getTagName() === 'a') {
//             $path = $node->getAttribute('href');
//             $this->visitPath($path);
//         }
//         else {
            $node->click();
//         }
Son Nguyen's avatar
Son Nguyen committed
312
313
    }

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
    /**
     * Press the key.
     *
     * @When /^I press the key "(?P<key>(?:[^"]|\\")*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" field$/
     * @param string $key_press want to simulate pressing
     * @param string $nodeelement Element we focus on
     */
    public function i_key_press($key_press, $nodeelement) {

        if (strtolower($key_press) == 'enter' || strtolower($key_press) == 'return') {
            $key_press = 13;
        }

        $node = $this->get_selected_node('field', $nodeelement);

        $node->keyPress($key_press);
    }

Son Nguyen's avatar
Son Nguyen committed
332
    /**
Son Nguyen's avatar
Son Nguyen committed
333
     * Click on the link or button which is located inside the second element.
Son Nguyen's avatar
Son Nguyen committed
334
     *
Son Nguyen's avatar
Son Nguyen committed
335
336
     * @When /^I click on "(?P<link_or_button>(?:[^"]|\\")*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)"$/
     * @param string $link_or_button we look for
Son Nguyen's avatar
Son Nguyen committed
337
338
339
     * @param string $nodeelement Element we look in
     * @param string $nodeselectortype The type of selector where we look in
     */
Son Nguyen's avatar
Son Nguyen committed
340
    public function i_click_on_in_the($link_or_button, $nodeelement, $nodeselectortype) {
Son Nguyen's avatar
Son Nguyen committed
341

Son Nguyen's avatar
Son Nguyen committed
342
        $node = $this->get_node_in_container('link_or_button', $link_or_button, $nodeselectortype, $nodeelement);
Son Nguyen's avatar
Son Nguyen committed
343
344
345
346
        $this->ensure_node_is_visible($node);
        $node->click();
    }

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
    /**
     * Follow the link which is located inside the second element.
     *
     * @When /^I follow "(?P<link>(?:[^"]|\\")*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)"$/
     * @param string $link we look for
     * @param string $nodeelement Element we look in
     * @param string $nodeselectortype The type of selector where we look in
     */
    public function i_follow_in_the($link, $nodeelement, $nodeselectortype) {

        $node = $this->get_node_in_container('link', $link, $nodeselectortype, $nodeelement);
        $this->ensure_node_is_visible($node);
        $node->click();
    }

    /**
     * Press a button which is located inside the second element.
     *
     * @When /^I press "(?P<button>(?:[^"]|\\")*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)"$/
     * @param string $button we look for
     * @param string $nodeelement Element we look in
     * @param string $nodeselectortype The type of selector where we look in
     */
    public function i_press_in_the($button, $nodeelement, $nodeselectortype) {

        $node = $this->get_node_in_container('button', $button, $nodeselectortype, $nodeelement);
        $this->ensure_node_is_visible($node);
        $node->click();
    }

Son Nguyen's avatar
Son Nguyen committed
377
    /**
378
     * Click on the link or button inside a list/table row containing the specified text.
Son Nguyen's avatar
Son Nguyen committed
379
     *
380
     * @When /^I click on "(?P<link_or_button>(?:[^"]|\\")*)" in "(?P<row_text_string>(?:[^"]|\\")*)" row$/
Son Nguyen's avatar
Son Nguyen committed
381
     * @param string $link_or_button we look for
382
     * @param string $rowtext The list/table row text
Son Nguyen's avatar
Son Nguyen committed
383
     * @throws ElementNotFoundException
Son Nguyen's avatar
Son Nguyen committed
384
     */
385
    public function i_click_on_in_row($link_or_button, $rowtext) {
Son Nguyen's avatar
Son Nguyen committed
386
387

        // The table row container.
388
        $rowtextliteral = $this->escaper->escapeLiteral($rowtext);
389
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the row containing the text "' . $rowtext . '"');
390
391
        $xpath = "//div[(contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'listrow', ' '))" .
            " or contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'list-group-item', ' ')))" .
392
393
394
395
            " and contains(normalize-space(.), " . $rowtextliteral . ")]" .
            "|" .
            "//tr[contains(normalize-space(.), " . $rowtextliteral . ")]";
        $rownode = $this->find('xpath', $xpath, $exception);
Son Nguyen's avatar
Son Nguyen committed
396
397

        // Looking for the element DOM node inside the specified row.
Son Nguyen's avatar
Son Nguyen committed
398
        list($selector, $locator) = $this->transform_selector('link_or_button', $link_or_button);
Son Nguyen's avatar
Son Nguyen committed
399
400
401
402
403
        $elementnode = $this->find($selector, $locator, false, $rownode);
        $this->ensure_node_is_visible($elementnode);
        $elementnode->click();
    }

404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
    /**
     * Click on a button in the modal dialog.
     *
     * @When /^I click on "(?P<link_or_button>(?:[^"]|\\")*)" in the dialog$/
     * @throws ElementNotFoundException
     */
    public function i_click_on_in_dialog($link_or_button) {

        // Find the dialog button.
        $exception = new ElementNotFoundException($this->getSession(), 'dialog');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' modal-dialog ')]";
        $rownode = $this->find('xpath', $xpath, $exception);

        list($selector, $locator) = $this->transform_selector('link_or_button', $link_or_button);
        $elementnode = $this->find($selector, $locator, false, $rownode);
        $this->ensure_node_is_visible($elementnode);
        $elementnode->click();
    }

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
    /**
     * Click on the bottom right menu elipsis inside a list panel containing the specified text.
     *
     * @When /^I click on "(?P<row_text_string>(?:[^"]|\\")*)" panel menu$/
     * @param string $rowtext The list/table row text
     * @throws ElementNotFoundException
     */
    public function i_click_on_in_panel($rowtext) {

        // The panel container.
        $rowtextliteral = $this->escaper->escapeLiteral($rowtext);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the panel containing the text "' . $rowtext . '"');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'panel', ' '))" .
            " and contains(normalize-space(.), " . $rowtextliteral . ")]";
        $rownode = $this->find('xpath', $xpath, $exception);

        // Click on the elipsis button for the panel
        $jscode = "jQuery(\"div.panel h3:contains(" . $this->escapeDoubleQuotes($rowtextliteral) . ")\").siblings('.panel-footer').find('.elipsis-right button')[0].click();";
        $this->getSession()->executeScript($jscode);
    }

    /**
     * Click on the bottom right collection menu inside a list panel containing the specified text.
     *
     * @When /^I click on "(?P<row_text_string>(?:[^"]|\\")*)" panel collection$/
     * @param string $rowtext The list/table row text
     * @throws ElementNotFoundException
     */
    public function i_click_on_in_panel_collection_box($rowtext) {

        // The panel container.
        $rowtextliteral = $this->escaper->escapeLiteral($rowtext);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the panel containing the text "' . $rowtext . '"');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'panel', ' '))" .
            " and contains(normalize-space(.), " . $rowtextliteral . ")]";
        $rownode = $this->find('xpath', $xpath, $exception);

        // Click on the collection box for the panel
        $jscode = "jQuery(\"div.panel h3:contains(" . $this->escapeDoubleQuotes($rowtextliteral) . ")\").siblings('.panel-footer').find('.collnum')[0].click();";
        $this->getSession()->executeScript($jscode);
    }

    /**
     * Click on the link or button inside a panel menu containing the specified text.
     *
     * @When /^I click on "(?P<link_or_button>(?:[^"]|\\")*)" in "(?P<row_text_string>(?:[^"]|\\")*)" panel menu$/
     * @param string $link_or_button we look for
     * @param string $rowtext The panel menu text
     * @throws ElementNotFoundException
     */
    public function i_click_on_in_panel_menu($link_or_button, $rowtext) {

        // The panel container.
        $rowtextliteral = $this->escaper->escapeLiteral($rowtext);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the panel containing the text "' . $rowtext . '"');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'panel', ' '))" .
            " and contains(normalize-space(.), " . $rowtextliteral . ")]";
        $rownode = $this->find('xpath', $xpath, $exception);

        // Click on the elipsis button for the panel
        $jscode = "jQuery(\"div.panel h3:contains(" . $this->escapeDoubleQuotes($rowtextliteral) . ")\").siblings('.panel-footer').find('.elipsis-right a:contains(" . $this->escapeDoubleQuotes($link_or_button) . ")')[0].click();";
        $this->getSession()->executeScript($jscode);
    }

    /**
     * Click on the link or button inside a panel collection list containing the specified text.
     *
     * @When /^I click on "(?P<link_or_button>(?:[^"]|\\")*)" in "(?P<row_text_string>(?:[^"]|\\")*)" panel collection$/
     * @param string $link_or_button we look for
     * @param string $rowtext The panel menu text
     * @throws ElementNotFoundException
     */
    public function i_click_on_in_panel_collection_menu($link_or_button, $rowtext) {

        // The panel container.
        $rowtextliteral = $this->escaper->escapeLiteral($rowtext);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the panel containing the text "' . $rowtext . '"');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'panel', ' '))" .
            " and contains(normalize-space(.), " . $rowtextliteral . ")]";
        $rownode = $this->find('xpath', $xpath, $exception);

        // Click on the elipsis button for the panel
        $jscode = "jQuery(\"div.panel h3:contains(" . $this->escapeDoubleQuotes($rowtextliteral) . ")\").siblings('.panel-footer').find(\"a:contains(" . $this->escapeDoubleQuotes($link_or_button) . ")\")[0].click();";
        $this->getSession()->executeScript($jscode);
    }

Son Nguyen's avatar
Son Nguyen committed
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
    /**
     * Click a row containing the specified text.
     *
     * @When /^I click the row "(?P<row_text_string>(?:[^"]|\\")*)"$/
     * @param string $rowtext the row text
     * @throws ElementNotFoundException
     */
    public function i_click_row($rowtext) {

        // The table row container.
        $rowtextliteral = $this->escaper->escapeLiteral($rowtext);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the row containing the text "' . $rowtext . '"');
        $xpath = "//div[(contains(concat(' ', normalize-space(@class), ' '), ' listrow ')" .
                            " or contains(concat(' ', normalize-space(@class), ' '), ' list-group-item '))" .
                        " and contains(normalize-space(.), " . $rowtextliteral . ")]" .
                    "//a[contains(concat(' ', normalize-space(@class), ' '), ' outer-link ')]";
        $rownode = $this->find('xpath', $xpath, $exception);

        //$this->ensure_node_is_visible($rownode);
        //$rownode->click();
        // For some reasons, the Mink function click() and check() do not work
        // Using jQuery as a workaround
        $jscode = "jQuery(\"div.list-group-item:contains(" . $this->escapeDoubleQuotes($rowtextliteral) . ") a.outer-link\")[0].click();";
        $this->getSession()->executeScript($jscode);
533
534
    }

535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
    /**
     * Click a panel header containing the specified text.
     *
     * @When /^I click the panel "(?P<row_text_string>(?:[^"]|\\")*)"$/
     * @param string $rowtext the panel heading text
     * @throws ElementNotFoundException
     */
    public function i_click_panel($rowtext) {

        // The panel container.
        $rowtextliteral = $this->escaper->escapeLiteral($rowtext);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the panel containing the text "' . $rowtext . '"');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'panel', ' '))" .
            " and contains(normalize-space(.), " . $rowtextliteral . ")]" .
            "//a[contains(concat(' ', normalize-space(@class), ' '), ' title-link ')]";
        $rownode = $this->find('xpath', $xpath, $exception);

        $jscode = "jQuery(\"div.panel h3 a.title-link:contains(" . $this->escapeDoubleQuotes($rowtextliteral) . ")\")[0].click();";
        $this->getSession()->executeScript($jscode);
    }

556
557
    /**
     * Click a matrix point by being given a column,row pair
558
     * NOTE: column and row start from number '0' so the first cell in a table is (0,0)
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
     *
     * @When I click on the matrix point :matrix_point
     * @param string $matrix_point a column,row value
     * @throws ElementNotFoundException
     * @throws ExpectationException
     */
    public function i_click_matrix_point($matrix_point) {
        // Check that we have a valid matrix point
        $point = explode(',', $matrix_point);
        if (empty($point[0]) || empty($point[1]) ||
            !is_numeric($point[0]) || !is_numeric($point[1])) {
            throw new ExpectationException('"' . $matrix_point . '" is not valid. Needs to be like "3,5"', $this->getSession());
        }

        // The table container.
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'Unable to find the point "(' . $matrix_point . ')" in a table with class "tablematrix"');
        $xpath = "//table[(contains(concat(' ', normalize-space(@class), ' '), ' tablematrix '))]" .
                 "/tbody/tr[" . $point[1] . "]/td[" . $point[0] . "]";
        $pointnode = $this->find('xpath', $xpath, $exception);

        // For some reasons, the Mink function click() and check() do not work
        // Using jQuery as a workaround
        $jscode = "jQuery(\".tablematrix tr:eq('" . $point[1] . "') td:eq('" . $point[0] . "') span\").click();";
        $this->getSession()->executeScript($jscode);
Son Nguyen's avatar
Son Nguyen committed
583
584
    }

585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
    /**
     * Click on the delete button inside a list/table row containing the specified text.
     *
     * @When /^I delete the "(?P<row_text_string>(?:[^"]|\\")*)" row$/
     * @param string $rowtext The list/table row text
     * @throws ElementNotFoundException
     */
    public function i_delete_the_row($rowtext) {

        // The table row container.
        $rowtextliteral = $this->escaper->escapeLiteral($rowtext);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the delete button in the row containing the text "' . $rowtext . '"');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'list-group-item', ' '))" .
            " and contains(normalize-space(.), " . $rowtextliteral . ")]//button[starts-with(@id, 'delete_')]" .
            "|" .
600
            "//tr[contains(normalize-space(.), " . $rowtextliteral . ")]//button[starts-with(@id, 'delete_') or starts-with(@name, 'files_filebrowser_delete')]";
601
602
603
604
605
606
607
        $deletenode = $this->find('xpath', $xpath, $exception);

        $this->ensure_node_is_visible($deletenode);
        $deletenode->press();
        $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
    }

Son Nguyen's avatar
Son Nguyen committed
608
609
610
611
612
613
614
    /**
     * Drags and drops the specified element to the specified container. This step does not work in all the browsers, consider it experimental.
     *
     * The steps definitions calling this step as part of them should
     * manage the wait times by themselves as the times and when the
     * waits should be done depends on what is being dragged & dropper.
     *
Son Nguyen's avatar
Son Nguyen committed
615
     * @When /^I drag "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector1_string>(?:[^"]|\\")*)" and drop in "(?P<container_element_string>(?:[^"]|\\")*)" "(?P<selector2_string>(?:[^"]|\\")*)"$/
Son Nguyen's avatar
Son Nguyen committed
616
617
618
619
620
     * @param string $element
     * @param string $selectortype
     * @param string $containerelement
     * @param string $containerselectortype
     */
Son Nguyen's avatar
Son Nguyen committed
621
    public function i_drag_and_drop_in($element, $selectortype, $containerelement, $containerselectortype) {
Son Nguyen's avatar
Son Nguyen committed
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672

        list($sourceselector, $sourcelocator) = $this->transform_selector($selectortype, $element);
        $sourcexpath = $this->getSession()->getSelectorsHandler()->selectorToXpath($sourceselector, $sourcelocator);

        list($containerselector, $containerlocator) = $this->transform_selector($containerselectortype, $containerelement);
        $destinationxpath = $this->getSession()->getSelectorsHandler()->selectorToXpath($containerselector, $containerlocator);

        $this->getSession()->getDriver()->dragTo($sourcexpath, $destinationxpath);
    }

    /**
     * Checks, that the specified element is visible. Only available in tests using Javascript.
     *
     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>(?:[^"]|\\")*)" should be visible$/
     * @throws ElementNotFoundException
     * @throws ExpectationException
     * @throws DriverException
     * @param string $element
     * @param string $selectortype
     * @return void
     */
    public function should_be_visible($element, $selectortype) {

        if (!$this->running_javascript()) {
            throw new DriverException('Visible checks are disabled in scenarios without Javascript support');
        }

        $node = $this->get_selected_node($selectortype, $element);
        if (!$node->isVisible()) {
            throw new ExpectationException('"' . $element . '" "' . $selectortype . '" is not visible', $this->getSession());
        }
    }

    /**
     * Checks, that the specified element is not visible. Only available in tests using Javascript.
     *
     * As a "not" method, it's performance is not specially good as we should ensure that the element
     * have time to appear.
     *
     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>(?:[^"]|\\")*)" should not be visible$/
     * @throws ElementNotFoundException
     * @throws ExpectationException
     * @param string $element
     * @param string $selectortype
     * @return void
     */
    public function should_not_be_visible($element, $selectortype) {

        try {
            $this->should_be_visible($element, $selectortype);
            throw new ExpectationException('"' . $element . '" "' . $selectortype . '" is visible', $this->getSession());
Son Nguyen's avatar
Son Nguyen committed
673
674
        }
        catch (ExpectationException $e) {
Son Nguyen's avatar
Son Nguyen committed
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
            // All as expected.
        }
    }

    /**
     * Checks, that the specified element is visible inside the specified container. Only available in tests using Javascript.
     *
     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)" should be visible$/
     * @throws ElementNotFoundException
     * @throws DriverException
     * @throws ExpectationException
     * @param string $element Element we look for
     * @param string $selectortype The type of what we look for
     * @param string $nodeelement Element we look in
     * @param string $nodeselectortype The type of selector where we look in
     */
    public function in_the_should_be_visible($element, $selectortype, $nodeelement, $nodeselectortype) {

        if (!$this->running_javascript()) {
            throw new DriverException('Visible checks are disabled in scenarios without Javascript support');
        }

        $node = $this->get_node_in_container($selectortype, $element, $nodeselectortype, $nodeelement);
        if (!$node->isVisible()) {
            throw new ExpectationException(
                '"' . $element . '" "' . $selectortype . '" in the "' . $nodeelement . '" "' . $nodeselectortype . '" is not visible',
                $this->getSession()
            );
        }
    }

    /**
     * Checks, that the specified element is not visible inside the specified container. Only available in tests using Javascript.
     *
     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)" should not be visible$/
     * @throws ElementNotFoundException
     * @throws ExpectationException
     * @param string $element Element we look for
     * @param string $selectortype The type of what we look for
     * @param string $nodeelement Element we look in
     * @param string $nodeselectortype The type of selector where we look in
     */
    public function in_the_should_not_be_visible($element, $selectortype, $nodeelement, $nodeselectortype) {

        try {
            $this->in_the_should_be_visible($element, $selectortype, $nodeelement, $nodeselectortype);
            throw new ExpectationException(
                '"' . $element . '" "' . $selectortype . '" in the "' . $nodeelement . '" "' . $nodeselectortype . '" is visible',
                $this->getSession()
            );
        }
Son Nguyen's avatar
Son Nguyen committed
726
727
        catch (ExpectationException $e) {
            // All as expected.
Son Nguyen's avatar
Son Nguyen committed
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
        }
    }

    /**
     * Checks, that the specified element contains the specified text. When running Javascript tests it also considers that texts may be hidden.
     *
     * @Then /^I should see "(?P<text_string>(?:[^"]|\\")*)" in the "(?P<element_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)"$/
     * @throws ElementNotFoundException
     * @throws ExpectationException
     * @param string $text
     * @param string $element Element we look in.
     * @param string $selectortype The type of element where we are looking in.
     */
    public function assert_element_contains_text($text, $element, $selectortype) {

        // Getting the container where the text should be found.
        $container = $this->get_selected_node($selectortype, $element);

        // Looking for all the matching nodes without any other descendant matching the
        // same xpath (we are using contains(., ....).
748
        $xpathliteral = $this->escaper->escapeLiteral($text);
Son Nguyen's avatar
Son Nguyen committed
749
750
751
752
753
754
        $xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
            "[count(descendant::*[contains(., $xpathliteral)]) = 0]";

        // Wait until it finds the text inside the container, otherwise custom exception.
        try {
            $nodes = $this->find_all('xpath', $xpath, false, $container);
Son Nguyen's avatar
Son Nguyen committed
755
756
        }
        catch (ElementNotFoundException $e) {
Son Nguyen's avatar
Son Nguyen committed
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
            throw new ExpectationException('"' . $text . '" text was not found in the "' . $element . '" element', $this->getSession());
        }

        // If we are not running javascript we have enough with the
        // element existing as we can't check if it is visible.
        if (!$this->running_javascript()) {
            return;
        }

        // We also check the element visibility when running JS tests.
        $this->spin(
            function($context, $args) {

                foreach ($args['nodes'] as $node) {
                    if ($node->isVisible()) {
                        return true;
                    }
                }

                throw new ExpectationException('"' . $args['text'] . '" text was found in the "' . $args['element'] . '" element but was not visible', $context->getSession());
            },
            array('nodes' => $nodes, 'text' => $text, 'element' => $element)
        );
    }

    /**
     * Checks, that the specified element does not contain the specified text. When running Javascript tests it also considers that texts may be hidden.
     *
     * @Then /^I should not see "(?P<text_string>(?:[^"]|\\")*)" in the "(?P<element_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)"$/
     * @throws ElementNotFoundException
     * @throws ExpectationException
     * @param string $text
     * @param string $element Element we look in.
     * @param string $selectortype The type of element where we are looking in.
     */
    public function assert_element_not_contains_text($text, $element, $selectortype) {

        // Getting the container where the text should be found.
        $container = $this->get_selected_node($selectortype, $element);

        // Looking for all the matching nodes without any other descendant matching the
        // same xpath (we are using contains(., ....).
799
        $xpathliteral = $this->escaper->escapeLiteral($text);
Son Nguyen's avatar
Son Nguyen committed
800
801
802
803
804
805
806
        $xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
            "[count(descendant::*[contains(., $xpathliteral)]) = 0]";

        // We should wait a while to ensure that the page is not still loading elements.
        // Giving preference to the reliability of the results rather than to the performance.
        try {
            $nodes = $this->find_all('xpath', $xpath, false, $container);
Son Nguyen's avatar
Son Nguyen committed
807
808
        }
        catch (ElementNotFoundException $e) {
Son Nguyen's avatar
Son Nguyen committed
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
            // All ok.
            return;
        }

        // If we are not running javascript we have enough with the
        // element not being found as we can't check if it is visible.
        if (!$this->running_javascript()) {
            throw new ExpectationException('"' . $text . '" text was found in the "' . $element . '" element', $this->getSession());
        }

        // We need to ensure all the found nodes are hidden.
        $this->spin(
            function($context, $args) {

                foreach ($args['nodes'] as $node) {
                    if ($node->isVisible()) {
                        throw new ExpectationException('"' . $args['text'] . '" text was found in the "' . $args['element'] . '" element', $context->getSession());
                    }
                }

                // If all the found nodes are hidden we are happy.
                return true;
            },
            array('nodes' => $nodes, 'text' => $text, 'element' => $element)
        );
    }

    /**
     * Checks, that the first specified element appears before the second one.
     *
     * @Given /^"(?P<preceding_element_string>(?:[^"]|\\")*)" "(?P<selector1_string>(?:[^"]|\\")*)" should appear before "(?P<following_element_string>(?:[^"]|\\")*)" "(?P<selector2_string>(?:[^"]|\\")*)"$/
     * @throws ExpectationException
     * @param string $preelement The locator of the preceding element
     * @param string $preselectortype The locator of the preceding element
     * @param string $postelement The locator of the latest element
     * @param string $postselectortype The selector type of the latest element
     */
    public function should_appear_before($preelement, $preselectortype, $postelement, $postselectortype) {

        // We allow postselectortype as a non-text based selector.
        list($preselector, $prelocator) = $this->transform_selector($preselectortype, $preelement);
        list($postselector, $postlocator) = $this->transform_selector($postselectortype, $postelement);

        $prexpath = $this->find($preselector, $prelocator)->getXpath();
        $postxpath = $this->find($postselector, $postlocator)->getXpath();

        // Using following xpath axe to find it.
        $msg = '"'.$preelement.'" "'.$preselectortype.'" does not appear before "'.$postelement.'" "'.$postselectortype.'"';
        $xpath = $prexpath.'/following::*[contains(., '.$postxpath.')]';
        if (!$this->getSession()->getDriver()->find($xpath)) {
            throw new ExpectationException($msg, $this->getSession());
        }
    }

    /**
     * Checks, that the first specified element appears after the second one.
     *
     * @Given /^"(?P<following_element_string>(?:[^"]|\\")*)" "(?P<selector1_string>(?:[^"]|\\")*)" should appear after "(?P<preceding_element_string>(?:[^"]|\\")*)" "(?P<selector2_string>(?:[^"]|\\")*)"$/
     * @throws ExpectationException
     * @param string $postelement The locator of the latest element
     * @param string $postselectortype The selector type of the latest element
     * @param string $preelement The locator of the preceding element
     * @param string $preselectortype The locator of the preceding element
     */
    public function should_appear_after($postelement, $postselectortype, $preelement, $preselectortype) {

        // We allow postselectortype as a non-text based selector.
        list($postselector, $postlocator) = $this->transform_selector($postselectortype, $postelement);
        list($preselector, $prelocator) = $this->transform_selector($preselectortype, $preelement);

        $postxpath = $this->find($postselector, $postlocator)->getXpath();
        $prexpath = $this->find($preselector, $prelocator)->getXpath();

        // Using preceding xpath axe to find it.
        $msg = '"'.$postelement.'" "'.$postselectortype.'" does not appear after "'.$preelement.'" "'.$preselectortype.'"';
        $xpath = $postxpath.'/preceding::*[contains(., '.$prexpath.')]';
        if (!$this->getSession()->getDriver()->find($xpath)) {
            throw new ExpectationException($msg, $this->getSession());
        }
    }

    /**
     * Checks, that element of specified type is disabled.
     *
     * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should be disabled$/
     * @throws ExpectationException Thrown by BehatBase::find
     * @param string $element Element we look in
     * @param string $selectortype The type of element where we are looking in.
     */
    public function the_element_should_be_disabled($element, $selectortype) {

        // Transforming from steps definitions selector/locator format to Mink format and getting the NodeElement.
        $node = $this->get_selected_node($selectortype, $element);

        if (!$node->hasAttribute('disabled')) {
            throw new ExpectationException('The element "' . $element . '" is not disabled', $this->getSession());
        }
    }

    /**
     * Checks, that element of specified type is enabled.
     *
     * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should be enabled$/
     * @throws ExpectationException Thrown by BehatBase::find
     * @param string $element Element we look on
     * @param string $selectortype The type of where we look
     */
    public function the_element_should_be_enabled($element, $selectortype) {

        // Transforming from steps definitions selector/locator format to mink format and getting the NodeElement.
        $node = $this->get_selected_node($selectortype, $element);

        if ($node->hasAttribute('disabled')) {
            throw new ExpectationException('The element "' . $element . '" is not enabled', $this->getSession());
        }
    }

    /**
     * Checks the provided element and selector type are readonly on the current page.
     *
     * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should be readonly$/
     * @throws ExpectationException Thrown by BehatBase::find
     * @param string $element Element we look in
     * @param string $selectortype The type of element where we are looking in.
     */
    public function the_element_should_be_readonly($element, $selectortype) {
        // Transforming from steps definitions selector/locator format to Mink format and getting the NodeElement.
        $node = $this->get_selected_node($selectortype, $element);

        if (!$node->hasAttribute('readonly')) {
            throw new ExpectationException('The element "' . $element . '" is not readonly', $this->getSession());
        }
    }

    /**
     * Checks the provided element and selector type are not readonly on the current page.
     *
     * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not be readonly$/
     * @throws ExpectationException Thrown by BehatBase::find
     * @param string $element Element we look in
     * @param string $selectortype The type of element where we are looking in.
     */
    public function the_element_should_not_be_readonly($element, $selectortype) {
        // Transforming from steps definitions selector/locator format to Mink format and getting the NodeElement.
        $node = $this->get_selected_node($selectortype, $element);

        if ($node->hasAttribute('readonly')) {
            throw new ExpectationException('The element "' . $element . '" is readonly', $this->getSession());
        }
    }

    /**
     * Checks the provided element and selector type exists in the current page.
     *
     * This step is for advanced users, use it if you don't find anything else suitable for what you need.
     *
     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exists$/
     * @throws ElementNotFoundException Thrown by BehatBase::find
     * @param string $element The locator of the specified selector
     * @param string $selectortype The selector type
     */
    public function should_exists($element, $selectortype) {

        // Getting Mink selector and locator.
        list($selector, $locator) = $this->transform_selector($selectortype, $element);

        // Will throw an ElementNotFoundException if it does not exist.
        $this->find($selector, $locator);
    }

    /**
     * Checks that the provided element and selector type not exists in the current page.
     *
     * This step is for advanced users, use it if you don't find anything else suitable for what you need.
     *
     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exists$/
     * @throws ExpectationException
     * @param string $element The locator of the specified selector
     * @param string $selectortype The selector type
     */
    public function should_not_exists($element, $selectortype) {

        try {
            $this->should_exists($element, $selectortype);
            throw new ExpectationException('The "' . $element . '" "' . $selectortype . '" exists in the current page', $this->getSession());
Son Nguyen's avatar
Son Nguyen committed
994
995
        }
        catch (ElementNotFoundException $e) {
Son Nguyen's avatar
Son Nguyen committed
996
997
998
999
1000
1001
            // It passes.
            return;
        }
    }

    /**
1002
     * This step triggers the cron, through the web interface.
Son Nguyen's avatar
Son Nguyen committed
1003
     *
1004
1005
1006
1007
     * It resets the "nextrun" on every cron task, so every cron task will run
     * every time this step is used.
     *
     * @Given /^I trigger (the )?cron$/
Son Nguyen's avatar
Son Nguyen committed
1008
1009
     */
    public function i_trigger_cron() {
1010
1011
1012
1013
        set_field('cron', 'nextrun', null);
        foreach(plugin_types() as $plugintype) {
            set_field($plugintype . '_cron', 'nextrun', null);
        }
1014
        $this->getSession()->visit($this->locate_path('/lib/cron.php?urlsecret=' . urlencode(get_config('urlsecret'))));
Son Nguyen's avatar
Son Nguyen committed
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
    }

    /**
     * Checks that an element and selector type exists in another element and selector type on the current page.
     *
     * This step is for advanced users, use it if you don't find anything else suitable for what you need.
     *
     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exist in the "(?P<element2_string>(?:[^"]|\\")*)" "(?P<selector2_string>[^"]*)"$/
     * @throws ElementNotFoundException Thrown by BehatBase::find
     * @param string $element The locator of the specified selector
     * @param string $selectortype The selector type
     * @param string $containerelement The container selector type
     * @param string $containerselectortype The container locator
     */
    public function should_exist_in_the($element, $selectortype, $containerelement, $containerselectortype) {
        // Get the container node.
        $containernode = $this->get_selected_node($containerselectortype, $containerelement);

        list($selector, $locator) = $this->transform_selector($selectortype, $element);

        // Specific exception giving info about where can't we find the element.
        $locatorexceptionmsg = $element . '" in the "' . $containerelement. '" "' . $containerselectortype. '"';
        $exception = new ElementNotFoundException($this->getSession(), $selectortype, null, $locatorexceptionmsg);

        // Looks for the requested node inside the container node.
        $this->find($selector, $locator, $exception, $containernode);
    }

    /**
     * Checks that an element and selector type does not exist in another element and selector type on the current page.
     *
     * This step is for advanced users, use it if you don't find anything else suitable for what you need.
     *
     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exist in the "(?P<element2_string>(?:[^"]|\\")*)" "(?P<selector2_string>[^"]*)"$/
     * @throws ExpectationException
     * @param string $element The locator of the specified selector
     * @param string $selectortype The selector type
     * @param string $containerelement The container selector type
     * @param string $containerselectortype The container locator
     */
    public function should_not_exist_in_the($element, $selectortype, $containerelement, $containerselectortype) {
        try {
            $this->should_exist_in_the($element, $selectortype, $containerelement, $containerselectortype);
            throw new ExpectationException('The "' . $element . '" "' . $selectortype . '" exists in the "' .
                $containerelement . '" "' . $containerselectortype . '"', $this->getSession());
Son Nguyen's avatar
Son Nguyen committed
1060
1061
        }
        catch (ElementNotFoundException $e) {
Son Nguyen's avatar
Son Nguyen committed
1062
1063
1064
1065
            // It passes.
            return;
        }
    }
1066

1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
    /**
     * Visit a Mahara portfolio Page with the specified title
     *
     * @Given /^I go to portfolio page "([^"]*)"$/
     * @Given /^I go to view "([^"]*)"$/
     */
    public function i_go_to_view($title) {
        // Find the page's ID number
        $views = get_records_array('view', 'title', $title, '', 'id');
        if (!$views) {
            throw new Exception(sprintf('Invalid page title. No view found with title "%s".', $title));
        }
        if (count($views) > 1) {
            throw new Exception(sprintf('Invalid page title. More than one view with title "%s".', $title));
        }

        $view = reset($views);

        // success
Son Nguyen's avatar
Son Nguyen committed
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
        $this->visitPath("/view/view.php?id={$view->id}");
    }

    /**
     * Expand a collapsible section containing the specified text.
     *
     * @When /^I expand the section "(?P<text>(?:[^"]|\\")*)"$/
     * @param string $text The text in the section
     * @throws ElementNotFoundException
     */
    public function i_expand_section($text) {

        // Find the section heading link.
        $textliteral = $this->escaper->escapeLiteral($text);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the collapsed section heading containing the text "' . $text . '"');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' collapsible-group ')]" .
                    "//a[contains(concat(' ', normalize-space(@data-toggle), ' '), ' collapse ')" .
                        " and contains(normalize-space(.), " . $textliteral . ")" .
                        " and contains(concat(' ', normalize-space(@class), ' '), ' collapsed ')]";
        $section_heading_link = $this->find('xpath', $xpath, $exception);

        $this->ensure_node_is_visible($section_heading_link);
        $section_heading_link->click();

    }

    /**
     * Unexpand a collapsible section containing the specified text.
     *
     * @When /^I unexpand the section "(?P<text>(?:[^"]|\\")*)"$/
     * @param string $text The text in the section
     * @throws ElementNotFoundException
     */
    public function i_unexpand_section($text) {

        // Find the section heading link.
        $textliteral = $this->escaper->escapeLiteral($text);
        $exception = new ElementNotFoundException($this->getSession(), 'text', null, 'the uncollapsed section heading containing the text "' . $text . '"');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' collapsible-group ')]" .
                    "//a[contains(concat(' ', normalize-space(@data-toggle), ' '), ' collapse ')" .
                        " and contains(normalize-space(.), " . $textliteral . ")" .
                        " and not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]";
        $section_heading_link = $this->find('xpath', $xpath, $exception);

        $this->ensure_node_is_visible($section_heading_link);
        $section_heading_link->click();

    }

    /**
     * Close the modal dialog.
     *
     * @When /^I close the dialog$/
     * @throws ElementNotFoundException
     */
    public function i_close_dialog() {

        // Find the dialog close button.
        $exception = new ElementNotFoundException($this->getSession(), 'dialog');
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' modal-dialog ')]" .
                    "//button[contains(concat(' ', normalize-space(@class), ' '), ' close ')]";
        $dialogclosebuttons = $this->find_all('xpath', $xpath, $exception);

        foreach ($dialogclosebuttons as $closebutton) {
            if ($closebutton->isVisible()) {
                $closebutton->click();
                return;
            }
        }

    }

1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
    /**
     * Close the config modal dialog.
     *
     * @When /^I close the config dialog$/
     * @throws ElementNotFoundException
     */
    public function i_close_config_dialog() {

        // Find the config dialog close button.
        $exception = new ElementNotFoundException($this->getSession(), 'dialog');
        $xpath = "//div[@id='configureblock']" .
                 "//div[contains(concat(' ', normalize-space(@class), ' '), ' modal-dialog ')]" .
                 "//button[contains(concat(' ', normalize-space(@class), ' '), ' close ')]";
        $closebutton = $this->find('xpath', $xpath, $exception);
        if ($closebutton->isVisible()) {
            $closebutton->click();
            $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
            return;
        }
    }

Son Nguyen's avatar
Son Nguyen committed
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
/**
 * Display the editting page
 *
 * @When /^I display the page$/
 *
 */
    public function i_display_page() {
        $this->getSession()->executeScript('jQuery("div.with-heading a:contains(\'Display page\')")[0].click();');
    }

/**
 * Jump to next page of a list (pagination)
 *
 * @When I jump to next page of the list :id
 *
 */
    public function i_jump_next_page_of_list($id) {
        $this->getSession()->executeScript('jQuery("div#' . $id . ' a:contains(\'Next page\')")[0].click();');
    }

/**
 * Jump to previous page of a list (pagination)
 *
 * @When I jump to previous page of the list :id
 *
 */
    public function i_jump_prev_page_of_list($id) {
        $this->getSession()->executeScript('jQuery("div#' . $id . ' a:contains(\'Previous page\')")[0].click();');
    }

/**
 * Jump to a page of a list (pagination)
 *
 * @When I jump to page :page of the list :id
 *
 */
    public function i_jump_page_of_list($page, $id) {
        $this->getSession()->executeScript('jQuery("div#' . $id . ' a:contains(\'' . $page . '\')")[0].click();');
1217
    }
Son Nguyen's avatar
Son Nguyen committed
1218
1219
1220
1221
1222
1223
1224
1225
1226

/**
 * Delete a Link and resource menu item
 *
 * @When I delete the link and resource menu item :item
 *
 */
    public function i_delete_link_resource_menu_item($item) {
        $this->getSession()->executeScript('jQuery("div#menuitemlist tr:contains(\'' . $item . '\') button:contains(\'Delete\')")[0].click();');
1227
        usleep(10000);
Son Nguyen's avatar
Son Nguyen committed
1228
        $this->i_accept_confirm_popup();
1229
        $this->wait_until_the_page_is_ready();
Son Nguyen's avatar
Son Nguyen committed
1230
1231
    }

1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
/**
 * Scroll element into view and align top of element with the top of the visible area.
 *
 * @When I scroll to the id :id
 *
 */
    public function i_scroll_into_view($id) {
        $function = <<<JS
          (function(){
              var elem = document.getElementById("$id");
              elem.scrollIntoView(true);
          })()
JS;
        try {
            $this->getSession()->executeScript($function);
        }
        catch(Exception $e) {
            throw new \Exception("scrollIntoView failed");
        }
    }

/**
 * Scroll element into view and align bottom of element with the bottom of the visible area.
 *
 * @When I scroll to the base of id :id
 *
 */
    public function i_scroll_into_view_base($id) {
        $function = <<<JS
          (function(){
              var elem = document.getElementById("$id");
              elem.scrollIntoView(false);
          })()
JS;
        try {
            $this->getSession()->executeScript($function);
        }
        catch(Exception $e) {
            throw new \Exception("scrollIntoView failed");
        }
    }

1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
/**
 * Check if images exist in the block given its title
 *
 * @Then I should see images in the block :blocktitle
 *
 */
    public function i_should_see_images_block($blocktitle) {
        // Find the block.
        $blocktitleliteral = $this->escaper->escapeLiteral($blocktitle);
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' column-content ')]" .
                     "/div[contains(@id,'blockinstance_')" .
                         " and contains(h3, " . $blocktitleliteral . ")]//img";
        // Wait until it finds the text inside the block title.
        try {
            $blockimages = $this->find_all('xpath', $xpath);
        }
        catch (ElementNotFoundException $e) {
            throw new ExpectationException('The block with title ' . $blocktitleliteral . ' was not found', $this->getSession());
        }

        // If we are not running javascript we have enough with the
        // element existing as we can't check if it is visible.
        if (!$this->running_javascript()) {
            return;
        }

        // We also check the element visibility when running JS tests.
        $this->spin(
            function($context, $args) {

                foreach ($args['nodes'] as $node) {
                    if ($node->isVisible()) {
                        return true;
                    }
                }

                throw new ExpectationException('The block with title ' . $args['text'] . ' was not visible', $context->getSession());
            },
            array('nodes' => $blockimages, 'text' => $blocktitleliteral)
        );
    }

/**
 * Check if images does not exist in the block given its title
 *
 * @Then I should not see images in the block :blocktitle
 *
 */
    public function i_should_not_see_images_block($blocktitle) {
        // Find the block.
        $blocktitleliteral = $this->escaper->escapeLiteral($blocktitle);
        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' column-content ')]" .
                     "/div[contains(@id,'blockinstance_')" .
                         " and contains(h3, " . $blocktitleliteral . ")]" .
                         "[count(descendant::img) = 0]";
        // Wait until it finds the text inside the block title.
        try {
            $blockimages = $this->find_all('xpath', $xpath);
        }
        catch (ElementNotFoundException $e) {
            throw new ExpectationException('The block with title ' . $blocktitleliteral . ' was not found', $this->getSession());
        }

        // If we are not running javascript we have enough with the
        // element existing as we can't check if it is visible.
        if (!$this->running_javascript()) {
            return;
        }

        // We also check the element visibility when running JS tests.
        $this->spin(
            function($context, $args) {

                foreach ($args['nodes'] as $node) {
                    if ($node->isVisible()) {
                        return true;
                    }
                }

                throw new ExpectationException('The block with title ' . $args['text'] . ' was not visible', $context->getSession());
            },
            array('nodes' => $blockimages, 'text' => $blocktitleliteral)
        );
    }

1359
}