commit
e9bd8c7bbe
|
@ -0,0 +1,42 @@
|
||||||
|
@mod @mod_messages @app @javascript
|
||||||
|
Feature: Test messages navigation in the app
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the following "users" exist:
|
||||||
|
| username | firstname |
|
||||||
|
| teacher | Teacher |
|
||||||
|
| student | Student |
|
||||||
|
And the following "courses" exist:
|
||||||
|
| fullname | shortname |
|
||||||
|
| Course 1 | C1 |
|
||||||
|
And the following "course enrolments" exist:
|
||||||
|
| user | course | role |
|
||||||
|
| teacher | C1 | editingteacher |
|
||||||
|
| student | C1 | student |
|
||||||
|
|
||||||
|
Scenario: Avoid recursive links to profile
|
||||||
|
When I enter the app
|
||||||
|
And I log in as "teacher"
|
||||||
|
And I press "Messages" in the app
|
||||||
|
And I press "Contacts" in the app
|
||||||
|
And I press "Search people and messages" in the app
|
||||||
|
And I set the field "Search" to "student" in the app
|
||||||
|
And I press "Search" "button" in the app
|
||||||
|
And I press "Student" in the app
|
||||||
|
And I set the field "New message" to "Hi there" in the app
|
||||||
|
And I press "Send" in the app
|
||||||
|
Then I should find "Hi there" in the app
|
||||||
|
|
||||||
|
When I press "Display options" in the app
|
||||||
|
And I press "User info" in the app
|
||||||
|
Then I should find "Details" in the app
|
||||||
|
|
||||||
|
When I press "Message" in the app
|
||||||
|
Then I should find "Hi there" in the app
|
||||||
|
|
||||||
|
When I press "Display options" in the app
|
||||||
|
Then I should not find "User info" in the app
|
||||||
|
|
||||||
|
When I press the back button in the app
|
||||||
|
And I press the back button in the app
|
||||||
|
Then I should find "Hi there" in the app
|
|
@ -100,6 +100,19 @@ class behat_app extends behat_base {
|
||||||
$this->prepare_browser();
|
$this->prepare_browser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Then /^I wait the app to restart$/
|
||||||
|
*/
|
||||||
|
public function i_wait_the_app_to_restart() {
|
||||||
|
// Wait window to reload.
|
||||||
|
$this->spin(function() {
|
||||||
|
return $this->evaluate_script("return !window.behat;");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prepare testing runtime again.
|
||||||
|
$this->prepare_browser(false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds elements in the app.
|
* Finds elements in the app.
|
||||||
*
|
*
|
||||||
|
@ -347,24 +360,28 @@ class behat_app extends behat_base {
|
||||||
* @param string $url App URL
|
* @param string $url App URL
|
||||||
* @throws DriverException If the app fails to load properly
|
* @throws DriverException If the app fails to load properly
|
||||||
*/
|
*/
|
||||||
protected function prepare_browser() {
|
protected function prepare_browser(bool $restart = true) {
|
||||||
// Restart the browser and set its size.
|
if ($restart) {
|
||||||
$this->getSession()->restart();
|
// Restart the browser and set its size.
|
||||||
$this->resize_window('360x720', true);
|
$this->getSession()->restart();
|
||||||
|
$this->resize_window('360x720', true);
|
||||||
|
|
||||||
if (empty($this->ionicurl)) {
|
if (empty($this->ionicurl)) {
|
||||||
$this->ionicurl = $this->start_or_reuse_ionic();
|
$this->ionicurl = $this->start_or_reuse_ionic();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the app is running a legacy version.
|
||||||
|
$json = @file_get_contents("{$this->ionicurl}/assets/env.json") ?: @file_get_contents("{$this->ionicurl}/config.json");
|
||||||
|
$data = json_decode($json);
|
||||||
|
$appversion = $data->build->version ?? str_replace('-dev', '', $data->versionname);
|
||||||
|
|
||||||
|
$this->islegacy = version_compare($appversion, '3.9.5', '<');
|
||||||
|
|
||||||
|
// Visit the Ionic URL.
|
||||||
|
$this->getSession()->visit($this->ionicurl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the app is running a legacy version.
|
// Wait the application to load.
|
||||||
$json = @file_get_contents("{$this->ionicurl}/assets/env.json") ?: @file_get_contents("{$this->ionicurl}/config.json");
|
|
||||||
$data = json_decode($json);
|
|
||||||
$appversion = $data->build->version ?? str_replace('-dev', '', $data->versionname);
|
|
||||||
|
|
||||||
$this->islegacy = version_compare($appversion, '3.9.5', '<');
|
|
||||||
|
|
||||||
// Visit the Ionic URL and wait for it to load.
|
|
||||||
$this->getSession()->visit($this->ionicurl);
|
|
||||||
$this->spin(function($context) {
|
$this->spin(function($context) {
|
||||||
$title = $context->getSession()->getPage()->find('xpath', '//title');
|
$title = $context->getSession()->getPage()->find('xpath', '//title');
|
||||||
|
|
||||||
|
@ -387,34 +404,36 @@ class behat_app extends behat_base {
|
||||||
$this->execute_script("window.BehatMoodleAppLegacy = $islegacyboolean;");
|
$this->execute_script("window.BehatMoodleAppLegacy = $islegacyboolean;");
|
||||||
$this->execute_script(file_get_contents(__DIR__ . '/app_behat_runtime.js'));
|
$this->execute_script(file_get_contents(__DIR__ . '/app_behat_runtime.js'));
|
||||||
|
|
||||||
// Assert initial page.
|
if ($restart) {
|
||||||
$this->spin(function($context) {
|
// Assert initial page.
|
||||||
$page = $context->getSession()->getPage();
|
$this->spin(function($context) {
|
||||||
$element = $page->find('xpath', '//page-core-login-site//input[@name="url"]');
|
$page = $context->getSession()->getPage();
|
||||||
|
$element = $page->find('xpath', '//page-core-login-site//input[@name="url"]');
|
||||||
if ($element) {
|
|
||||||
// Wait for the onboarding modal to open, if any.
|
|
||||||
$this->wait_for_pending_js();
|
|
||||||
|
|
||||||
$element = $this->islegacy
|
|
||||||
? $page->find('xpath', '//page-core-login-site-onboarding')
|
|
||||||
: $page->find('xpath', '//core-login-site-onboarding');
|
|
||||||
|
|
||||||
if ($element) {
|
if ($element) {
|
||||||
$this->i_press_in_the_app($this->parse_element_locator('"Skip"'));
|
// Wait for the onboarding modal to open, if any.
|
||||||
|
$this->wait_for_pending_js();
|
||||||
|
|
||||||
|
$element = $this->islegacy
|
||||||
|
? $page->find('xpath', '//page-core-login-site-onboarding')
|
||||||
|
: $page->find('xpath', '//core-login-site-onboarding');
|
||||||
|
|
||||||
|
if ($element) {
|
||||||
|
$this->i_press_in_the_app($this->parse_element_locator('"Skip"'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login screen found.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login screen found.
|
if ($page->find('xpath', '//page-core-mainmenu')) {
|
||||||
return true;
|
// Main menu found.
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($page->find('xpath', '//page-core-mainmenu')) {
|
throw new DriverException('Moodle app not launched properly');
|
||||||
// Main menu found.
|
}, false, 60);
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
throw new DriverException('Moodle app not launched properly');
|
|
||||||
}, false, 60);
|
|
||||||
|
|
||||||
// Continue only after JS finishes.
|
// Continue only after JS finishes.
|
||||||
$this->wait_for_pending_js();
|
$this->wait_for_pending_js();
|
||||||
|
@ -484,12 +503,12 @@ class behat_app extends behat_base {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives push notifications for forum events.
|
* Receives push notifications.
|
||||||
*
|
*
|
||||||
* @Given /^I receive a forum push notification for:$/
|
* @Given /^I receive a push notification in the app for:$/
|
||||||
* @param TableNode $data
|
* @param TableNode $data
|
||||||
*/
|
*/
|
||||||
public function i_receive_a_forum_push_notification(TableNode $data) {
|
public function i_receive_a_push_notification(TableNode $data) {
|
||||||
global $DB, $CFG;
|
global $DB, $CFG;
|
||||||
|
|
||||||
$data = (object) $data->getColumnsHash()[0];
|
$data = (object) $data->getColumnsHash()[0];
|
||||||
|
@ -513,6 +532,41 @@ class behat_app extends behat_base {
|
||||||
$this->wait_for_pending_js();
|
$this->wait_for_pending_js();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace arguments from the content in the given activity field.
|
||||||
|
*
|
||||||
|
* @Given /^I replace the arguments in "([^"]+)" "([^"]+)"$/
|
||||||
|
*/
|
||||||
|
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}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a custom link.
|
||||||
|
*
|
||||||
|
* @Given /^I open a custom link in the app for:$/
|
||||||
|
*/
|
||||||
|
public function i_open_a_custom_link(TableNode $data) {
|
||||||
|
global $DB, $CFG;
|
||||||
|
|
||||||
|
$data = (object) $data->getColumnsHash()[0];
|
||||||
|
$discussion = $DB->get_record('forum_discussions', ['name' => $data->discussion]);
|
||||||
|
$pageurl = "{$CFG->behat_wwwroot}/mod/forum/discuss.php?d={$discussion->id}";
|
||||||
|
$url = "moodlemobile://link=" . urlencode($pageurl);
|
||||||
|
|
||||||
|
$this->evaluate_script("return window.urlSchemes.handleCustomURL('$url')");
|
||||||
|
$this->wait_for_pending_js();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes a popup by clicking on the 'backdrop' behind it.
|
* Closes a popup by clicking on the 'backdrop' behind it.
|
||||||
*
|
*
|
||||||
|
@ -853,4 +907,31 @@ class behat_app extends behat_base {
|
||||||
return str_replace('$WWWROOT', $CFG->behat_wwwroot, $text);
|
return str_replace('$WWWROOT', $CFG->behat_wwwroot, $text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace arguments with the format "${activity:field}" from a string, where "activity" is
|
||||||
|
* the idnumber of an activity and "field" is the activity's field to get replacement from.
|
||||||
|
*
|
||||||
|
* At the moment, the only field supported is "cmid", the id of the course module for this activity.
|
||||||
|
*
|
||||||
|
* @param string $text Original text.
|
||||||
|
* @return string Text with arguments replaced.
|
||||||
|
*/
|
||||||
|
protected function replace_arguments(string $text): string {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
preg_match_all("/\\$\\{([^:}]+):([^}]+)\\}/", $text, $matches);
|
||||||
|
|
||||||
|
foreach ($matches[0] as $index => $match) {
|
||||||
|
switch ($matches[2][$index]) {
|
||||||
|
case 'cmid':
|
||||||
|
$coursemodule = $DB->get_record('course_modules', ['idnumber' => $matches[1][$index]]);
|
||||||
|
$text = str_replace($match, $coursemodule->id, $text);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
@app @javascript
|
||||||
|
Feature: It navigates properly within activities.
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the following "users" exist:
|
||||||
|
| username |
|
||||||
|
| student |
|
||||||
|
And the following "courses" exist:
|
||||||
|
| fullname | shortname |
|
||||||
|
| Course 1 | C1 |
|
||||||
|
And the following "course enrolments" exist:
|
||||||
|
| user | course | role |
|
||||||
|
| student | C1 | student |
|
||||||
|
And the following "activities" exist:
|
||||||
|
| activity | idnumber | course | name | intro | content |
|
||||||
|
| label | label | C1 | Label | Label description | - |
|
||||||
|
| page | page | C1 | Page | - | <a href="/mod/label/view.php?id=${label:cmid}">Go to label</a> |
|
||||||
|
And I replace the arguments in "page" "content"
|
||||||
|
|
||||||
|
Scenario: Navigates using deep links
|
||||||
|
When I enter the app
|
||||||
|
And I log in as "student"
|
||||||
|
And I press "Course 1" in the app
|
||||||
|
And I press "Page" in the app
|
||||||
|
And I press "Go to label" in the app
|
||||||
|
Then I should find "Label description" in the app
|
||||||
|
|
||||||
|
When I press the back button in the app
|
||||||
|
Then I should find "Go to label" in the app
|
||||||
|
|
||||||
|
When I press the back button in the app
|
||||||
|
Then I should find "Label description" in the app
|
|
@ -1,5 +1,5 @@
|
||||||
@app @javascript
|
@app @javascript
|
||||||
Feature: It navigates properly after receiving push notifications.
|
Feature: It navigates properly using deep links.
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given the following "users" exist:
|
Given the following "users" exist:
|
||||||
|
@ -22,7 +22,7 @@ Feature: It navigates properly after receiving push notifications.
|
||||||
And the following config values are set as admin:
|
And the following config values are set as admin:
|
||||||
| forcelogout | 1 | tool_mobile |
|
| forcelogout | 1 | tool_mobile |
|
||||||
|
|
||||||
Scenario: Open a forum push notification
|
Scenario: Receive a push notification
|
||||||
When I enter the app
|
When I enter the app
|
||||||
And I log in as "student2"
|
And I log in as "student2"
|
||||||
And I press the main menu button in the app
|
And I press the main menu button in the app
|
||||||
|
@ -31,12 +31,33 @@ Feature: It navigates properly after receiving push notifications.
|
||||||
And I set the field "Your site" to "$WWWROOT" in the app
|
And I set the field "Your site" to "$WWWROOT" in the app
|
||||||
And I press "Connect to your site" in the app
|
And I press "Connect to your site" in the app
|
||||||
And I log in as "student1"
|
And I log in as "student1"
|
||||||
And I receive a forum push notification for:
|
And I receive a push notification in the app for:
|
||||||
| username | course | module | discussion |
|
| username | module | discussion |
|
||||||
| student2 | C1 | forum | Forum topic |
|
| student2 | forum | Forum topic |
|
||||||
Then I should find "Reconnect" in the app
|
Then I should find "Reconnect" in the app
|
||||||
|
|
||||||
When I set the field "Password" to "student2" in the app
|
When I set the field "Password" to "student2" in the app
|
||||||
And I press "Log in" in the app
|
And I press "Log in" in the app
|
||||||
Then I should find "Forum topic" in the app
|
Then I should find "Forum topic" in the app
|
||||||
And I should find "Forum message" in the app
|
And I should find "Forum message" in the app
|
||||||
|
But I should not find "Site home" in the app
|
||||||
|
|
||||||
|
When I press the back button in the app
|
||||||
|
Then I should find "Site home" in the app
|
||||||
|
But I should not find "Forum topic" in the app
|
||||||
|
And I should not find "Forum message" in the app
|
||||||
|
|
||||||
|
Scenario: Open a link with a custom URL
|
||||||
|
When I launch the app
|
||||||
|
And I open a custom link in the app for:
|
||||||
|
| discussion |
|
||||||
|
| Forum topic |
|
||||||
|
And I log in as "student1"
|
||||||
|
Then I should find "Forum topic" in the app
|
||||||
|
And I should find "Forum message" in the app
|
||||||
|
But I should not find "Site home" in the app
|
||||||
|
|
||||||
|
When I press the back button in the app
|
||||||
|
Then I should find "Site home" in the app
|
||||||
|
But I should not find "Forum topic" in the app
|
||||||
|
And I should not find "Forum message" in the app
|
Loading…
Reference in New Issue