2018-11-12 12:11:06 +00:00
|
|
|
<?php
|
|
|
|
// This file is part of Moodle - http://moodle.org/
|
|
|
|
//
|
|
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
|
|
|
|
|
2021-04-27 15:43:47 +00:00
|
|
|
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
|
2022-05-12 08:55:15 +00:00
|
|
|
require_once(__DIR__ . '/behat_app_helper.php');
|
2018-11-12 12:11:06 +00:00
|
|
|
|
2021-05-11 16:54:18 +00:00
|
|
|
use Behat\Gherkin\Node\TableNode;
|
2018-11-12 12:11:06 +00:00
|
|
|
use Behat\Mink\Exception\DriverException;
|
|
|
|
use Behat\Mink\Exception\ExpectationException;
|
|
|
|
|
|
|
|
/**
|
2022-05-02 14:39:40 +00:00
|
|
|
* Moodle App steps definitions.
|
2018-11-12 12:11:06 +00:00
|
|
|
*
|
|
|
|
* @package core
|
|
|
|
* @category test
|
|
|
|
* @copyright 2018 The Open University
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
2022-05-12 08:55:15 +00:00
|
|
|
class behat_app extends behat_app_helper {
|
2021-09-29 12:14:48 +00:00
|
|
|
|
2022-03-10 11:07:15 +00:00
|
|
|
/** @var array Config overrides */
|
2022-05-31 07:27:50 +00:00
|
|
|
protected $appconfig = [
|
|
|
|
'disableUserTours' => true,
|
|
|
|
'toastDurations' => [ // Extend toast durations in Behat so they don't disappear too soon.
|
|
|
|
'short' => 7500,
|
|
|
|
'long' => 10000,
|
|
|
|
'sticky' => 0,
|
|
|
|
],
|
|
|
|
];
|
2022-03-10 11:07:15 +00:00
|
|
|
|
2022-05-12 13:47:23 +00:00
|
|
|
protected $windowsize = '360x720';
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
2022-05-12 13:31:08 +00:00
|
|
|
* Opens the Moodle App in the browser and optionally logs in.
|
2018-11-12 12:11:06 +00:00
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @When I enter the app
|
|
|
|
* @Given I entered the app as :username
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $username Username
|
2018-11-12 12:11:06 +00:00
|
|
|
* @throws DriverException Issue with configuration or feature file
|
|
|
|
* @throws dml_exception Problem with Moodle setup
|
|
|
|
* @throws ExpectationException Problem with resizing window
|
|
|
|
*/
|
2022-05-12 13:31:08 +00:00
|
|
|
public function i_enter_the_app(string $username = null) {
|
2021-05-26 15:09:09 +00:00
|
|
|
$this->i_launch_the_app();
|
2022-05-12 13:31:08 +00:00
|
|
|
|
|
|
|
if (!is_null($username)) {
|
|
|
|
$this->open_moodleapp_custom_login_url($username);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-05-16 11:17:27 +00:00
|
|
|
$this->enter_site();
|
2021-05-26 15:09:09 +00:00
|
|
|
}
|
|
|
|
|
2022-05-12 13:27:52 +00:00
|
|
|
/**
|
2022-05-12 13:31:08 +00:00
|
|
|
* Check whether the current page is the login form.
|
2022-05-12 13:27:52 +00:00
|
|
|
*/
|
2022-05-12 13:31:08 +00:00
|
|
|
protected function is_in_login_page(): bool {
|
|
|
|
$page = $this->getSession()->getPage();
|
|
|
|
$logininput = $page->find('xpath', '//page-core-login-site//input[@name="url"]');
|
2022-05-12 13:27:52 +00:00
|
|
|
|
2022-05-12 13:31:08 +00:00
|
|
|
return !is_null($logininput);
|
2022-05-12 13:27:52 +00:00
|
|
|
}
|
|
|
|
|
2021-05-26 15:09:09 +00:00
|
|
|
/**
|
2022-05-09 16:00:30 +00:00
|
|
|
* Opens the Moodle App in the browser.
|
2021-05-26 15:09:09 +00:00
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @When I launch the app :runtime
|
|
|
|
* @When I launch the app
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $runtime Runtime
|
2021-05-26 15:09:09 +00:00
|
|
|
* @throws DriverException Issue with configuration or feature file
|
|
|
|
* @throws dml_exception Problem with Moodle setup
|
|
|
|
* @throws ExpectationException Problem with resizing window
|
|
|
|
*/
|
2021-09-23 10:26:49 +00:00
|
|
|
public function i_launch_the_app(string $runtime = '') {
|
2018-11-12 12:11:06 +00:00
|
|
|
// Go to page and prepare browser for app.
|
2021-09-23 10:26:49 +00:00
|
|
|
$this->prepare_browser(['skiponboarding' => empty($runtime)]);
|
2018-11-12 12:11:06 +00:00
|
|
|
}
|
|
|
|
|
2022-08-04 11:38:52 +00:00
|
|
|
/**
|
|
|
|
* Restart the app.
|
|
|
|
*
|
|
|
|
* @When I restart the app
|
|
|
|
*/
|
|
|
|
public function i_restart_the_app() {
|
2022-10-10 14:36:16 +00:00
|
|
|
$this->getSession()->visit($this->get_app_url());
|
2022-08-04 11:38:52 +00:00
|
|
|
|
|
|
|
$this->i_wait_the_app_to_restart();
|
|
|
|
}
|
|
|
|
|
2021-06-10 13:15:38 +00:00
|
|
|
/**
|
2022-05-12 13:31:08 +00:00
|
|
|
* @Then I wait the app to restart
|
2021-06-10 13:15:38 +00:00
|
|
|
*/
|
|
|
|
public function i_wait_the_app_to_restart() {
|
|
|
|
// Prepare testing runtime again.
|
2022-07-07 12:37:57 +00:00
|
|
|
$this->prepare_browser();
|
2021-06-10 13:15:38 +00:00
|
|
|
}
|
|
|
|
|
2022-07-07 10:03:58 +00:00
|
|
|
/**
|
|
|
|
* @Then I log out in the app
|
|
|
|
*
|
|
|
|
* @param bool $force If force logout or not.
|
|
|
|
*/
|
|
|
|
public function i_log_out_in_app($force = true) {
|
|
|
|
$options = json_encode([
|
|
|
|
'forceLogout' => $force,
|
|
|
|
]);
|
|
|
|
|
|
|
|
$result = $this->zone_js("sites.logout($options)");
|
|
|
|
|
|
|
|
if ($result !== 'OK') {
|
|
|
|
throw new DriverException('Error on log out - ' . $result);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->i_wait_the_app_to_restart();
|
|
|
|
}
|
|
|
|
|
2021-04-27 15:43:47 +00:00
|
|
|
/**
|
|
|
|
* Finds elements in the app.
|
|
|
|
*
|
2022-02-22 12:45:37 +00:00
|
|
|
* @Then /^I should( not)? find (".+")( inside the .+)? in the app$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param bool $not Whether assert that the element was not found
|
|
|
|
* @param string $locator Element locator
|
2022-06-30 11:48:43 +00:00
|
|
|
* @param string $container Container name
|
2021-04-27 15:43:47 +00:00
|
|
|
*/
|
2022-06-30 11:48:43 +00:00
|
|
|
public function i_find_in_the_app(bool $not, string $locator, string $container = '') {
|
2021-10-21 08:53:08 +00:00
|
|
|
$locator = $this->parse_element_locator($locator);
|
2022-06-30 11:48:43 +00:00
|
|
|
if (!empty($container)) {
|
|
|
|
preg_match('/^ inside the (.+)$/', $container, $matches);
|
|
|
|
$container = $matches[1];
|
2022-02-22 12:45:37 +00:00
|
|
|
}
|
2022-06-30 11:48:43 +00:00
|
|
|
$options = json_encode(['containerName' => $container]);
|
2021-04-27 15:43:47 +00:00
|
|
|
|
2022-06-30 11:48:43 +00:00
|
|
|
$this->spin(function() use ($not, $locator, $options) {
|
|
|
|
$result = $this->runtime_js("find($locator, $options)");
|
2021-04-27 15:43:47 +00:00
|
|
|
|
|
|
|
if ($not && $result === 'OK') {
|
2022-06-29 10:17:29 +00:00
|
|
|
throw new DriverException('Error, found an element that should not be found');
|
2021-04-27 15:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$not && $result !== 'OK') {
|
2022-06-29 10:17:29 +00:00
|
|
|
throw new DriverException('Error finding element - ' . $result);
|
2021-04-27 15:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2021-04-27 15:43:47 +00:00
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2021-11-25 12:04:25 +00:00
|
|
|
/**
|
|
|
|
* Scroll to an element in the app.
|
|
|
|
*
|
|
|
|
* @When /^I scroll to (".+") in the app$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $locator Element locator
|
2021-11-25 12:04:25 +00:00
|
|
|
*/
|
|
|
|
public function i_scroll_to_in_the_app(string $locator) {
|
|
|
|
$locator = $this->parse_element_locator($locator);
|
|
|
|
|
2022-05-12 13:47:23 +00:00
|
|
|
$this->spin(function() use ($locator) {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("scrollTo($locator)");
|
2021-11-25 12:04:25 +00:00
|
|
|
|
|
|
|
if ($result !== 'OK') {
|
2022-06-29 10:17:29 +00:00
|
|
|
throw new DriverException('Error finding element - ' . $result);
|
2021-11-25 12:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
2022-03-30 15:34:53 +00:00
|
|
|
|
|
|
|
// Wait scroll animation to finish.
|
|
|
|
$this->getSession()->wait(300);
|
2021-11-25 12:04:25 +00:00
|
|
|
}
|
|
|
|
|
2021-12-01 13:12:15 +00:00
|
|
|
/**
|
|
|
|
* Load more items in a list with an infinite loader.
|
|
|
|
*
|
|
|
|
* @When /^I (should not be able to )?load more items in the app$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param bool $not Whether assert that it is not possible to load more items
|
2021-12-01 13:12:15 +00:00
|
|
|
*/
|
|
|
|
public function i_load_more_items_in_the_app(bool $not = false) {
|
|
|
|
$this->spin(function() use ($not) {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js('loadMoreItems()');
|
2021-12-01 13:12:15 +00:00
|
|
|
|
2022-05-12 13:47:23 +00:00
|
|
|
if ($not && $result !== 'ERROR: All items are already loaded.') {
|
2021-12-01 13:12:15 +00:00
|
|
|
throw new DriverException('It should not have been possible to load more items');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$not && $result !== 'OK') {
|
|
|
|
throw new DriverException('Error loading more items - ' . $result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2021-11-09 15:06:13 +00:00
|
|
|
/**
|
|
|
|
* Trigger swipe gesture.
|
|
|
|
*
|
|
|
|
* @When /^I swipe to the (left|right) in the app$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $direction Swipe direction
|
2021-11-09 15:06:13 +00:00
|
|
|
*/
|
|
|
|
public function i_swipe_in_the_app(string $direction) {
|
|
|
|
$method = 'swipe' . ucwords($direction);
|
|
|
|
|
2022-09-02 15:11:09 +00:00
|
|
|
$this->zone_js("getAngularInstance('ion-content', 'CoreSwipeNavigationDirective').$method()");
|
2022-03-10 10:17:19 +00:00
|
|
|
|
2022-05-09 16:00:30 +00:00
|
|
|
$this->wait_for_pending_js();
|
|
|
|
|
2022-03-10 10:17:19 +00:00
|
|
|
// Wait swipe animation to finish.
|
|
|
|
$this->getSession()->wait(300);
|
2021-11-09 15:06:13 +00:00
|
|
|
}
|
|
|
|
|
2021-04-29 11:40:42 +00:00
|
|
|
/**
|
|
|
|
* Check if elements are selected in the app.
|
|
|
|
*
|
2021-06-03 09:20:36 +00:00
|
|
|
* @Then /^(".+") should( not)? be selected in the app$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $locator Element locator
|
|
|
|
* @param bool $not Whether to assert that the element is not selected
|
2021-04-29 11:40:42 +00:00
|
|
|
*/
|
2021-10-21 08:53:08 +00:00
|
|
|
public function be_selected_in_the_app(string $locator, bool $not = false) {
|
|
|
|
$locator = $this->parse_element_locator($locator);
|
2021-04-29 11:40:42 +00:00
|
|
|
|
2022-05-12 13:47:23 +00:00
|
|
|
$this->spin(function() use ($locator, $not) {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("isSelected($locator)");
|
2021-04-29 11:40:42 +00:00
|
|
|
|
|
|
|
switch ($result) {
|
|
|
|
case 'YES':
|
|
|
|
if ($not) {
|
2022-06-29 10:17:29 +00:00
|
|
|
throw new ExpectationException("Element was selected and shouldn't have", $this->getSession()->getDriver());
|
2021-04-29 11:40:42 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'NO':
|
|
|
|
if (!$not) {
|
2022-06-29 10:17:29 +00:00
|
|
|
throw new ExpectationException("Element wasn't selected and should have", $this->getSession()->getDriver());
|
2021-04-29 11:40:42 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2022-06-29 10:17:29 +00:00
|
|
|
throw new DriverException('Error finding element - ' . $result);
|
2021-04-29 11:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2021-04-29 11:40:42 +00:00
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
2019-01-15 16:51:38 +00:00
|
|
|
* Carries out the login steps for the app, assuming the user is on the app login page. Called
|
|
|
|
* from behat_auth.php.
|
2018-11-12 12:11:06 +00:00
|
|
|
*
|
|
|
|
* @param string $username Username (and password)
|
2019-01-15 16:51:38 +00:00
|
|
|
* @throws Exception Any error
|
2018-11-12 12:11:06 +00:00
|
|
|
*/
|
2019-01-15 16:51:38 +00:00
|
|
|
public function login(string $username) {
|
2018-11-12 12:11:06 +00:00
|
|
|
$this->i_set_the_field_in_the_app('Username', $username);
|
|
|
|
$this->i_set_the_field_in_the_app('Password', $username);
|
|
|
|
|
|
|
|
// Note there are two 'Log in' texts visible (the title and the button) so we have to use
|
2019-01-15 16:51:38 +00:00
|
|
|
// a 'near' value here.
|
2021-10-21 08:53:08 +00:00
|
|
|
$this->i_press_in_the_app('"Log in" near "Forgotten"');
|
2018-11-12 12:11:06 +00:00
|
|
|
|
|
|
|
// Wait until the main page appears.
|
|
|
|
$this->spin(
|
2022-09-01 08:45:49 +00:00
|
|
|
function($context) {
|
2022-07-29 08:04:13 +00:00
|
|
|
$initialpage = $context->getSession()->getPage()->find('xpath', '//page-core-mainmenu') ??
|
|
|
|
$context->getSession()->getPage()->find('xpath', '//page-core-login-change-password') ??
|
|
|
|
$context->getSession()->getPage()->find('xpath', '//page-core-user-complete-profile');
|
|
|
|
if ($initialpage) {
|
2022-05-12 13:31:08 +00:00
|
|
|
return true;
|
2018-11-12 12:11:06 +00:00
|
|
|
}
|
2022-05-09 16:00:30 +00:00
|
|
|
throw new DriverException('Moodle App main page not loaded after login');
|
2018-11-12 12:11:06 +00:00
|
|
|
}, false, 30);
|
|
|
|
|
|
|
|
// Wait for JS to finish as well.
|
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2022-05-16 11:17:27 +00:00
|
|
|
/**
|
|
|
|
* Enter site.
|
|
|
|
*/
|
|
|
|
protected function enter_site() {
|
|
|
|
if (!$this->is_in_login_page()) {
|
|
|
|
// Already in the site.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
global $CFG;
|
|
|
|
|
|
|
|
$this->i_set_the_field_in_the_app('Your site', $CFG->wwwroot);
|
|
|
|
$this->i_press_in_the_app('"Connect to your site"');
|
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2021-11-29 09:37:04 +00:00
|
|
|
/**
|
2022-05-12 13:31:08 +00:00
|
|
|
* Shortcut to let the user enter a course in the app.
|
2021-11-29 09:37:04 +00:00
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @Given I entered the course :coursename as :username in the app
|
|
|
|
* @Given I entered the course :coursename in the app
|
2021-11-29 09:37:04 +00:00
|
|
|
* @param string $coursename Course name
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $username Username
|
2021-11-29 09:37:04 +00:00
|
|
|
* @throws DriverException If the button push doesn't work
|
|
|
|
*/
|
2022-05-12 13:31:08 +00:00
|
|
|
public function i_entered_the_course_in_the_app(string $coursename, ?string $username = null) {
|
|
|
|
$courseid = $this->get_course_id($coursename);
|
2022-05-12 13:27:52 +00:00
|
|
|
if (!$courseid) {
|
|
|
|
throw new DriverException("Course '$coursename' not found");
|
|
|
|
}
|
2021-11-29 09:37:04 +00:00
|
|
|
|
2022-05-12 13:27:52 +00:00
|
|
|
if ($username) {
|
|
|
|
$this->i_launch_the_app();
|
2021-11-29 09:37:04 +00:00
|
|
|
|
2022-05-12 13:27:52 +00:00
|
|
|
$this->open_moodleapp_custom_login_url($username, "/course/view.php?id=$courseid", '//page-core-course-index');
|
|
|
|
} else {
|
|
|
|
$this->open_moodleapp_custom_url("/course/view.php?id=$courseid", '//page-core-course-index');
|
2021-11-29 09:37:04 +00:00
|
|
|
}
|
2022-05-11 09:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-05-12 13:31:08 +00:00
|
|
|
* User enters a course in the app.
|
2022-05-11 09:57:07 +00:00
|
|
|
*
|
2022-06-02 15:09:01 +00:00
|
|
|
* @Given I enter the course :coursename as :username in the app
|
2022-05-12 13:31:08 +00:00
|
|
|
* @Given I enter the course :coursename in the app
|
2022-05-11 09:57:07 +00:00
|
|
|
* @param string $coursename Course name
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $username Username
|
2022-05-11 09:57:07 +00:00
|
|
|
* @throws DriverException If the button push doesn't work
|
|
|
|
*/
|
2022-05-12 13:31:08 +00:00
|
|
|
public function i_enter_the_course_in_the_app(string $coursename, ?string $username = null) {
|
|
|
|
if (!is_null($username)) {
|
|
|
|
$this->i_enter_the_app();
|
|
|
|
$this->login($username);
|
2022-05-11 09:57:07 +00:00
|
|
|
}
|
|
|
|
|
2022-06-30 11:48:43 +00:00
|
|
|
$mycoursesfound = $this->runtime_js("find({ text: 'My courses', selector: 'ion-tab-button'})");
|
2022-05-12 13:31:08 +00:00
|
|
|
|
|
|
|
if ($mycoursesfound !== 'OK') {
|
|
|
|
// My courses not present enter from Dashboard.
|
|
|
|
$this->i_press_in_the_app('"Home" "ion-tab-button"');
|
|
|
|
$this->i_press_in_the_app('"Dashboard"');
|
|
|
|
$this->i_press_in_the_app('"'.$coursename.'" near "Course overview"');
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
|
|
|
|
|
|
|
return;
|
2022-05-11 09:57:07 +00:00
|
|
|
}
|
|
|
|
|
2022-05-12 13:31:08 +00:00
|
|
|
$this->i_press_in_the_app('"My courses" "ion-tab-button"');
|
|
|
|
$this->i_press_in_the_app('"'.$coursename.'"');
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* User enters an activity in a course in the app.
|
|
|
|
*
|
|
|
|
* @Given I entered the :activity activity :activityname on course :course as :username in the app
|
|
|
|
* @Given I entered the :activity activity :activityname on course :course in the app
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $activity Activity
|
|
|
|
* @param string $activityname Activity name
|
|
|
|
* @param string $coursename Course name
|
|
|
|
* @param string $username Username
|
2022-05-12 13:31:08 +00:00
|
|
|
* @throws DriverException If the button push doesn't work
|
|
|
|
*/
|
|
|
|
public function i_enter_the_activity_in_the_app(string $activity, string $activityname, string $coursename, ?string $username = null) {
|
|
|
|
$cm = $this->get_cm_by_activity_name_and_course($activity, $activityname, $coursename);
|
|
|
|
if (!$cm) {
|
2022-05-11 09:57:07 +00:00
|
|
|
throw new DriverException("'$activityname' activity '$activityname' not found");
|
|
|
|
}
|
2021-11-29 09:37:04 +00:00
|
|
|
|
2022-05-11 09:57:07 +00:00
|
|
|
$pageurl = "/mod/$activity/view.php?id={$cm->id}";
|
2021-11-29 09:37:04 +00:00
|
|
|
|
2022-05-11 09:57:07 +00:00
|
|
|
if ($username) {
|
|
|
|
$this->i_launch_the_app();
|
|
|
|
|
|
|
|
$this->open_moodleapp_custom_login_url($username, $pageurl);
|
|
|
|
} else {
|
|
|
|
$this->open_moodleapp_custom_url($pageurl);
|
|
|
|
}
|
2021-11-29 09:37:04 +00:00
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
|
|
|
* Presses standard buttons in the app.
|
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @When /^I press the (back|more menu|page menu|user menu|main menu) button in the app$/
|
2018-11-12 12:11:06 +00:00
|
|
|
* @param string $button Button type
|
|
|
|
* @throws DriverException If the button push doesn't work
|
|
|
|
*/
|
|
|
|
public function i_press_the_standard_button_in_the_app(string $button) {
|
2021-06-03 09:20:36 +00:00
|
|
|
$this->spin(function() use ($button) {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("pressStandard('$button')");
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
if ($result !== 'OK') {
|
|
|
|
throw new DriverException('Error pressing standard button - ' . $result);
|
|
|
|
}
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
return true;
|
|
|
|
});
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2021-05-11 16:54:18 +00:00
|
|
|
/**
|
2021-06-10 13:14:51 +00:00
|
|
|
* Receives push notifications.
|
2021-05-11 16:54:18 +00:00
|
|
|
*
|
2022-05-30 11:24:36 +00:00
|
|
|
* @When /^I click a push notification in the app for:$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param TableNode $data Table data
|
2021-05-11 16:54:18 +00:00
|
|
|
*/
|
2022-05-30 11:24:36 +00:00
|
|
|
public function i_click_a_push_notification(TableNode $data) {
|
2021-05-11 16:54:18 +00:00
|
|
|
global $DB, $CFG;
|
|
|
|
|
|
|
|
$data = (object) $data->getColumnsHash()[0];
|
2022-05-30 11:24:36 +00:00
|
|
|
|
|
|
|
if (isset($data->module, $data->discussion)) {
|
|
|
|
$module = $DB->get_record('course_modules', ['idnumber' => $data->module]);
|
|
|
|
$discussion = $DB->get_record('forum_discussions', ['name' => $data->discussion]);
|
|
|
|
$data->name = 'posts';
|
|
|
|
$data->component = 'mod_forum';
|
|
|
|
}
|
|
|
|
|
2021-05-11 16:54:18 +00:00
|
|
|
$notification = json_encode([
|
2022-05-11 09:57:07 +00:00
|
|
|
'site' => md5($CFG->behat_wwwroot . $data->username),
|
2022-05-30 11:24:36 +00:00
|
|
|
'subject' => $data->subject ?? null,
|
|
|
|
'userfrom' => $data->userfrom ?? null,
|
|
|
|
'userto' => $data->username ?? null,
|
|
|
|
'message' => $data->message ?? '',
|
|
|
|
'title' => $data->title ?? '',
|
|
|
|
'image' => $data->image ?? null,
|
|
|
|
'courseid' => $discussion->course ?? null,
|
|
|
|
'moodlecomponent' => $data->component ?? null,
|
|
|
|
'name' => $data->name ?? null,
|
2021-05-11 16:54:18 +00:00
|
|
|
'contexturl' => '',
|
|
|
|
'notif' => 1,
|
2022-05-30 11:24:36 +00:00
|
|
|
'customdata' => isset($discussion->id, $module->id, $discussion->forum)
|
|
|
|
? ['discussionid' => $discussion->id, 'cmid' => $module->id, 'instance' => $discussion->forum]
|
|
|
|
: null,
|
|
|
|
'additionalData' => isset($data->subject) || isset($data->userfrom)
|
|
|
|
? ['foreground' => true, 'notId' => 23, 'notif' => 1] : null,
|
2021-05-11 16:54:18 +00:00
|
|
|
]);
|
|
|
|
|
2022-05-30 11:24:36 +00:00
|
|
|
$this->evaluate_script("pushNotifications.notificationClicked($notification)", true);
|
2021-05-11 16:54:18 +00:00
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2021-06-15 16:32:31 +00:00
|
|
|
/**
|
|
|
|
* Replace arguments from the content in the given activity field.
|
|
|
|
*
|
|
|
|
* @Given /^I replace the arguments in "([^"]+)" "([^"]+)"$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $idnumber Id number
|
|
|
|
* @param string $field Field
|
2021-06-15 16:32:31 +00:00
|
|
|
*/
|
|
|
|
public function i_replace_arguments_in_the_activity(string $idnumber, string $field) {
|
|
|
|
global $DB;
|
|
|
|
|
|
|
|
$coursemodule = $DB->get_record('course_modules', compact('idnumber'));
|
|
|
|
$module = $DB->get_record('modules', ['id' => $coursemodule->module]);
|
|
|
|
$activity = $DB->get_record($module->name, ['id' => $coursemodule->instance]);
|
|
|
|
|
|
|
|
$DB->update_record($module->name, [
|
|
|
|
'id' => $coursemodule->instance,
|
|
|
|
$field => $this->replace_arguments($activity->{$field}),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2021-06-10 13:14:51 +00:00
|
|
|
/**
|
|
|
|
* Opens a custom link.
|
|
|
|
*
|
|
|
|
* @Given /^I open a custom link in the app for:$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param TableNode $data Table data
|
2021-06-10 13:14:51 +00:00
|
|
|
*/
|
|
|
|
public function i_open_a_custom_link(TableNode $data) {
|
2022-05-09 16:00:30 +00:00
|
|
|
global $DB;
|
2021-06-10 13:14:51 +00:00
|
|
|
|
2022-01-31 12:12:36 +00:00
|
|
|
$data = $data->getColumnsHash()[0];
|
|
|
|
$title = array_keys($data)[0];
|
|
|
|
$data = (object) $data;
|
|
|
|
|
|
|
|
switch ($title) {
|
|
|
|
case 'discussion':
|
|
|
|
$discussion = $DB->get_record('forum_discussions', ['name' => $data->discussion]);
|
2022-05-12 13:27:52 +00:00
|
|
|
$pageurl = "/mod/forum/discuss.php?d={$discussion->id}";
|
2022-01-31 12:12:36 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
2022-05-09 16:00:30 +00:00
|
|
|
case 'assign':
|
|
|
|
case 'bigbluebuttonbn':
|
|
|
|
case 'book':
|
|
|
|
case 'chat':
|
|
|
|
case 'choice':
|
|
|
|
case 'data':
|
|
|
|
case 'feedback':
|
|
|
|
case 'folder':
|
2022-01-31 12:12:36 +00:00
|
|
|
case 'forum':
|
2022-05-09 16:00:30 +00:00
|
|
|
case 'glossary':
|
|
|
|
case 'h5pactivity':
|
|
|
|
case 'imscp':
|
|
|
|
case 'label':
|
|
|
|
case 'lesson':
|
|
|
|
case 'lti':
|
|
|
|
case 'page':
|
|
|
|
case 'quiz':
|
|
|
|
case 'resource':
|
|
|
|
case 'scorm':
|
|
|
|
case 'survey':
|
|
|
|
case 'url':
|
|
|
|
case 'wiki':
|
|
|
|
case 'workshop':
|
|
|
|
$name = $data->$title;
|
|
|
|
$module = $DB->get_record($title, ['name' => $name]);
|
|
|
|
$cm = get_coursemodule_from_instance($title, $module->id);
|
|
|
|
$pageurl = "/mod/$title/view.php?id={$cm->id}";
|
2022-01-31 12:12:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new DriverException('Invalid custom link title - ' . $title);
|
|
|
|
}
|
|
|
|
|
2022-05-12 13:27:52 +00:00
|
|
|
$this->open_moodleapp_custom_url($pageurl);
|
2021-06-10 13:14:51 +00:00
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
|
|
|
* Closes a popup by clicking on the 'backdrop' behind it.
|
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @When I close the popup in the app
|
2018-11-12 12:11:06 +00:00
|
|
|
* @throws DriverException If there isn't a popup to close
|
|
|
|
*/
|
|
|
|
public function i_close_the_popup_in_the_app() {
|
2021-06-03 09:20:36 +00:00
|
|
|
$this->spin(function() {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js('closePopup()');
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
if ($result !== 'OK') {
|
|
|
|
throw new DriverException('Error closing popup - ' . $result);
|
|
|
|
}
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
return true;
|
|
|
|
});
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2022-03-10 11:07:15 +00:00
|
|
|
/**
|
|
|
|
* Override app config.
|
|
|
|
*
|
|
|
|
* @Given /^the app has the following config:$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param TableNode $data Table data
|
2022-03-10 11:07:15 +00:00
|
|
|
*/
|
|
|
|
public function the_app_has_the_following_config(TableNode $data) {
|
|
|
|
foreach ($data->getRows() as $configrow) {
|
|
|
|
$this->appconfig[$configrow[0]] = json_decode($configrow[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-05 08:38:02 +00:00
|
|
|
/**
|
|
|
|
* Check whether the moodle site is compatible with the current feature file
|
|
|
|
* and skip it otherwise. This will be checked looking at tags such as @lms_uptoXXX
|
|
|
|
*
|
|
|
|
* @Given the Moodle site is compatible with this feature
|
|
|
|
*/
|
|
|
|
public function the_moodle_site_is_compatible_with_this_feature() {
|
|
|
|
$this->check_tags();
|
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
|
|
|
* Clicks on / touches something that is visible in the app.
|
|
|
|
*
|
|
|
|
* Note it is difficult to use the standard 'click on' or 'press' steps because those do not
|
2022-06-29 10:17:29 +00:00
|
|
|
* distinguish visible elements and the app always has many non-visible elements in the DOM.
|
2018-11-12 12:11:06 +00:00
|
|
|
*
|
2022-06-14 07:20:00 +00:00
|
|
|
* @When /^I press (".+") in the app$/
|
2021-10-21 08:53:08 +00:00
|
|
|
* @param string $locator Element locator
|
2018-11-12 12:11:06 +00:00
|
|
|
* @throws DriverException If the press doesn't work
|
|
|
|
*/
|
2021-10-21 08:53:08 +00:00
|
|
|
public function i_press_in_the_app(string $locator) {
|
|
|
|
$locator = $this->parse_element_locator($locator);
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2022-05-12 13:47:23 +00:00
|
|
|
$this->spin(function() use ($locator) {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("press($locator)");
|
2021-06-03 09:20:36 +00:00
|
|
|
|
|
|
|
if ($result !== 'OK') {
|
|
|
|
throw new DriverException('Error pressing item - ' . $result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
2018-11-12 12:11:06 +00:00
|
|
|
}
|
|
|
|
|
2022-06-14 07:20:00 +00:00
|
|
|
/**
|
|
|
|
* Performs a pull to refresh gesture.
|
|
|
|
*
|
2022-07-13 09:07:53 +00:00
|
|
|
* @When I pull to refresh in the app
|
2022-06-14 07:20:00 +00:00
|
|
|
* @throws DriverException If the gesture is not available
|
|
|
|
*/
|
|
|
|
public function i_pull_to_refresh_in_the_app() {
|
|
|
|
$this->spin(function() {
|
2022-07-07 08:39:36 +00:00
|
|
|
$result = $this->runtime_js('pullToRefresh()');
|
2022-06-14 07:20:00 +00:00
|
|
|
|
|
|
|
if ($result !== 'OK') {
|
|
|
|
throw new DriverException('Error pulling to refresh - ' . $result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
2022-12-12 09:51:27 +00:00
|
|
|
|
|
|
|
// Wait for UI to settle after refreshing.
|
|
|
|
$this->getSession()->wait(300);
|
2022-06-14 07:20:00 +00:00
|
|
|
}
|
|
|
|
|
2022-06-29 10:17:29 +00:00
|
|
|
/**
|
|
|
|
* Checks if elements can be pressed in the app.
|
|
|
|
*
|
|
|
|
* @Then /^I should( not)? be able to press (".+") in the app$/
|
|
|
|
* @param bool $not Whether to assert that the element cannot be pressed
|
|
|
|
* @param string $locator Element locator
|
|
|
|
*/
|
|
|
|
public function i_should_be_able_to_press_in_the_app(bool $not, string $locator) {
|
|
|
|
$locator = $this->parse_element_locator($locator);
|
|
|
|
|
|
|
|
$this->spin(function() use ($not, $locator) {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("find($locator, { onlyClickable: true })");
|
2022-06-29 10:17:29 +00:00
|
|
|
|
|
|
|
if ($not && $result === 'OK') {
|
|
|
|
throw new DriverException('Error, found a clickable element that should not be found');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$not && $result !== 'OK') {
|
|
|
|
throw new DriverException('Error finding clickable element - ' . $result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
2021-05-18 13:53:31 +00:00
|
|
|
* Select an item from a list of options, such as a radio button.
|
2018-11-12 12:11:06 +00:00
|
|
|
*
|
2021-05-18 13:53:31 +00:00
|
|
|
* It may be necessary to use this step instead of "I press..." because radio buttons in Ionic are initialized
|
|
|
|
* with JavaScript, and clicks may not work until they are initialized properly which may cause flaky tests due
|
|
|
|
* to race conditions.
|
2018-11-12 12:11:06 +00:00
|
|
|
*
|
2021-06-03 09:20:36 +00:00
|
|
|
* @Then /^I (unselect|select) (".+") in the app$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param string $selectedtext Text inidicating if the element should be selected or unselected
|
|
|
|
* @param string $locator Element locator
|
2018-11-12 12:11:06 +00:00
|
|
|
* @throws DriverException If the press doesn't work
|
|
|
|
*/
|
2021-10-21 08:53:08 +00:00
|
|
|
public function i_select_in_the_app(string $selectedtext, string $locator) {
|
2021-05-31 14:39:45 +00:00
|
|
|
$selected = $selectedtext === 'select' ? 'YES' : 'NO';
|
2021-10-21 08:53:08 +00:00
|
|
|
$locator = $this->parse_element_locator($locator);
|
2021-05-31 14:39:45 +00:00
|
|
|
|
2022-05-12 13:47:23 +00:00
|
|
|
$this->spin(function() use ($selectedtext, $selected, $locator) {
|
2021-05-31 14:39:45 +00:00
|
|
|
// Don't do anything if the item is already in the expected state.
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("isSelected($locator)");
|
2021-05-31 14:39:45 +00:00
|
|
|
|
|
|
|
if ($result === $selected) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-29 10:17:29 +00:00
|
|
|
// Press element.
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("press($locator)");
|
2021-05-31 14:39:45 +00:00
|
|
|
|
|
|
|
if ($result !== 'OK') {
|
2022-06-29 10:17:29 +00:00
|
|
|
throw new DriverException('Error pressing element - ' . $result);
|
2021-05-31 14:39:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check that it worked as expected.
|
2022-05-12 13:47:23 +00:00
|
|
|
$this->wait_for_pending_js();
|
2022-03-14 13:49:50 +00:00
|
|
|
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("isSelected($locator)");
|
2021-05-31 14:39:45 +00:00
|
|
|
|
|
|
|
switch ($result) {
|
|
|
|
case 'YES':
|
|
|
|
case 'NO':
|
|
|
|
if ($result !== $selected) {
|
|
|
|
throw new ExpectationException("Item wasn't $selectedtext after pressing it", $this->getSession()->getDriver());
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
throw new DriverException('Error finding item - ' . $result);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
2019-01-15 16:51:38 +00:00
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
|
|
|
* Sets a field to the given text value in the app.
|
|
|
|
*
|
|
|
|
* Currently this only works for input fields which must be identified using a partial or
|
|
|
|
* exact match on the placeholder text.
|
|
|
|
*
|
2021-11-29 09:04:21 +00:00
|
|
|
* @Given /^I set the field "((?:[^"]|\\")+)" to "((?:[^"]|\\")*)" in the app$/
|
2018-11-12 12:11:06 +00:00
|
|
|
* @param string $field Text identifying field
|
|
|
|
* @param string $value Value for field
|
|
|
|
* @throws DriverException If the field set doesn't work
|
|
|
|
*/
|
|
|
|
public function i_set_the_field_in_the_app(string $field, string $value) {
|
2021-06-03 09:20:36 +00:00
|
|
|
$field = addslashes_js($field);
|
|
|
|
$value = addslashes_js($value);
|
|
|
|
|
|
|
|
$this->spin(function() use ($field, $value) {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js("setField('$field', '$value')");
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
if ($result !== 'OK') {
|
|
|
|
throw new DriverException('Error setting field - ' . $result);
|
|
|
|
}
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
return true;
|
|
|
|
});
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2022-06-13 10:58:21 +00:00
|
|
|
/**
|
|
|
|
* Fills a form with field/value data.
|
|
|
|
*
|
|
|
|
* @Given /^I set the following fields to these values in the app:$/
|
|
|
|
* @param TableNode $data
|
|
|
|
*/
|
|
|
|
public function i_set_the_following_fields_to_these_values_in_the_app(TableNode $data) {
|
|
|
|
$datahash = $data->getRowsHash();
|
|
|
|
|
|
|
|
// The action depends on the field type.
|
|
|
|
foreach ($datahash as $locator => $value) {
|
|
|
|
$this->i_set_the_field_in_the_app($locator, $value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-15 09:37:17 +00:00
|
|
|
/**
|
|
|
|
* Checks a field matches a certain value in the app.
|
|
|
|
*
|
|
|
|
* Currently this only works for input fields which must be identified using a partial or
|
|
|
|
* exact match on the placeholder text.
|
|
|
|
*
|
|
|
|
* @Given /^the field "((?:[^"]|\\")+)" matches value "((?:[^"]|\\")*)" in the app$/
|
|
|
|
* @param string $field Text identifying field
|
|
|
|
* @param string $value Value for field
|
|
|
|
* @throws DriverException If the field isn't found
|
|
|
|
* @throws ExpectationException If the field value is different to the expected value
|
|
|
|
*/
|
|
|
|
public function the_field_matches_value_in_the_app(string $field, string $value) {
|
|
|
|
$field = addslashes_js($field);
|
|
|
|
$value = addslashes_js($value);
|
|
|
|
|
|
|
|
$this->spin(function() use ($field, $value) {
|
|
|
|
$result = $this->runtime_js("fieldMatches('$field', '$value')");
|
|
|
|
|
|
|
|
if ($result !== 'OK') {
|
|
|
|
if (str_contains($result, 'No element matches')) {
|
|
|
|
throw new DriverException('Error field matches value - ' . $result);
|
|
|
|
} else {
|
|
|
|
throw new ExpectationException(
|
|
|
|
'Error field matches value - ' . $result,
|
|
|
|
$this->getSession()->getDriver()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->wait_for_pending_js();
|
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
|
|
|
* Checks that the current header stripe in the app contains the expected text.
|
|
|
|
*
|
|
|
|
* This can be used to see if the app went to the expected page.
|
|
|
|
*
|
2021-06-03 09:20:36 +00:00
|
|
|
* @Then /^the header should be "((?:[^"]|\\")+)" in the app$/
|
2018-11-12 12:11:06 +00:00
|
|
|
* @param string $text Expected header text
|
|
|
|
* @throws DriverException If the header can't be retrieved
|
|
|
|
* @throws ExpectationException If the header text is different to the expected value
|
|
|
|
*/
|
|
|
|
public function the_header_should_be_in_the_app(string $text) {
|
2021-04-27 15:43:47 +00:00
|
|
|
$this->spin(function() use ($text) {
|
2022-06-30 11:48:43 +00:00
|
|
|
$result = $this->runtime_js('getHeader()');
|
2021-04-27 15:43:47 +00:00
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
if (substr($result, 0, 3) !== 'OK:') {
|
|
|
|
throw new DriverException('Error getting header - ' . $result);
|
|
|
|
}
|
2021-04-27 15:43:47 +00:00
|
|
|
|
|
|
|
$header = substr($result, 3);
|
|
|
|
if (trim($header) !== trim($text)) {
|
|
|
|
throw new ExpectationException(
|
|
|
|
"The header text was not as expected: '$header'",
|
|
|
|
$this->getSession()->getDriver()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2018-11-12 12:11:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-05-26 15:09:47 +00:00
|
|
|
/**
|
|
|
|
* Check that the app opened a new browser tab.
|
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @Then /^the app should( not)? have opened a browser tab(?: with url "(?P<pattern>[^"]+)")?$/
|
2022-06-02 15:09:01 +00:00
|
|
|
* @param bool $not Whether to check if the app did not open a new browser tab
|
|
|
|
* @param string $urlpattern Url pattern
|
2021-05-26 15:09:47 +00:00
|
|
|
*/
|
2021-10-08 13:11:02 +00:00
|
|
|
public function the_app_should_have_opened_a_browser_tab(bool $not = false, ?string $urlpattern = null) {
|
|
|
|
$this->spin(function() use ($not, $urlpattern) {
|
2022-05-09 16:00:30 +00:00
|
|
|
$windowNames = $this->getSession()->getWindowNames();
|
|
|
|
$openedbrowsertab = count($windowNames) === 2;
|
2021-05-26 15:09:47 +00:00
|
|
|
|
2021-10-08 13:11:02 +00:00
|
|
|
if ((!$not && !$openedbrowsertab) || ($not && $openedbrowsertab && is_null($urlpattern))) {
|
2021-05-26 15:09:47 +00:00
|
|
|
throw new ExpectationException(
|
|
|
|
$not
|
|
|
|
? 'Did not expect the app to have opened a browser tab'
|
|
|
|
: 'Expected the app to have opened a browser tab',
|
|
|
|
$this->getSession()->getDriver()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-10-08 13:11:02 +00:00
|
|
|
if (!is_null($urlpattern)) {
|
2022-05-09 16:00:30 +00:00
|
|
|
$this->getSession()->switchToWindow($windowNames[1]);
|
2021-10-08 13:11:02 +00:00
|
|
|
$windowurl = $this->getSession()->getCurrentUrl();
|
2022-09-01 08:45:49 +00:00
|
|
|
$windowhaspattern = (bool)preg_match("/$urlpattern/", $windowurl);
|
2022-05-09 16:00:30 +00:00
|
|
|
$this->getSession()->switchToWindow($windowNames[0]);
|
2021-10-08 13:11:02 +00:00
|
|
|
|
|
|
|
if ($not === $windowhaspattern) {
|
|
|
|
throw new ExpectationException(
|
|
|
|
$not
|
|
|
|
? "Did not expect the app to have opened a browser tab with pattern '$urlpattern'"
|
|
|
|
: "Browser tab url does not match pattern '$urlpattern', it is '$windowurl'",
|
|
|
|
$this->getSession()->getDriver()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 15:09:47 +00:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
|
|
|
* Switches to a newly-opened browser tab.
|
|
|
|
*
|
|
|
|
* This assumes the app opened a new tab.
|
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @Given I switch to the browser tab opened by the app
|
2018-11-12 12:11:06 +00:00
|
|
|
* @throws DriverException If there aren't exactly 2 tabs open
|
|
|
|
*/
|
|
|
|
public function i_switch_to_the_browser_tab_opened_by_the_app() {
|
2022-05-09 16:00:30 +00:00
|
|
|
$windowNames = $this->getSession()->getWindowNames();
|
|
|
|
if (count($windowNames) !== 2) {
|
|
|
|
throw new DriverException('Expected to see 2 tabs open, not ' . count($windowNames));
|
2018-11-12 12:11:06 +00:00
|
|
|
}
|
2022-05-09 16:00:30 +00:00
|
|
|
$this->getSession()->switchToWindow($windowNames[1]);
|
2018-11-12 12:11:06 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 06:00:28 +00:00
|
|
|
/**
|
|
|
|
* Switches to the app if the user is in a browser tab.
|
|
|
|
*
|
|
|
|
* @Given I switch back to the app
|
|
|
|
*/
|
|
|
|
public function i_switch_back_to_the_app() {
|
|
|
|
$windowNames = $this->getSession()->getWindowNames();
|
|
|
|
if (count($windowNames) > 1) {
|
|
|
|
$this->getSession()->switchToWindow($windowNames[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 15:39:47 +00:00
|
|
|
/**
|
|
|
|
* Force cron tasks instead of waiting for the next scheduled execution.
|
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @When I run cron tasks in the app
|
2021-06-07 15:39:47 +00:00
|
|
|
*/
|
|
|
|
public function i_run_cron_tasks_in_the_app() {
|
2022-06-30 11:48:43 +00:00
|
|
|
$this->zone_js('cronDelegate.forceSyncExecution()');
|
2022-06-14 10:29:59 +00:00
|
|
|
$this->wait_for_pending_js();
|
2021-06-07 15:39:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait until loading has finished.
|
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @When I wait loading to finish in the app
|
2021-06-07 15:39:47 +00:00
|
|
|
*/
|
|
|
|
public function i_wait_loading_to_finish_in_the_app() {
|
2022-06-30 11:48:43 +00:00
|
|
|
$this->runtime_js('waitLoadingToFinish()');
|
2022-06-14 10:29:59 +00:00
|
|
|
$this->wait_for_pending_js();
|
2021-06-07 15:39:47 +00:00
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
/**
|
|
|
|
* Closes the current browser tab.
|
|
|
|
*
|
|
|
|
* This assumes it was opened by the app and you will now get back to the app.
|
|
|
|
*
|
2022-05-12 13:31:08 +00:00
|
|
|
* @Given I close the browser tab opened by the app
|
2018-11-12 12:11:06 +00:00
|
|
|
* @throws DriverException If there aren't exactly 2 tabs open
|
|
|
|
*/
|
|
|
|
public function i_close_the_browser_tab_opened_by_the_app() {
|
|
|
|
$names = $this->getSession()->getWindowNames();
|
|
|
|
if (count($names) !== 2) {
|
|
|
|
throw new DriverException('Expected to see 2 tabs open, not ' . count($names));
|
|
|
|
}
|
2021-10-08 13:11:02 +00:00
|
|
|
// Make sure the browser tab is selected.
|
|
|
|
if ($this->getSession()->getWindowName() !== $names[1]) {
|
|
|
|
$this->getSession()->switchToWindow($names[1]);
|
|
|
|
}
|
|
|
|
|
2022-07-07 08:39:36 +00:00
|
|
|
$this->evaluate_script('window.close()');
|
2018-11-12 12:11:06 +00:00
|
|
|
$this->getSession()->switchToWindow($names[0]);
|
|
|
|
}
|
2019-05-31 11:51:17 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Switch navigator online mode.
|
|
|
|
*
|
2021-06-03 09:20:36 +00:00
|
|
|
* @Given /^I switch offline mode to "(true|false)"$/
|
2019-05-31 11:51:17 +00:00
|
|
|
* @param string $offline New value for navigator online mode
|
|
|
|
* @throws DriverException If the navigator.online mode is not available
|
2022-07-12 08:44:45 +00:00
|
|
|
* @deprecated since 4.1 use i_switch_network_connection instead.
|
2019-05-31 11:51:17 +00:00
|
|
|
*/
|
|
|
|
public function i_switch_offline_mode(string $offline) {
|
2022-07-12 08:44:45 +00:00
|
|
|
$this->i_switch_network_connection($offline == 'true' ? 'offline' : 'wifi');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Switch network connection.
|
|
|
|
*
|
|
|
|
* @When /^I switch network connection to (wifi|cellular|offline)$/
|
|
|
|
* @param string $more New network mode.
|
|
|
|
* @throws DriverException If the navigator.online mode is not available
|
|
|
|
*/
|
|
|
|
public function i_switch_network_connection(string $mode) {
|
|
|
|
switch ($mode) {
|
|
|
|
case 'wifi':
|
|
|
|
$this->runtime_js("network.setForceConnectionMode('$mode');");
|
|
|
|
break;
|
|
|
|
case 'cellular':
|
|
|
|
$this->runtime_js("network.setForceConnectionMode('$mode');");
|
|
|
|
break;
|
|
|
|
case 'offline':
|
|
|
|
$this->runtime_js("network.setForceConnectionMode('none');");
|
|
|
|
break;
|
2022-09-01 08:45:49 +00:00
|
|
|
default:
|
|
|
|
break;
|
2022-07-12 08:44:45 +00:00
|
|
|
}
|
2019-05-31 11:51:17 +00:00
|
|
|
}
|
2021-06-03 09:20:36 +00:00
|
|
|
|
2022-09-15 09:37:17 +00:00
|
|
|
/**
|
|
|
|
* Open a browser tab with a certain URL.
|
|
|
|
*
|
|
|
|
* @Then /^I open a browser tab with url "(?P<pattern>[^"]+)"$/
|
|
|
|
* @param string $url URL
|
|
|
|
*/
|
|
|
|
public function i_open_a_browser_tab_with_url(string $url) {
|
|
|
|
$this->execute_script("window.open('$url', '_system');");
|
|
|
|
|
|
|
|
$windowNames = $this->getSession()->getWindowNames();
|
|
|
|
$this->getSession()->switchToWindow($windowNames[1]);
|
|
|
|
}
|
|
|
|
|
2022-10-28 08:44:26 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a notification has been triggered and is present.
|
|
|
|
*
|
|
|
|
* @Then /^a notification with title (".+") is( not)? present in the app$/
|
|
|
|
* @param string $title Notification title
|
|
|
|
* @param bool $not Whether assert that the notification was not found
|
|
|
|
*/
|
|
|
|
public function notification_present_in_the_app(string $title, bool $not = false) {
|
|
|
|
$result = $this->runtime_js("notificationIsPresentWithText($title)");
|
|
|
|
|
|
|
|
if ($not && $result === 'YES') {
|
|
|
|
throw new ExpectationException("Notification is present", $this->getSession()->getDriver());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$not && $result === 'NO') {
|
|
|
|
throw new ExpectationException("Notification is not present", $this->getSession()->getDriver());
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($result !== 'YES' && $result !== 'NO') {
|
|
|
|
throw new DriverException('Error checking notification - ' . $result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close a notification present in the app
|
|
|
|
*
|
|
|
|
* @Then /^I close a notification with title (".+") in the app$/
|
|
|
|
* @param string $title Notification title
|
|
|
|
*/
|
|
|
|
public function close_notification_app(string $title) {
|
|
|
|
$result = $this->runtime_js("closeNotification($title)");
|
|
|
|
|
|
|
|
if ($result !== 'OK') {
|
|
|
|
throw new DriverException('Error closing notification - ' . $result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-16 11:20:48 +00:00
|
|
|
/**
|
|
|
|
* View a specific month in the calendar in the app.
|
|
|
|
*
|
|
|
|
* @When /^I open the calendar for "(?P<month>\d+)" "(?P<year>\d+)" in the app$/
|
|
|
|
* @param int $month the month selected as a number
|
|
|
|
* @param int $year the four digit year
|
|
|
|
*/
|
|
|
|
public function i_open_the_calendar_for($month, $year) {
|
|
|
|
$options = json_encode([
|
|
|
|
'params' => [
|
|
|
|
'month' => $month,
|
|
|
|
'year' => $year,
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->zone_js("navigator.navigateToSitePath('/calendar/index', $options)");
|
|
|
|
}
|
|
|
|
|
2018-11-12 12:11:06 +00:00
|
|
|
}
|