commit
5630f5b054
|
@ -91,8 +91,6 @@ class behat_app extends behat_app_helper {
|
|||
* @throws ExpectationException Problem with resizing window
|
||||
*/
|
||||
public function i_launch_the_app(string $runtime = '') {
|
||||
$this->check_tags();
|
||||
|
||||
// Go to page and prepare browser for app.
|
||||
$this->prepare_browser(['skiponboarding' => empty($runtime)]);
|
||||
}
|
||||
|
@ -101,18 +99,27 @@ class behat_app extends behat_app_helper {
|
|||
* @Then I wait the app to restart
|
||||
*/
|
||||
public function i_wait_the_app_to_restart() {
|
||||
// Wait window to reload.
|
||||
$this->spin(function() {
|
||||
if ($this->runtime_js('hasInitialized()')) {
|
||||
// Behat runtime shouldn't be initialized after reload.
|
||||
throw new DriverException('Window is not reloading properly.');
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// Prepare testing runtime again.
|
||||
$this->prepare_browser(['restart' => false]);
|
||||
$this->prepare_browser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -567,7 +574,7 @@ class behat_app extends behat_app_helper {
|
|||
/**
|
||||
* Performs a pull to refresh gesture.
|
||||
*
|
||||
* @When /^I pull to refresh in the app$/
|
||||
* @When I pull to refresh in the app
|
||||
* @throws DriverException If the gesture is not available
|
||||
*/
|
||||
public function i_pull_to_refresh_in_the_app() {
|
||||
|
@ -841,9 +848,31 @@ class behat_app extends behat_app_helper {
|
|||
* @Given /^I switch offline mode to "(true|false)"$/
|
||||
* @param string $offline New value for navigator online mode
|
||||
* @throws DriverException If the navigator.online mode is not available
|
||||
* @deprecated since 4.1 use i_switch_network_connection instead.
|
||||
*/
|
||||
public function i_switch_offline_mode(string $offline) {
|
||||
$this->runtime_js("network.setForceOffline($offline)");
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -95,6 +95,12 @@ class behat_app_helper extends behat_base {
|
|||
public function start_scenario() {
|
||||
$this->check_behat_setup();
|
||||
$this->fix_moodle_setup();
|
||||
|
||||
if ($this->apprunning) {
|
||||
$this->notify_unload();
|
||||
$this->apprunning = false;
|
||||
}
|
||||
|
||||
$this->ionicurl = $this->start_or_reuse_ionic();
|
||||
}
|
||||
|
||||
|
@ -274,17 +280,20 @@ class behat_app_helper extends behat_base {
|
|||
* @throws DriverException If the app fails to load properly
|
||||
*/
|
||||
protected function prepare_browser(array $options = []) {
|
||||
$restart = $options['restart'] ?? true;
|
||||
if ($this->evaluate_script('window.behat') && $this->runtime_js('hasInitialized()')) {
|
||||
// Already initialized.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($restart) {
|
||||
if ($this->apprunning) {
|
||||
$this->notify_unload();
|
||||
}
|
||||
$restart = false;
|
||||
|
||||
// Restart the browser and set its size.
|
||||
$this->getSession()->restart();
|
||||
if (!$this->apprunning) {
|
||||
$this->check_tags();
|
||||
|
||||
$restart = true;
|
||||
|
||||
// Reset its size.
|
||||
$this->resize_window($this->windowsize, true);
|
||||
|
||||
if (empty($this->ionicurl)) {
|
||||
$this->ionicurl = $this->start_or_reuse_ionic();
|
||||
}
|
||||
|
@ -506,6 +515,8 @@ class behat_app_helper extends behat_base {
|
|||
$successXPath = '//page-core-mainmenu';
|
||||
}
|
||||
|
||||
$this->i_log_out_in_app(false);
|
||||
|
||||
$this->handle_url($url, $successXPath);
|
||||
}
|
||||
|
||||
|
@ -536,7 +547,6 @@ class behat_app_helper extends behat_base {
|
|||
if ($result !== 'OK') {
|
||||
throw new DriverException('Error handling url - ' . $result);
|
||||
}
|
||||
|
||||
if (!empty($successXPath)) {
|
||||
// Wait until the page appears.
|
||||
$this->spin(
|
||||
|
@ -550,6 +560,8 @@ class behat_app_helper extends behat_base {
|
|||
}
|
||||
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
$this->i_wait_the_app_to_restart();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,6 +68,8 @@ function do_match {
|
|||
print_message "$2"
|
||||
tput setaf 6
|
||||
grep "$match" $LANGPACKSFOLDER/en/*.php
|
||||
else
|
||||
coincidence=0
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
@ -1705,7 +1705,9 @@
|
|||
"core.erroropenfilenoextension": "local_moodlemobileapp",
|
||||
"core.erroropenpopup": "local_moodlemobileapp",
|
||||
"core.errorrenamefile": "local_moodlemobileapp",
|
||||
"core.errorsitesupport": "local_moodlemobileapp",
|
||||
"core.errorsomedatanotdownloaded": "local_moodlemobileapp",
|
||||
"core.errorsomethingwrong": "local_moodlemobileapp",
|
||||
"core.errorsync": "local_moodlemobileapp",
|
||||
"core.errorsyncblocked": "local_moodlemobileapp",
|
||||
"core.errorurlschemeinvalidscheme": "local_moodlemobileapp",
|
||||
|
@ -2179,6 +2181,8 @@
|
|||
"core.settings.colorscheme-system": "local_moodlemobileapp",
|
||||
"core.settings.colorscheme-system-notice": "local_moodlemobileapp",
|
||||
"core.settings.compilationinfo": "local_moodlemobileapp",
|
||||
"core.settings.connecttosync": "local_moodlemobileapp",
|
||||
"core.settings.connectwifitosync": "local_moodlemobileapp",
|
||||
"core.settings.copyinfo": "local_moodlemobileapp",
|
||||
"core.settings.cordovadevicemodel": "local_moodlemobileapp",
|
||||
"core.settings.cordovadeviceosversion": "local_moodlemobileapp",
|
||||
|
@ -2200,9 +2204,7 @@
|
|||
"core.settings.enablefirebaseanalyticsdescription": "local_moodlemobileapp",
|
||||
"core.settings.enablerichtexteditor": "local_moodlemobileapp",
|
||||
"core.settings.enablerichtexteditordescription": "local_moodlemobileapp",
|
||||
"core.settings.enablesyncwifi": "local_moodlemobileapp",
|
||||
"core.settings.entriesincache": "local_moodlemobileapp",
|
||||
"core.settings.errorsyncsite": "local_moodlemobileapp",
|
||||
"core.settings.estimatedfreespace": "local_moodlemobileapp",
|
||||
"core.settings.filesystemroot": "local_moodlemobileapp",
|
||||
"core.settings.fontsize": "local_moodlemobileapp",
|
||||
|
@ -2219,6 +2221,7 @@
|
|||
"core.settings.locationhref": "local_moodlemobileapp",
|
||||
"core.settings.loggedin": "message",
|
||||
"core.settings.loggedoff": "message",
|
||||
"core.settings.logintosync": "local_moodlemobileapp",
|
||||
"core.settings.navigatorlanguage": "local_moodlemobileapp",
|
||||
"core.settings.navigatoruseragent": "local_moodlemobileapp",
|
||||
"core.settings.networkstatus": "local_moodlemobileapp",
|
||||
|
@ -2233,7 +2236,9 @@
|
|||
"core.settings.showdownloadoptions": "local_moodlemobileapp",
|
||||
"core.settings.siteinfo": "local_moodlemobileapp",
|
||||
"core.settings.sites": "moodle",
|
||||
"core.settings.sitesyncfailed": "local_moodlemobileapp",
|
||||
"core.settings.spaceusage": "local_moodlemobileapp",
|
||||
"core.settings.syncdatasaver": "local_moodlemobileapp",
|
||||
"core.settings.synchronization": "local_moodlemobileapp",
|
||||
"core.settings.synchronizenow": "local_moodlemobileapp",
|
||||
"core.settings.synchronizenowhelp": "local_moodlemobileapp",
|
||||
|
|
|
@ -49,7 +49,6 @@ Feature: Timeline block.
|
|||
@lms_from3.11
|
||||
Scenario: See courses inside block
|
||||
Given I entered the app as "student1"
|
||||
And I press "Open block drawer" in the app
|
||||
Then I should find "Assignment 00" within "Timeline" "ion-card" in the app
|
||||
And I should find "Assignment 02" within "Timeline" "ion-card" in the app
|
||||
And I should find "Assignment 05" within "Timeline" "ion-card" in the app
|
||||
|
|
|
@ -163,7 +163,7 @@ Feature: Test basic usage of messages in app
|
|||
And I set the field "Search" to "student1" in the app
|
||||
And I press "Search" "button" in the app
|
||||
And I press "Student1 student1" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the field "New message" to "heeey student" in the app
|
||||
And I press "Send" in the app
|
||||
Then I should find "heeey student" in the app
|
||||
|
@ -172,7 +172,7 @@ Feature: Test basic usage of messages in app
|
|||
And I press "Send" in the app
|
||||
Then I should find "byee" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "Student1 student1" in the app
|
||||
Then I should find "heeey student" in the app
|
||||
|
@ -192,14 +192,14 @@ Feature: Test basic usage of messages in app
|
|||
And I set the field "Search" to "student1" in the app
|
||||
And I press "Search" "button" in the app
|
||||
And I press "Student1 student1" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the field "New message" to "heeey student" in the app
|
||||
And I press "Send" in the app
|
||||
And I set the field "New message" to "byee" in the app
|
||||
And I press "Send" in the app
|
||||
Then I should find "byee" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I run cron tasks in the app
|
||||
|
||||
Given I entered the app as "student1"
|
||||
|
@ -346,12 +346,12 @@ Feature: Test basic usage of messages in app
|
|||
And I press "Send" in the app
|
||||
Then I should find "self conversation online" in the app
|
||||
|
||||
When I switch offline mode to "true"
|
||||
When I switch network connection to offline
|
||||
And I set the field "New message" to "self conversation offline" in the app
|
||||
And I press "Send" in the app
|
||||
Then I should find "self conversation offline" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "Student1 student1" in the app
|
||||
And I press "Display options" in the app
|
||||
|
|
|
@ -79,6 +79,7 @@ Feature: Test basic usage of assignment activity in app
|
|||
|
||||
# Submit second attempt as a student
|
||||
Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
|
||||
When I pull to refresh in the app
|
||||
Then I should find "Reopened" in the app
|
||||
And I should find "2 out of Unlimited" in the app
|
||||
And I should find "Add a new attempt based on previous submission" in the app
|
||||
|
@ -97,6 +98,7 @@ Feature: Test basic usage of assignment activity in app
|
|||
# View second attempt as a teacher
|
||||
Given I entered the assign activity "assignment1" on course "Course 1" as "teacher1" in the app
|
||||
When I press "Participants" in the app
|
||||
And I pull to refresh in the app
|
||||
And I press "Student student" near "assignment1" in the app
|
||||
Then I should find "Online text submissions" in the app
|
||||
And I should find "Submission test 2nd attempt" in the app
|
||||
|
@ -104,14 +106,14 @@ Feature: Test basic usage of assignment activity in app
|
|||
Scenario: Add submission offline (online text) & Submit for grading offline & Sync submissions
|
||||
Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
|
||||
When I press "Add submission" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the field "Online text submissions" to "Submission test" in the app
|
||||
And I press "Save" in the app
|
||||
And I press "Submit assignment" in the app
|
||||
And I press "OK" in the app
|
||||
Then I should find "This Assignment has offline data to be synchronised." in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "assignment1" in the app
|
||||
And I press "Information" in the app
|
||||
|
@ -122,7 +124,7 @@ Feature: Test basic usage of assignment activity in app
|
|||
Scenario: Edit an offline submission before synchronising it
|
||||
Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
|
||||
When I press "Add submission" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the field "Online text submissions" to "Submission test original offline" in the app
|
||||
And I press "Save" in the app
|
||||
Then I should find "This Assignment has offline data to be synchronised." in the app
|
||||
|
@ -139,7 +141,7 @@ Feature: Test basic usage of assignment activity in app
|
|||
And I press "OK" in the app
|
||||
Then I should find "This Assignment has offline data to be synchronised." in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "assignment1" in the app
|
||||
Then I should find "Submitted for grading" in the app
|
||||
|
|
|
@ -34,6 +34,9 @@ Feature: Test basic usage of chat in app
|
|||
And I press "Send" in the app
|
||||
Then I should find "Hi!" in the app
|
||||
And I should find "I am David" in the app
|
||||
# Confirm leave the page
|
||||
And I press the back button in the app
|
||||
And I press "OK" in the app
|
||||
|
||||
# Read messages, view connected users, send beep and reply as student2
|
||||
Given I entered the chat activity "Test chat name" on course "Course 1" as "student2" in the app
|
||||
|
@ -62,6 +65,9 @@ Feature: Test basic usage of chat in app
|
|||
When I set the field "New message" to "I am David" in the app
|
||||
And I press "Send" in the app
|
||||
Then I should find "I am David" in the app
|
||||
# Confirm leave the page
|
||||
And I press the back button in the app
|
||||
And I press "OK" in the app
|
||||
|
||||
# Read messages from past sessions as student2
|
||||
Given I entered the chat activity "Test chat name" on course "Course 1" as "student2" in the app
|
||||
|
|
|
@ -23,6 +23,9 @@ Feature: Test chat navigation
|
|||
And I set the field "New message" to "Test message" in the app
|
||||
And I press "Send" in the app
|
||||
Then I should find "Test message" in the app
|
||||
# Confirm leave the page
|
||||
And I press the back button in the app
|
||||
And I press "OK" in the app
|
||||
|
||||
Scenario: Tablet navigation on chat
|
||||
Given I entered the course "Course 1" as "student2" in the app
|
||||
|
|
|
@ -72,7 +72,7 @@ Feature: Test basic usage of choice activity in app
|
|||
| choice | Test single choice name | Test single choice description | C1 | choice1 | Option 1, Option 2, Option 3 | 0 | 0 | 1 |
|
||||
And I entered the choice activity "Test single choice name" on course "Course 1" as "student1" in the app
|
||||
When I select "Option 1" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I select "Option 2" in the app
|
||||
And I press "Save my choice" in the app
|
||||
Then I should find "Are you sure" in the app
|
||||
|
@ -85,7 +85,7 @@ Feature: Test basic usage of choice activity in app
|
|||
And I should not find "Option 2: 1" in the app
|
||||
And I should not find "Option 3: 0" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "Test single choice name" in the app
|
||||
Then I should find "Test single choice description" in the app
|
||||
|
@ -103,7 +103,7 @@ Feature: Test basic usage of choice activity in app
|
|||
| choice | Test single choice name | Test single choice description | C1 | choice1 | Option 1, Option 2, Option 3 | 0 | 0 | 1 |
|
||||
And I entered the choice activity "Test single choice name" on course "Course 1" as "student1" in the app
|
||||
When I select "Option 1" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I select "Option 2" in the app
|
||||
And I press "Save my choice" in the app
|
||||
Then I should find "Are you sure" in the app
|
||||
|
@ -114,7 +114,7 @@ Feature: Test basic usage of choice activity in app
|
|||
And I should not find "Option 2: 1" in the app
|
||||
And I should not find "Option 3: 0" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I run cron tasks in the app
|
||||
And I wait loading to finish in the app
|
||||
Then I should find "Option 1: 0" in the app
|
||||
|
@ -133,7 +133,7 @@ Feature: Test basic usage of choice activity in app
|
|||
Then I should find "Downloaded" within "Test single choice name" "ion-item" in the app
|
||||
And I press the back button in the app
|
||||
|
||||
When I switch offline mode to "true"
|
||||
When I switch network connection to offline
|
||||
And I press "Test multi choice name" in the app
|
||||
Then I should find "There was a problem connecting to the site. Please check your connection and try again." in the app
|
||||
|
||||
|
@ -152,7 +152,7 @@ Feature: Test basic usage of choice activity in app
|
|||
And I should not find "Option 2: 1" in the app
|
||||
And I should not find "Option 3: 0" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "Test single choice name" in the app
|
||||
Then I should find "Option 1: 0" in the app
|
||||
|
|
|
@ -33,7 +33,7 @@ Feature: Users can store entries in database activities when offline and sync wh
|
|||
|
||||
Scenario: Create entry (offline)
|
||||
Given I entered the data activity "Web links" on course "Course 1" as "student1" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I should find "No entries in database" in the app
|
||||
When I press "Add entries" in the app
|
||||
And I set the following fields to these values in the app:
|
||||
|
@ -44,7 +44,7 @@ Feature: Users can store entries in database activities when offline and sync wh
|
|||
And I should find "Moodle community site" in the app
|
||||
And I should find "This Database has offline data to be synchronised" in the app
|
||||
And I press the back button in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Web links" near "General" in the app
|
||||
And I should find "https://moodle.org/" in the app
|
||||
And I should find "Moodle community site" in the app
|
||||
|
@ -63,7 +63,8 @@ Feature: Users can store entries in database activities when offline and sync wh
|
|||
And I press "Information" in the app
|
||||
And I press "Download" in the app
|
||||
And I wait until the page is ready
|
||||
And I switch offline mode to "true"
|
||||
And I close the popup in the app
|
||||
And I switch network connection to offline
|
||||
When I press "Edit" in the app
|
||||
And I set the following fields to these values in the app:
|
||||
| URL | https://moodlecloud.com/ |
|
||||
|
@ -75,7 +76,7 @@ Feature: Users can store entries in database activities when offline and sync wh
|
|||
And I should find "Moodle Cloud" in the app
|
||||
And I should find "This Database has offline data to be synchronised" in the app
|
||||
And I press the back button in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Web links" near "General" in the app
|
||||
And I should not find "https://moodle.org/" in the app
|
||||
And I should not find "Moodle community site" in the app
|
||||
|
@ -85,7 +86,7 @@ Feature: Users can store entries in database activities when offline and sync wh
|
|||
And I press "Information" in the app
|
||||
And I press "Refresh" in the app
|
||||
And I wait until the page is ready
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Delete" in the app
|
||||
And I should find "Are you sure you want to delete this entry?" in the app
|
||||
And I press "Delete" in the app
|
||||
|
@ -93,7 +94,7 @@ Feature: Users can store entries in database activities when offline and sync wh
|
|||
And I should find "Moodle Cloud" in the app
|
||||
And I should find "This Database has offline data to be synchronised" in the app
|
||||
And I press the back button in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Web links" near "General" in the app
|
||||
And I should not find "https://moodlecloud.com/" in the app
|
||||
And I should not find "Moodle Cloud" in the app
|
||||
|
@ -112,7 +113,8 @@ Feature: Users can store entries in database activities when offline and sync wh
|
|||
And I press "Information" in the app
|
||||
And I press "Download" in the app
|
||||
And I wait until the page is ready
|
||||
When I switch offline mode to "true"
|
||||
And I close the popup in the app
|
||||
When I switch network connection to offline
|
||||
And I press "Delete" in the app
|
||||
And I should find "Are you sure you want to delete this entry?" in the app
|
||||
And I press "Delete" in the app
|
||||
|
@ -121,7 +123,7 @@ Feature: Users can store entries in database activities when offline and sync wh
|
|||
And I should find "This Database has offline data to be synchronised" in the app
|
||||
And I press "Restore" in the app
|
||||
And I press the back button in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Web links" near "General" in the app
|
||||
Then I should find "https://moodle.org/" in the app
|
||||
And I should find "Moodle community site" in the app
|
||||
|
|
|
@ -96,7 +96,7 @@ Feature: Test basic usage of forum activity in app
|
|||
Then I should find "Reply" in the app
|
||||
|
||||
When I press the back button in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Initial discussion" in the app
|
||||
And I press "Reply" in the app
|
||||
And I set the field "Message" to "not sent reply" in the app
|
||||
|
@ -110,7 +110,7 @@ Feature: Test basic usage of forum activity in app
|
|||
Then I should find "Not sent" in the app
|
||||
And I should find "This Discussion has offline data to be synchronised" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "Initial discussion" in the app
|
||||
Then I should not find "Not sent" in the app
|
||||
|
@ -118,7 +118,7 @@ Feature: Test basic usage of forum activity in app
|
|||
|
||||
Scenario: Edit a not sent new discussion offline
|
||||
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
|
||||
When I switch offline mode to "true"
|
||||
When I switch network connection to offline
|
||||
And I press "Add discussion topic" in the app
|
||||
And I set the following fields to these values in the app:
|
||||
| Subject | Auto-test |
|
||||
|
@ -129,7 +129,7 @@ Feature: Test basic usage of forum activity in app
|
|||
And I press "Post to forum" in the app
|
||||
Then I should find "This Forum has offline data to be synchronised." in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press "Auto-test" in the app
|
||||
Then I should find "Post to forum" in the app
|
||||
|
||||
|
@ -151,7 +151,7 @@ Feature: Test basic usage of forum activity in app
|
|||
Then I should find "Edit" in the app
|
||||
|
||||
When I press "Edit" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the field "Message" to "Auto-test message edited" in the app
|
||||
And I press "Save changes" in the app
|
||||
Then I should find "There was a problem connecting to the site. Please check your connection and try again." in the app
|
||||
|
@ -163,7 +163,7 @@ Feature: Test basic usage of forum activity in app
|
|||
And I press "Edit" in the app
|
||||
Then I should find "There was a problem connecting to the site. Please check your connection and try again." in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press "OK" in the app
|
||||
And I press "Edit" in the app
|
||||
And I set the field "Message" to "Auto-test message edited" in the app
|
||||
|
@ -183,7 +183,7 @@ Feature: Test basic usage of forum activity in app
|
|||
|
||||
When I press "Delete" in the app
|
||||
And I press "Cancel" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Display options" near "Reply" in the app
|
||||
Then I should find "Delete" in the app
|
||||
|
||||
|
@ -192,7 +192,7 @@ Feature: Test basic usage of forum activity in app
|
|||
|
||||
When I press "OK" in the app
|
||||
And I close the popup in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Display options" near "Reply" in the app
|
||||
And I press "Delete" in the app
|
||||
And I press "Delete" in the app
|
||||
|
@ -215,14 +215,14 @@ Feature: Test basic usage of forum activity in app
|
|||
When I press "Auto-test" in the app
|
||||
And I press "None" near "Auto-test message" in the app
|
||||
And I press "1" near "Cancel" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "None" near "test2" in the app
|
||||
And I press "0" near "Cancel" in the app
|
||||
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
|
||||
And I should find "Average of ratings: -" in the app
|
||||
And I should find "Average of ratings: 1" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
Then I should find "This Forum has offline data to be synchronised." in the app
|
||||
|
||||
|
@ -244,7 +244,7 @@ Feature: Test basic usage of forum activity in app
|
|||
Scenario: Reply a post offline
|
||||
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
|
||||
When I press "Initial discussion" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
Then I should find "Reply" in the app
|
||||
|
||||
When I press "Reply" in the app
|
||||
|
@ -255,7 +255,7 @@ Feature: Test basic usage of forum activity in app
|
|||
And I should find "Not sent" in the app
|
||||
|
||||
When I press the back button in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Initial discussion" in the app
|
||||
Then I should find "Initial discussion message" in the app
|
||||
And I should find "ReplyMessage" in the app
|
||||
|
@ -263,7 +263,7 @@ Feature: Test basic usage of forum activity in app
|
|||
|
||||
Scenario: New discussion offline & Sync Forum
|
||||
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
|
||||
When I switch offline mode to "true"
|
||||
When I switch network connection to offline
|
||||
And I press "Add discussion topic" in the app
|
||||
And I set the following fields to these values in the app:
|
||||
| Subject | DiscussionSubject |
|
||||
|
@ -273,7 +273,7 @@ Feature: Test basic usage of forum activity in app
|
|||
And I should find "Not sent" in the app
|
||||
And I should find "This Forum has offline data to be synchronised." in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "Test forum name" in the app
|
||||
And I press "Information" in the app
|
||||
|
@ -286,7 +286,7 @@ Feature: Test basic usage of forum activity in app
|
|||
|
||||
Scenario: New discussion offline & Auto-sync forum
|
||||
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
|
||||
When I switch offline mode to "true"
|
||||
When I switch network connection to offline
|
||||
And I press "Add discussion topic" in the app
|
||||
And I set the following fields to these values in the app:
|
||||
| Subject | DiscussionSubject |
|
||||
|
@ -296,7 +296,7 @@ Feature: Test basic usage of forum activity in app
|
|||
And I should find "Not sent" in the app
|
||||
And I should find "This Forum has offline data to be synchronised." in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I run cron tasks in the app
|
||||
And I wait loading to finish in the app
|
||||
Then I should not find "Not sent" in the app
|
||||
|
@ -314,7 +314,7 @@ Feature: Test basic usage of forum activity in app
|
|||
Then I should find "Downloaded" within "Test forum name" "ion-item" in the app
|
||||
|
||||
When I press the back button in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Test forum name" in the app
|
||||
Then I should find "Initial discussion" in the app
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ Feature: Test forum navigation
|
|||
# Offline
|
||||
When I press the back button in the app
|
||||
And I press "Add discussion topic" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the following fields to these values in the app:
|
||||
| Subject | Offline discussion 1 |
|
||||
| Message | Offline discussion 1 message |
|
||||
|
@ -199,7 +199,7 @@ Feature: Test forum navigation
|
|||
|
||||
# Offline
|
||||
When I press "Add discussion topic" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the following fields to these values in the app:
|
||||
| Subject | Offline discussion 1 |
|
||||
| Message | Offline discussion 1 message |
|
||||
|
|
|
@ -100,6 +100,7 @@ Feature: Test basic usage of glossary in app
|
|||
# View comments as a student
|
||||
Given I entered the glossary activity "Test glossary" on course "Course 1" as "student1" in the app
|
||||
And I press "Eggplant" in the app
|
||||
When I pull to refresh in the app
|
||||
Then I should find "Comments (2)" in the app
|
||||
|
||||
When I press "Comments" in the app
|
||||
|
@ -111,7 +112,7 @@ Feature: Test basic usage of glossary in app
|
|||
When I press "Course downloads" in the app
|
||||
When I press "Download" within "Test glossary" "ion-item" in the app
|
||||
And I press the back button in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Test glossary" in the app
|
||||
Then the header should be "Test glossary" in the app
|
||||
And I should find "Cucumber" in the app
|
||||
|
@ -156,7 +157,7 @@ Feature: Test basic usage of glossary in app
|
|||
Scenario: Sync
|
||||
Given I entered the glossary activity "Test glossary" on course "Course 1" as "student1" in the app
|
||||
And I press "Add a new entry" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the following fields to these values in the app:
|
||||
| Concept | Broccoli |
|
||||
| Definition | Brassica oleracea var. italica |
|
||||
|
@ -181,7 +182,7 @@ Feature: Test basic usage of glossary in app
|
|||
And I should find "Entries to be synced" in the app
|
||||
And I should find "This Glossary has offline data to be synchronised." in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press "Information" in the app
|
||||
And I press "Synchronise now" in the app
|
||||
Then the header should be "Test glossary" in the app
|
||||
|
@ -211,13 +212,13 @@ Feature: Test basic usage of glossary in app
|
|||
# Rate entries as teacher2
|
||||
Given I entered the glossary activity "Test glossary" on course "Course 1" as "teacher2" in the app
|
||||
And I press "Cucumber" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "None" in the app
|
||||
And I press "0" in the app
|
||||
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
|
||||
And I should find "Average of ratings: 1" in the app
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
Then I should find "This Glossary has offline data to be synchronised." in the app
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ Feature: Test glossary navigation
|
|||
When I press the back button in the app
|
||||
And I press "Clear search" in the app
|
||||
And I press "Add a new entry" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the following fields to these values in the app:
|
||||
| Concept | Tomato |
|
||||
| Definition | Tomato is a fruit |
|
||||
|
@ -274,7 +274,7 @@ Feature: Test glossary navigation
|
|||
# Offline
|
||||
When I press "Clear search" in the app
|
||||
And I press "Add a new entry" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the following fields to these values in the app:
|
||||
| Concept | Tomato |
|
||||
| Definition | Tomato is a fruit |
|
||||
|
|
|
@ -233,12 +233,12 @@ Feature: Test basic usage of survey activity in app
|
|||
| activity | name | intro | template | course | idnumber | groupmode |
|
||||
| survey | Test survey critical incidents | Test survey1 | 5 | C1 | survey1 | 0 |
|
||||
Given I entered the survey activity "Test survey critical incidents" on course "Course 1" as "student1" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Submit" in the app
|
||||
And I press "OK" in the app
|
||||
Then I should see "This Survey has offline data to be synchronised."
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I press the back button in the app
|
||||
And I press "Test survey critical incidents" in the app
|
||||
And I press "Information" in the app
|
||||
|
@ -255,7 +255,7 @@ Feature: Test basic usage of survey activity in app
|
|||
And I press "Course downloads" in the app
|
||||
And I press "Download" within "Test survey critical incidents" "ion-item" in the app
|
||||
And I press the back button in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Test survey name" in the app
|
||||
Then I should see "There was a problem connecting to the site. Please check your connection and try again."
|
||||
|
||||
|
@ -266,7 +266,7 @@ Feature: Test basic usage of survey activity in app
|
|||
And I press "OK" in the app
|
||||
Then I should see "This Survey has offline data to be synchronised."
|
||||
|
||||
When I switch offline mode to "false"
|
||||
When I switch network connection to wifi
|
||||
And I run cron tasks in the app
|
||||
Then I should not see "This Survey has offline data to be synchronised."
|
||||
And I should see "You have completed this survey."
|
||||
|
|
|
@ -23,7 +23,7 @@ import { CoreAjaxWSError } from './ajaxwserror';
|
|||
import { CoreCaptureError } from './captureerror';
|
||||
import { CoreNetworkError } from './network-error';
|
||||
import { CoreSiteError } from './siteerror';
|
||||
import { CoreErrorWithTitle } from './errorwithtitle';
|
||||
import { CoreErrorWithOptions } from './errorwithtitle';
|
||||
import { CoreHttpError } from './httperror';
|
||||
|
||||
export const CORE_ERRORS_CLASSES: Type<unknown>[] = [
|
||||
|
@ -36,6 +36,6 @@ export const CORE_ERRORS_CLASSES: Type<unknown>[] = [
|
|||
CoreSilentError,
|
||||
CoreSiteError,
|
||||
CoreWSError,
|
||||
CoreErrorWithTitle,
|
||||
CoreErrorWithOptions,
|
||||
CoreHttpError,
|
||||
];
|
||||
|
|
|
@ -12,20 +12,24 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { AlertButton } from '@ionic/angular';
|
||||
import { CoreError } from './error';
|
||||
|
||||
/**
|
||||
* Error with an explicit title describing the problem (instead of just "Error" or a generic message).
|
||||
* This title should be used to communicate the problem with users, and if it's undefined it should be omitted.
|
||||
* The error also may contain customizable action buttons.
|
||||
*/
|
||||
export class CoreErrorWithTitle extends CoreError {
|
||||
export class CoreErrorWithOptions extends CoreError {
|
||||
|
||||
title?: string;
|
||||
buttons?: AlertButton[];
|
||||
|
||||
constructor(message?: string, title?: string) {
|
||||
constructor(message?: string, title?: string, buttons?: AlertButton[]) {
|
||||
super(message);
|
||||
|
||||
this.title = title;
|
||||
this.buttons = buttons;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
</ion-button>
|
||||
<ion-slides [options]="slidesOpts" [dir]="direction" role="tablist" [attr.aria-label]="description">
|
||||
<ng-container *ngFor="let tab of tabs">
|
||||
<ion-slide role="presentation" [id]="tab.id! + '-tab'" tabindex="-1" [class.selected]="selected == tab.id">
|
||||
<ion-slide role="presentation" [id]="tab.id! + '-tab'" tabindex="-1" [class.selected]="selected == tab.id"
|
||||
class="{{tab.class}}">
|
||||
<ion-tab-button (ionTabButtonClick)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown($event)"
|
||||
(keyup)="tabAction.keyUp(tab.id, $event)" [tab]="tab.page" [layout]="layout" class="{{tab.class}}" role="tab"
|
||||
(keyup)="tabAction.keyUp(tab.id, $event)" [tab]="tab.page" [layout]="layout" role="tab"
|
||||
[attr.aria-controls]="tab.id" [attr.aria-selected]="selected == tab.id"
|
||||
[tabindex]="selected == tab.id ? 0 : -1">
|
||||
<ion-icon *ngIf="tab.icon" [name]="tab.icon" aria-hidden="true"></ion-icon>
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
</ion-button>
|
||||
<ion-slides [options]="slidesOpts" [dir]="direction" role="tablist" [attr.aria-label]="description">
|
||||
<ng-container *ngFor="let tab of tabs">
|
||||
<ion-slide *ngIf="tab.enabled" role="presentation" [id]="tab.id! + '-tab'" [class.selected]="selected == tab.id">
|
||||
<ion-slide *ngIf="tab.enabled" role="presentation" [id]="tab.id! + '-tab'" [class.selected]="selected == tab.id"
|
||||
class="{{tab.class}}">
|
||||
<ion-tab-button (click)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown($event)"
|
||||
(keyup)="tabAction.keyUp(tab.id, $event)" class="{{tab.class}}" [layout]="layout" role="tab"
|
||||
[attr.aria-controls]="tab.id" [attr.aria-selected]="selected == tab.id" [tabindex]="selected == tab.id ? 0 : -1">
|
||||
(keyup)="tabAction.keyUp(tab.id, $event)" [layout]="layout" role="tab" [attr.aria-controls]="tab.id"
|
||||
[attr.aria-selected]="selected == tab.id" [tabindex]="selected == tab.id ? 0 : -1">
|
||||
<ion-icon *ngIf="tab.icon" [name]="tab.icon" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
{{ tab.title | translate}}
|
||||
|
|
|
@ -36,8 +36,9 @@ Feature: Test basic usage of comments in app
|
|||
| Field description | Test field description |
|
||||
And I press "Save"
|
||||
And I close the browser tab opened by the app
|
||||
When I entered the course "Course 1" as "teacher1" in the app
|
||||
And I press "Data" in the app
|
||||
And I close the popup in the app
|
||||
|
||||
When I pull to refresh in the app
|
||||
And I press "Add entries" in the app
|
||||
And I set the field "Test field name" to "Test" in the app
|
||||
And I press "Save" in the app
|
||||
|
@ -75,7 +76,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
Scenario: Add comments offline & Delete comments offline & Sync comments (database)
|
||||
Given I entered the data activity "Data" on course "Course 1" as "teacher1" in the app
|
||||
When I press "Information" in the app
|
||||
And I press "Information" in the app
|
||||
And I press "Open in browser" in the app
|
||||
And I switch to the browser tab opened by the app
|
||||
And I log in as "teacher1"
|
||||
|
@ -84,14 +85,15 @@ Feature: Test basic usage of comments in app
|
|||
| Field description | Test field description |
|
||||
And I press "Save"
|
||||
And I close the browser tab opened by the app
|
||||
And I close the popup in the app
|
||||
|
||||
Given I entered the data activity "Data" on course "Course 1" as "teacher1" in the app
|
||||
Then I press "Add entries" in the app
|
||||
When I pull to refresh in the app
|
||||
And I press "Add entries" in the app
|
||||
And I set the field "Test field name" to "Test" in the app
|
||||
And I press "Save" in the app
|
||||
And I press "More" in the app
|
||||
And I press "Comments (0)" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the field "Add a comment..." to "comment test" in the app
|
||||
And I press "Send" in the app
|
||||
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
|
||||
|
@ -100,7 +102,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (0)" in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Display options" in the app
|
||||
And I press "Synchronise now" in the app
|
||||
And I close the popup in the app
|
||||
|
@ -109,7 +111,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (1)" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Toggle delete buttons" in the app
|
||||
And I press "Delete" in the app
|
||||
And I press "Delete" near "Cancel" in the app
|
||||
|
@ -120,7 +122,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (1)" in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Display options" in the app
|
||||
And I press "Synchronise now" in the app
|
||||
And I close the popup in the app
|
||||
|
@ -179,7 +181,7 @@ Feature: Test basic usage of comments in app
|
|||
And I press "Save" in the app
|
||||
And I press "potato" in the app
|
||||
And I press "Comments (0)" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the field "Add a comment..." to "comment test" in the app
|
||||
And I press "Send" in the app
|
||||
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
|
||||
|
@ -188,7 +190,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (0)" in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Display options" in the app
|
||||
And I press "Synchronise now" in the app
|
||||
And I close the popup in the app
|
||||
|
@ -197,7 +199,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (1)" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Toggle delete buttons" in the app
|
||||
And I press "Delete" in the app
|
||||
And I press "Delete" near "Cancel" in the app
|
||||
|
@ -208,7 +210,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (1)" in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Display options" in the app
|
||||
And I press "Synchronise now" in the app
|
||||
And I close the popup in the app
|
||||
|
@ -262,7 +264,7 @@ Feature: Test basic usage of comments in app
|
|||
And I should find "Blog body" in the app
|
||||
|
||||
When I press "Comments (0)" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I set the field "Add a comment..." to "comment test" in the app
|
||||
And I press "Send" in the app
|
||||
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
|
||||
|
@ -271,7 +273,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (0)" in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Display options" in the app
|
||||
And I press "Synchronise now" in the app
|
||||
And I close the popup in the app
|
||||
|
@ -280,7 +282,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (1)" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I switch network connection to offline
|
||||
And I press "Toggle delete buttons" in the app
|
||||
And I press "Delete" in the app
|
||||
And I press "Delete" near "Cancel" in the app
|
||||
|
@ -291,7 +293,7 @@ Feature: Test basic usage of comments in app
|
|||
|
||||
When I press the back button in the app
|
||||
And I press "Comments (1)" in the app
|
||||
And I switch offline mode to "false"
|
||||
And I switch network connection to wifi
|
||||
And I press "Display options" in the app
|
||||
And I press "Synchronise now" in the app
|
||||
And I close the popup in the app
|
||||
|
|
|
@ -177,7 +177,9 @@ export class CoreCourseProvider {
|
|||
CorePlatform.resume.subscribe(() => {
|
||||
// Run the handler the app is open to keep user in online status.
|
||||
setTimeout(() => {
|
||||
CoreCronDelegate.forceCronHandlerExecution(CoreCourseLogCronHandler.name);
|
||||
CoreUtils.ignoreErrors(
|
||||
CoreCronDelegate.forceCronHandlerExecution(CoreCourseLogCronHandler.name),
|
||||
);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
|
|
|
@ -411,6 +411,8 @@ Feature: Test basic usage of one course in app
|
|||
And I select "Enrolment methods" from the "jump" singleselect
|
||||
And I click on "Enable" "icon" in the "Self enrolment (Student)" "table_row"
|
||||
And I close the browser tab opened by the app
|
||||
And I close the popup in the app
|
||||
|
||||
Given I entered the app as "student2"
|
||||
When I press "Site home" in the app
|
||||
And I press "Available courses" in the app
|
||||
|
|
|
@ -148,9 +148,12 @@ Feature: Test basic usage of courses in app
|
|||
|
||||
# Grade assignment as teacher
|
||||
Given I entered the app as "teacher1"
|
||||
When I press "Grade" in the app
|
||||
When I pull to refresh in the app
|
||||
And I press "Grade" in the app
|
||||
Then the header should be "assignment" in the app
|
||||
And I should find "Test assignment description" in the app
|
||||
|
||||
When I pull to refresh in the app
|
||||
Then I should find "Test assignment description" in the app
|
||||
And I should find "Time remaining" in the app
|
||||
|
||||
When I press "Needs grading" in the app
|
||||
|
|
|
@ -26,8 +26,7 @@ Feature: Test basic usage of login in app
|
|||
And I should find "Connect to Moodle" in the app
|
||||
|
||||
Scenario: Add a new account in the app & Site name in displayed when adding a new account
|
||||
When I enter the app
|
||||
And I press the back button in the app
|
||||
When I launch the app
|
||||
And I set the field "Your site" to "$WWWROOT" in the app
|
||||
And I press "Connect to your site" in the app
|
||||
Then I should find "Acceptance test site" in the app
|
||||
|
@ -42,9 +41,7 @@ Feature: Test basic usage of login in app
|
|||
Scenario: Add a non existing account
|
||||
When I enter the app
|
||||
And I log in as "student1"
|
||||
And I press the user menu button in the app
|
||||
And I press "Log out" in the app
|
||||
And I wait the app to restart
|
||||
When I log out in the app
|
||||
And I press "Add" in the app
|
||||
And I set the field "Your site" to "Wrong Site Address" in the app
|
||||
And I press enter in the app
|
||||
|
@ -63,11 +60,16 @@ Feature: Test basic usage of login in app
|
|||
Then I should find "Cannot connect" in the app
|
||||
And I should find "Wrong Site Address" in the app
|
||||
|
||||
Scenario: Log out from the app
|
||||
Given I entered the app as "student1"
|
||||
And I press the user menu button in the app
|
||||
When I press "Log out" in the app
|
||||
And I wait the app to restart
|
||||
Then the header should be "Accounts" in the app
|
||||
|
||||
Scenario: Delete an account
|
||||
Given I entered the app as "student1"
|
||||
When I press the user menu button in the app
|
||||
And I press "Log out" in the app
|
||||
And I wait the app to restart
|
||||
When I log out in the app
|
||||
Then I should find "Acceptance test site" in the app
|
||||
And I press "Edit accounts list" in the app
|
||||
And I press "Remove account" near "Acceptance test site" in the app
|
||||
|
@ -75,14 +77,14 @@ Feature: Test basic usage of login in app
|
|||
Then I should find "Connect to Moodle" in the app
|
||||
But I should not find "Acceptance test site" in the app
|
||||
|
||||
Scenario: Require minium version of the app for a site
|
||||
|
||||
Scenario: Require minium (previous) version of the app for a site
|
||||
# Log in with a previous required version
|
||||
Given the following config values are set as admin:
|
||||
| minimumversion | 3.8.1 | tool_mobile |
|
||||
When I enter the app
|
||||
Then I should not find "App update required" in the app
|
||||
|
||||
Scenario: Require minium (future) version of the app for a site
|
||||
# Log in with a future required version
|
||||
Given the following config values are set as admin:
|
||||
| minimumversion | 11.0.0 | tool_mobile |
|
||||
|
|
|
@ -71,7 +71,14 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
|
|||
|
||||
// Load the handlers.
|
||||
if (this.siteInfo) {
|
||||
this.user = await CoreUser.getProfile(this.siteInfo.userid);
|
||||
try {
|
||||
this.user = await CoreUser.getProfile(this.siteInfo.userid);
|
||||
} catch {
|
||||
this.user = {
|
||||
id: this.siteInfo.userid,
|
||||
fullname: this.siteInfo.fullname,
|
||||
};
|
||||
}
|
||||
|
||||
this.subscription = CoreUserDelegate.getProfileHandlersFor(this.user, CoreUserDelegateContext.USER_MENU)
|
||||
.subscribe((handlers) => {
|
||||
|
|
|
@ -746,11 +746,7 @@ export class CorePushNotificationsProvider {
|
|||
CoreEvents.trigger(CoreEvents.DEVICE_REGISTERED_IN_MOODLE, {}, site.getId());
|
||||
|
||||
// Insert the device in the local DB.
|
||||
try {
|
||||
await this.registeredDevicesTables[site.getId()].insert(data);
|
||||
} catch (err) {
|
||||
// Ignore errors.
|
||||
}
|
||||
await CoreUtils.ignoreErrors(this.registeredDevicesTables[site.getId()].insert(data));
|
||||
}
|
||||
} finally {
|
||||
// Remove pending unregisters for this site.
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
"appsettings": "App settings",
|
||||
"appversion": "App version",
|
||||
"cannotsyncloggedout": "This site cannot be synchronised because you've logged out. Please try again when you're logged in the site again.",
|
||||
"cannotsyncoffline": "Cannot synchronise offline.",
|
||||
"cannotsyncwithoutwifi": "Cannot synchronise because the current settings only allow to synchronise when connected to Wi-Fi. Please connect to a Wi-Fi network.",
|
||||
"cannotsyncoffline": "Site synchronisation failed because your device is not connected to the internet.",
|
||||
"cannotsyncwithoutwifi": "Your device is not connected to Wi-Fi. Connect to a Wi-Fi network or turn off Data Saver in the app settings.",
|
||||
"changelanguage": "Change to {{$a}}",
|
||||
"changelanguagealert": "Changing the language will restart the app.",
|
||||
"colorscheme-dark": "Dark",
|
||||
|
@ -14,6 +14,8 @@
|
|||
"colorscheme-system": "System default",
|
||||
"colorscheme": "Color Scheme",
|
||||
"compilationinfo": "Compilation info",
|
||||
"connectwifitosync": "Connect to a Wi-Fi network or turn off Data saver to synchronise sites.",
|
||||
"connecttosync": "Your device is offline. Connect to the internet to synchronise sites.",
|
||||
"copyinfo": "Copy device info on the clipboard",
|
||||
"cordovadevicemodel": "Cordova device model",
|
||||
"cordovadeviceosversion": "Cordova device OS version",
|
||||
|
@ -35,9 +37,7 @@
|
|||
"enablefirebaseanalyticsdescription": "If enabled, the app will collect anonymous data usage.",
|
||||
"enablerichtexteditor": "Enable text editor",
|
||||
"enablerichtexteditordescription": "If enabled, a text editor will be available when entering content.",
|
||||
"enablesyncwifi": "Allow sync only when on Wi-Fi",
|
||||
"entriesincache": "{{$a}} entries in cache",
|
||||
"errorsyncsite": "Error synchronising site data. Please check your Internet connection and try again.",
|
||||
"estimatedfreespace": "Estimated free space",
|
||||
"filesystemroot": "File system root",
|
||||
"fontsize": "Text size",
|
||||
|
@ -54,6 +54,7 @@
|
|||
"locationhref": "Web view URL",
|
||||
"loggedin": "Online",
|
||||
"loggedoff": "Offline",
|
||||
"logintosync": "Log in to synchronise",
|
||||
"navigatorlanguage": "Navigator language",
|
||||
"navigatoruseragent": "Navigator userAgent",
|
||||
"networkstatus": "Internet connection status",
|
||||
|
@ -68,7 +69,9 @@
|
|||
"showdownloadoptions": "Show download options",
|
||||
"siteinfo": "Site info",
|
||||
"sites": "Sites",
|
||||
"sitesyncfailed": "Site synchronisation failed",
|
||||
"spaceusage": "Space usage",
|
||||
"syncdatasaver": "Data saver: Synchronise only when on Wi-Fi",
|
||||
"synchronization": "Synchronisation",
|
||||
"synchronizenow": "Synchronise now",
|
||||
"synchronizenowhelp": "Synchronising a site will send pending changes and all offline activity stored in the device and will synchronise some data like messages and notifications.",
|
||||
|
|
|
@ -37,11 +37,20 @@
|
|||
</p>
|
||||
</ion-label>
|
||||
<core-button-with-spinner [loading]="isSynchronizing()" slot="end">
|
||||
<ion-button fill="clear" (click)="synchronize()" [attr.aria-label]="'core.settings.synchronizenow' | translate">
|
||||
<ion-button fill="clear" (click)="synchronize()" [attr.aria-label]="'core.settings.synchronizenow' | translate"
|
||||
[disabled]="!isOnline || (dataSaver && limitedConnection)">
|
||||
<ion-icon name="fas-sync-alt" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</core-button-with-spinner>
|
||||
</ion-item>
|
||||
<ion-item class="core-warning-item ion-text-wrap" *ngIf="!isOnline || (dataSaver && limitedConnection)">
|
||||
<ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<ng-container *ngIf="isOnline && dataSaver && limitedConnection">
|
||||
{{ 'core.settings.connectwifitosync' | translate }}</ng-container>
|
||||
<ng-container *ngIf="!isOnline">{{ 'core.settings.connecttosync' | translate }}</ng-container>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
</core-loading>
|
||||
</core-split-view>
|
||||
|
|
|
@ -25,6 +25,11 @@ import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/
|
|||
import { CoreSettingsHandlersSource } from '@features/settings/classes/settings-handlers-source';
|
||||
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreNetwork } from '@services/network';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { NgZone } from '@singletons';
|
||||
import { CoreConstants } from '@/core/constants';
|
||||
import { CoreConfig } from '@services/config';
|
||||
|
||||
/**
|
||||
* Page that displays the list of site settings pages.
|
||||
|
@ -39,8 +44,13 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
|
|||
|
||||
handlers: CoreListItemsManager<CoreSettingsHandlerToDisplay>;
|
||||
|
||||
dataSaver = false;
|
||||
limitedConnection = false;
|
||||
isOnline = true;
|
||||
|
||||
protected siteId: string;
|
||||
protected sitesObserver: CoreEventObserver;
|
||||
protected networkObserver: Subscription;
|
||||
protected isDestroyed = false;
|
||||
|
||||
constructor() {
|
||||
|
@ -53,12 +63,25 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
|
|||
this.sitesObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
|
||||
this.refreshData();
|
||||
}, this.siteId);
|
||||
|
||||
this.isOnline = CoreNetwork.isOnline();
|
||||
this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
|
||||
|
||||
this.networkObserver = CoreNetwork.onChange().subscribe(() => {
|
||||
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
||||
NgZone.run(() => {
|
||||
this.isOnline = CoreNetwork.isOnline();
|
||||
this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngAfterViewInit(): Promise<void> {
|
||||
this.dataSaver = await CoreConfig.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, true);
|
||||
|
||||
const pageToOpen = CoreNavigator.getRouteParam('page');
|
||||
|
||||
try {
|
||||
|
@ -94,7 +117,7 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
|
|||
if (this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
CoreDomUtils.showErrorModalDefault(error, 'core.settings.errorsyncsite', true);
|
||||
CoreDomUtils.showErrorModalDefault(error, 'core.settings.sitesyncfailed', true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -121,11 +144,12 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.sitesObserver?.off();
|
||||
this.sitesObserver.off();
|
||||
this.networkObserver.unsubscribe();
|
||||
this.handlers.destroy();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,37 +17,97 @@
|
|||
</ion-header>
|
||||
<ion-content class="limited-width">
|
||||
<core-loading [hideUntil]="sitesLoaded">
|
||||
<ion-list>
|
||||
<ion-list class="core-sitelist limited-width">
|
||||
<ion-item-divider>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.settings.syncsettings' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item-divider>
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>{{ 'core.settings.enablesyncwifi' | translate }}</ion-label>
|
||||
<ion-toggle slot="end" [(ngModel)]="syncOnlyOnWifi" (ngModelChange)="syncOnlyOnWifiChanged()">
|
||||
</ion-toggle>
|
||||
</ion-item>
|
||||
<ion-item-divider>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.settings.sites' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item-divider>
|
||||
<ion-item *ngFor="let site of sites" [class.item-current]="site.id == currentSiteId" class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p class="item-heading">
|
||||
<core-format-text [text]="site.siteName" clean="true" [siteId]="site.id"></core-format-text>
|
||||
{{ 'core.settings.syncdatasaver' | translate }}
|
||||
</p>
|
||||
<p>{{ site.fullName }}</p>
|
||||
<p>{{ site.siteUrlWithoutProtocol }}</p>
|
||||
</ion-label>
|
||||
<core-button-with-spinner [loading]="isSynchronizing(site.id)" slot="end">
|
||||
<ion-button fill="clear" (click)="synchronize(site.id)" [title]="site.siteName"
|
||||
[attr.aria-label]="'core.settings.synchronizenow' | translate">
|
||||
<ion-icon name="fas-sync-alt" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</core-button-with-spinner>
|
||||
<ion-toggle slot="end" [(ngModel)]="dataSaver" (ngModelChange)="syncOnlyOnWifiChanged()">
|
||||
</ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-card class="core-warning-card" *ngIf="!isOnline || (dataSaver && limitedConnection)">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<ng-container *ngIf="isOnline && dataSaver && limitedConnection">
|
||||
{{ 'core.settings.connectwifitosync' | translate }}</ng-container>
|
||||
<ng-container *ngIf="!isOnline">{{ 'core.settings.connecttosync' | translate }}</ng-container>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<ng-container *ngIf="isOnline && (!dataSaver || !limitedConnection)">
|
||||
<ion-item-divider>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.accounts' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item-divider>
|
||||
|
||||
<ion-card *ngIf="accountsList.currentSite">
|
||||
<ion-item-divider sticky="true" class="core-sitelist-sitename">
|
||||
<ion-label>
|
||||
<p class="item-heading">
|
||||
<core-format-text [text]="accountsList.currentSite.siteName" clean="true"
|
||||
[siteId]="accountsList.currentSite.id"></core-format-text>
|
||||
</p>
|
||||
<p><a [href]="accountsList.currentSite.siteUrl" core-link autoLogin="yes">{{
|
||||
accountsList.currentSite.siteUrlWithoutProtocol }}</a>
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item-divider>
|
||||
|
||||
<ion-item class="item-current">
|
||||
<ng-container *ngTemplateOutlet="siteSync; context: {site: accountsList.currentSite}"></ng-container>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngFor="let site of accountsList.sameSite">
|
||||
<ng-container *ngTemplateOutlet="siteSync; context: {site: site}"></ng-container>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<ion-card *ngFor="let sites of accountsList.otherSites">
|
||||
<ion-item-divider sticky="true" *ngIf="sites[0]" class="core-sitelist-sitename">
|
||||
<ion-label>
|
||||
<p class="item-heading">
|
||||
<core-format-text [text]="sites[0].siteName" clean="true" [siteId]="sites[0].id"></core-format-text>
|
||||
</p>
|
||||
<p><a [href]="sites[0].siteUrl" core-link autoLogin="no">{{ sites[0].siteUrlWithoutProtocol }}</a></p>
|
||||
</ion-label>
|
||||
</ion-item-divider>
|
||||
|
||||
<ion-item *ngFor="let site of sites">
|
||||
<ng-container *ngTemplateOutlet="siteSync; context: {site: site}"></ng-container>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
||||
<!-- Template to render a site to sync. -->
|
||||
<ng-template #siteSync let-site="site">
|
||||
<ion-avatar slot="start">
|
||||
<img [src]="site.avatar" core-external-content [siteId]="site.id" alt="{{ 'core.pictureof' | translate:{$a: site.fullName} }}"
|
||||
onError="this.src='assets/img/user-avatar.png'">
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<p class="item-heading">{{site.fullName}}</p>
|
||||
<p class="text-danger" *ngIf="site.loggedOut">{{ 'core.settings.logintosync' | translate }}</p>
|
||||
</ion-label>
|
||||
<core-button-with-spinner [loading]="isSynchronizing(site.id)" slot="end" *ngIf="!site.loggedOut">
|
||||
<ion-button fill="clear" (click)="synchronize(site.id)" [attr.aria-label]="'core.settings.synchronizenow' | translate">
|
||||
<ion-icon name="fas-sync-alt" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</core-button-with-spinner>
|
||||
<ion-button fill="clear" (click)="login(site.id)" [attr.aria-label]="'core.login.login' | translate" *ngIf="site.loggedOut" slot="end">
|
||||
<ion-icon name="fas-sign-in-alt" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ng-template>
|
||||
|
|
|
@ -16,11 +16,15 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
|
|||
|
||||
import { CoreConstants } from '@/core/constants';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreSites, CoreSiteBasicInfo } from '@services/sites';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreConfig } from '@services/config';
|
||||
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
|
||||
import { Translate } from '@singletons';
|
||||
import { NgZone, Translate } from '@singletons';
|
||||
import { CoreAccountsList, CoreLoginHelper } from '@features/login/services/login-helper';
|
||||
import { CoreNetwork } from '@services/network';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
|
||||
/**
|
||||
* Page that displays the synchronization settings.
|
||||
|
@ -28,61 +32,101 @@ import { Translate } from '@singletons';
|
|||
@Component({
|
||||
selector: 'page-core-app-settings-synchronization',
|
||||
templateUrl: 'synchronization.html',
|
||||
styleUrls: ['../../../login/sitelist.scss'],
|
||||
})
|
||||
export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
|
||||
|
||||
sites: CoreSiteBasicInfo[] = [];
|
||||
accountsList: CoreAccountsList = {
|
||||
sameSite: [],
|
||||
otherSites: [],
|
||||
count: 0,
|
||||
};
|
||||
|
||||
sitesLoaded = false;
|
||||
currentSiteId = '';
|
||||
syncOnlyOnWifi = false;
|
||||
dataSaver = false;
|
||||
limitedConnection = false;
|
||||
isOnline = true;
|
||||
|
||||
protected isDestroyed = false;
|
||||
protected sitesObserver: CoreEventObserver;
|
||||
protected networkObserver: Subscription;
|
||||
|
||||
constructor() {
|
||||
|
||||
this.currentSiteId = CoreSites.getCurrentSiteId();
|
||||
|
||||
this.sitesObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, async (data) => {
|
||||
const site = await CoreSites.getSite(data.siteId);
|
||||
const siteId = data.siteId;
|
||||
|
||||
const siteEntry = this.sites.find((siteEntry) => siteEntry.id == site.id);
|
||||
if (siteEntry) {
|
||||
const siteInfo = site.getInfo();
|
||||
let siteEntry = siteId === this.accountsList.currentSite?.id
|
||||
? this.accountsList.currentSite
|
||||
: undefined;
|
||||
|
||||
siteEntry.siteName = site.getSiteName();
|
||||
if (!siteEntry) {
|
||||
siteEntry = this.accountsList.sameSite.find((siteEntry) => siteEntry.id === siteId);
|
||||
}
|
||||
|
||||
if (siteInfo) {
|
||||
siteEntry.siteUrl = siteInfo.siteurl;
|
||||
siteEntry.fullName = siteInfo.fullname;
|
||||
}
|
||||
if (!siteEntry) {
|
||||
this.accountsList.otherSites.some((sites) => {
|
||||
siteEntry = sites.find((siteEntry) => siteEntry.id === siteId);
|
||||
|
||||
return siteEntry;
|
||||
});
|
||||
}
|
||||
|
||||
if (!siteEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
const siteInfo = site.getInfo();
|
||||
|
||||
siteEntry.siteName = site.getSiteName();
|
||||
|
||||
if (siteInfo) {
|
||||
siteEntry.siteUrl = siteInfo.siteurl;
|
||||
siteEntry.fullName = siteInfo.fullname;
|
||||
}
|
||||
});
|
||||
|
||||
this.isOnline = CoreNetwork.isOnline();
|
||||
this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
|
||||
|
||||
this.networkObserver = CoreNetwork.onChange().subscribe(() => {
|
||||
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
||||
NgZone.run(() => {
|
||||
this.isOnline = CoreNetwork.isOnline();
|
||||
this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
const currentSiteId = CoreSites.getCurrentSiteId();
|
||||
|
||||
try {
|
||||
this.sites = await CoreSites.getSortedSites();
|
||||
this.accountsList = await CoreLoginHelper.getAccountsList(currentSiteId);
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
this.sitesLoaded = true;
|
||||
|
||||
this.syncOnlyOnWifi = await CoreConfig.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, true);
|
||||
this.dataSaver = await CoreConfig.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when sync only on wifi setting is enabled or disabled.
|
||||
*/
|
||||
syncOnlyOnWifiChanged(): void {
|
||||
CoreConfig.set(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, this.syncOnlyOnWifi ? 1 : 0);
|
||||
CoreConfig.set(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, this.dataSaver ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncrhonizes a site.
|
||||
* Synchronizes a site.
|
||||
*
|
||||
* @param siteId Site ID.
|
||||
*/
|
||||
|
@ -95,10 +139,20 @@ export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
CoreDomUtils.showErrorModalDefault(error, 'core.settings.errorsyncsite', true);
|
||||
CoreDomUtils.showErrorModalDefault(error, 'core.settings.sitesyncfailed', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes site.
|
||||
*
|
||||
* @param siteId Site ID.
|
||||
*/
|
||||
async login(siteId: string): Promise<void> {
|
||||
// This navigation will logout and navigate to the site home.
|
||||
await CoreNavigator.navigateToSiteHome({ preferCurrentTab: false , siteId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if site is beeing synchronized.
|
||||
*
|
||||
|
@ -120,11 +174,12 @@ export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.sitesObserver?.off();
|
||||
this.sitesObserver.off();
|
||||
this.networkObserver.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import { CoreCourse } from '@features/course/services/course';
|
|||
import { makeSingleton, Translate } from '@singletons';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
|
||||
/**
|
||||
* Object with space usage and cache entries that can be erased.
|
||||
|
@ -260,6 +261,7 @@ export class CoreSettingsHelperProvider {
|
|||
const site = await CoreSites.getSite(siteId);
|
||||
const hasSyncHandlers = CoreCronDelegate.hasManualSyncHandlers();
|
||||
|
||||
// All these errors should not happen on manual sync because are prevented on UI.
|
||||
if (site.isLoggedOut()) {
|
||||
// Cannot sync logged out sites.
|
||||
throw new CoreError(Translate.instant('core.settings.cannotsyncloggedout'));
|
||||
|
@ -286,6 +288,8 @@ export class CoreSettingsHelperProvider {
|
|||
|
||||
try {
|
||||
await syncPromise;
|
||||
} catch (error) {
|
||||
throw CoreTextUtils.addTitleToError(error, Translate.instant('core.settings.sitesyncfailed'));
|
||||
} finally {
|
||||
delete this.syncPromises[siteId];
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@app @javascript
|
||||
@app @javascript @core_settings
|
||||
Feature: It navigates properly within settings.
|
||||
|
||||
Background:
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
@app @javascript @core_settings
|
||||
Feature: It synchronise sites properly
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname |
|
||||
| student1 | david | student |
|
||||
| student2 | pau | student2 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | option | allowmultiple | allowupdate | showresults |
|
||||
| choice | Sync choice | Intro | C1 | choice1 | Option 1, Option 2, Option 3 | 0 | 0 | 1 |
|
||||
|
||||
Scenario: Sync the current site
|
||||
# Add something offline
|
||||
Given I entered the choice activity "Sync choice" on course "Course 1" as "student1" in the app
|
||||
When I switch network connection to offline
|
||||
And I select "Option 1" in the app
|
||||
And I press "Save my choice" in the app
|
||||
And I press "OK" in the app
|
||||
Then I should find "This Choice has offline data to be synchronised." in the app
|
||||
|
||||
# Cannot sync in offline
|
||||
When I press the back button in the app
|
||||
And I press the back button in the app
|
||||
And I press the user menu button in the app
|
||||
And I press "Preferences" in the app
|
||||
Then I should find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
|
||||
When I switch network connection to wifi
|
||||
Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
|
||||
# Check synced
|
||||
When I press "Synchronise now" "button" in the app
|
||||
And I wait loading to finish in the app
|
||||
And I switch network connection to offline
|
||||
And I press the back button in the app
|
||||
And I entered the course "Course 1" in the app
|
||||
And I press "Sync choice" in the app
|
||||
Then I should not find "This Choice has offline data to be synchronised." in the app
|
||||
|
||||
# Check limited sync.
|
||||
When I switch network connection to cellular
|
||||
And I press the back button in the app
|
||||
And I press the back button in the app
|
||||
And I press the user menu button in the app
|
||||
And I press "Preferences" in the app
|
||||
|
||||
# Cannot sync in cellular
|
||||
Then I should find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
And I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
|
||||
Scenario: Sync sites messages with different network connections
|
||||
Given I entered the app as "student1"
|
||||
|
||||
# Wifi + data saver on.
|
||||
When I press the more menu button in the app
|
||||
And I press "App settings" in the app
|
||||
And I press "Synchronisation" in the app
|
||||
Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
And I should find "Accounts" in the app
|
||||
|
||||
# Limited + data saver on.
|
||||
When I switch network connection to cellular
|
||||
Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
And I should find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
And I should not find "Accounts" in the app
|
||||
|
||||
# Offline + data saver on.
|
||||
When I switch network connection to offline
|
||||
Then I should find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
And I should not find "Accounts" in the app
|
||||
|
||||
# Wifi + data saver off.
|
||||
When I press "Data saver: Synchronise only when on Wi-Fi" in the app
|
||||
And I switch network connection to wifi
|
||||
Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
And I should find "Accounts" in the app
|
||||
|
||||
# Limited + data saver off.
|
||||
When I switch network connection to cellular
|
||||
Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
And I should find "Accounts" in the app
|
||||
|
||||
# Offline + data saver off.
|
||||
When I switch network connection to offline
|
||||
Then I should find "Your device is offline. Connect to the internet to synchronise sites." in the app
|
||||
And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
|
||||
And I should not find "Accounts" in the app
|
||||
|
||||
Scenario: Sync logged in and logged out sites
|
||||
Given I entered the app as "student1"
|
||||
And I log out in the app
|
||||
And I entered the choice activity "Sync choice" on course "Course 1" as "student2" in the app
|
||||
|
||||
# Add something offline
|
||||
When I switch network connection to offline
|
||||
And I select "Option 1" in the app
|
||||
And I press "Save my choice" in the app
|
||||
And I press "OK" in the app
|
||||
Then I should find "This Choice has offline data to be synchronised." in the app
|
||||
|
||||
When I press the back button in the app
|
||||
And I press the back button in the app
|
||||
And I press the more menu button in the app
|
||||
And I press "App settings" in the app
|
||||
And I press "Synchronisation" in the app
|
||||
And I switch network connection to wifi
|
||||
Then I should find "Accounts" in the app
|
||||
|
||||
# Check synced
|
||||
When I press "Synchronise now" "button" in the app
|
||||
And I wait loading to finish in the app
|
||||
And I switch network connection to offline
|
||||
And I press the back button in the app
|
||||
And I entered the course "Course 1" in the app
|
||||
And I press "Sync choice" in the app
|
||||
Then I should not find "This Choice has offline data to be synchronised." in the app
|
||||
|
||||
# Test log in to sync
|
||||
When I press the back button in the app
|
||||
And I press the back button in the app
|
||||
And I press the more menu button in the app
|
||||
And I press "App settings" in the app
|
||||
And I press "Synchronisation" in the app
|
||||
And I switch network connection to wifi
|
||||
Then I should find "Accounts" in the app
|
||||
And I should find "Log in to synchronise" in the app
|
||||
|
||||
When I press "Log in" in the app
|
||||
Then I should find "Reconnect" in the app
|
||||
|
||||
When I set the field "Password" to "student1" in the app
|
||||
And I press "Log in" in the app
|
||||
And I press the more menu button in the app
|
||||
And I press "App settings" in the app
|
||||
And I press "Synchronisation" in the app
|
||||
Then I should not find "Log in to synchronise" in the app
|
|
@ -1,7 +1,7 @@
|
|||
:host {
|
||||
--popover-background: var(--ion-overlay-background-color, var(--ion-background-color, #fff));
|
||||
|
||||
z-index: 99;
|
||||
z-index: 105; // Main menu is 101.
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
|
|
|
@ -62,6 +62,7 @@ export class CoreUserToursUserTourComponent implements AfterViewInit, OnDestroy
|
|||
@Output() afterDismiss = new EventEmitter<void>();
|
||||
@HostBinding('class.is-active') active = false;
|
||||
@HostBinding('class.is-popover') popover = false;
|
||||
@HostBinding('class.backdrop') backdrop = true;
|
||||
@ViewChild('wrapper') wrapper?: ElementRef<HTMLElement>;
|
||||
|
||||
focusStyles?: string;
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
"downloading": "Downloading",
|
||||
"edit": "Edit",
|
||||
"emptysplit": "This page will appear blank if the left panel is empty or is loading.",
|
||||
"endonesteptour": "Got it",
|
||||
"error": "Error",
|
||||
"errorchangecompletion": "An error occurred while changing the completion status. Please try again.",
|
||||
"errordeletefile": "Error deleting the file. Please try again.",
|
||||
|
@ -111,7 +112,9 @@
|
|||
"erroropenfilenoextension": "Error opening file: the file doesn't have an extension.",
|
||||
"erroropenpopup": "This activity is trying to open a popup. This is not supported in the app.",
|
||||
"errorrenamefile": "Error renaming file. Please try again.",
|
||||
"errorsitesupport": "If the problem persists, contact site support.",
|
||||
"errorsomedatanotdownloaded": "If you downloaded this activity, please notice that some data isn't downloaded during the download process for performance and data usage reasons.",
|
||||
"errorsomethingwrong": "Something went wrong. Please try again.",
|
||||
"errorsync": "An error occurred while synchronising. Please try again.",
|
||||
"errorsyncblocked": "This {{$a}} cannot be synchronised right now because of an ongoing process. Please try again later. If the problem persists, try restarting the app.",
|
||||
"errorurlschemeinvalidscheme": "This URL is meant to be used in another app: {{$a}}.",
|
||||
|
@ -248,8 +251,8 @@
|
|||
"resources": "Resources",
|
||||
"restore": "Restore",
|
||||
"restricted": "Restricted",
|
||||
"retry": "Retry",
|
||||
"resume": "Resume",
|
||||
"retry": "Retry",
|
||||
"save": "Save",
|
||||
"savechanges": "Save changes",
|
||||
"scanqr": "Scan QR code",
|
||||
|
@ -328,11 +331,10 @@
|
|||
"user": "User",
|
||||
"userdeleted": "This user account has been deleted",
|
||||
"userdetails": "User details",
|
||||
"usernotfullysetup": "User not fully set-up",
|
||||
"usernologin": "Authentication has been revoked for this account",
|
||||
"usersuspended": "Registration suspended",
|
||||
"endonesteptour": "Got it",
|
||||
"usernotfullysetup": "User not fully set-up",
|
||||
"users": "Users",
|
||||
"usersuspended": "Registration suspended",
|
||||
"view": "View",
|
||||
"viewcode": "View code",
|
||||
"vieweditor": "View editor",
|
||||
|
@ -351,8 +353,8 @@
|
|||
"year": "year",
|
||||
"years": "years",
|
||||
"yes": "Yes",
|
||||
"youreoffline": "You are offline",
|
||||
"youreonline": "You are back online",
|
||||
"youreoffline": "Your device is offline",
|
||||
"youreonline": "Your device is back online",
|
||||
"zoomin": "Zoom In",
|
||||
"zoomout": "Zoom Out"
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import { CoreDatabaseTable } from '@classes/database/database-table';
|
|||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
import { CoreNetwork } from '@services/network';
|
||||
import { CoreNetwork, CoreNetworkConnection } from '@services/network';
|
||||
|
||||
/**
|
||||
* Factory to provide some global functionalities, like access to the global app database.
|
||||
|
@ -644,10 +644,10 @@ export class CoreAppProvider {
|
|||
* Set value of forceOffline flag. If true, the app will think the device is offline.
|
||||
*
|
||||
* @param value Value to set.
|
||||
* @deprecated since 4.1.0. Use CoreNetwork instead.
|
||||
* @deprecated since 4.1.0. Use CoreNetwork.setForceConnectionMode instead.
|
||||
*/
|
||||
setForceOffline(value: boolean): void {
|
||||
CoreNetwork.setForceOffline(value);
|
||||
CoreNetwork.setForceConnectionMode(value ? CoreNetworkConnection.NONE : CoreNetworkConnection.WIFI);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@ import { CoreUtils } from '@services/utils/utils';
|
|||
import { CoreConstants } from '@/core/constants';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { makeSingleton, Translate } from '@singletons';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { APP_SCHEMA, CRON_TABLE_NAME, CronDBEntry } from '@services/database/cron';
|
||||
import { asyncInstance } from '../utils/async-instance';
|
||||
|
@ -81,10 +81,11 @@ export class CoreCronDelegateService {
|
|||
protected async checkAndExecuteHandler(name: string, force?: boolean, siteId?: string): Promise<void> {
|
||||
if (!this.handlers[name] || !this.handlers[name].execute) {
|
||||
// Invalid handler.
|
||||
const message = `Cannot execute handler because is invalid: ${name}`;
|
||||
this.logger.debug(message);
|
||||
this.logger.debug(`Cannot execute cron job because is invalid: ${name}`);
|
||||
|
||||
throw new CoreError(message);
|
||||
throw new CoreError(
|
||||
Translate.instant('core.errorsomethingwrong') + '<br>' + Translate.instant('core.errorsitesupport'),
|
||||
);
|
||||
}
|
||||
|
||||
const usesNetwork = this.handlerUsesNetwork(name);
|
||||
|
@ -92,11 +93,10 @@ export class CoreCronDelegateService {
|
|||
|
||||
if (usesNetwork && !CoreNetwork.isOnline()) {
|
||||
// Offline, stop executing.
|
||||
const message = `Cannot execute handler because device is offline: ${name}`;
|
||||
this.logger.debug(message);
|
||||
this.logger.debug(`Cron job failed because your device is not connected to the internet: ${name}`);
|
||||
this.stopHandler(name);
|
||||
|
||||
throw new CoreError(message);
|
||||
throw new CoreError(Translate.instant('core.settings.cannotsyncoffline'));
|
||||
}
|
||||
|
||||
if (isSync) {
|
||||
|
@ -105,11 +105,10 @@ export class CoreCronDelegateService {
|
|||
|
||||
if (syncOnlyOnWifi && !CoreNetwork.isWifi()) {
|
||||
// Cannot execute in this network connection, retry soon.
|
||||
const message = `Cannot execute handler because device is using limited connection: ${name}`;
|
||||
this.logger.debug(message);
|
||||
this.logger.debug(`Cron job failed because your device has a limited internet connection: ${name}`);
|
||||
this.scheduleNextExecution(name, CoreCronDelegateService.MIN_INTERVAL);
|
||||
|
||||
throw new CoreError(message);
|
||||
throw new CoreError(Translate.instant('core.settings.cannotsyncwithoutwifi'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +117,7 @@ export class CoreCronDelegateService {
|
|||
try {
|
||||
await this.executeHandler(name, force, siteId);
|
||||
|
||||
this.logger.debug(`Execution of handler '${name}' was a success.`);
|
||||
this.logger.debug(`Cron job '${name}' was successfully executed.`);
|
||||
|
||||
await CoreUtils.ignoreErrors(this.setHandlerLastExecutionTime(name, Date.now()));
|
||||
|
||||
|
@ -127,11 +126,10 @@ export class CoreCronDelegateService {
|
|||
return;
|
||||
} catch (error) {
|
||||
// Handler call failed. Retry soon.
|
||||
const message = `Execution of handler '${name}' failed.`;
|
||||
this.logger.error(message, error);
|
||||
this.logger.error(`Cron job '${name}' failed.`, error);
|
||||
this.scheduleNextExecution(name, CoreCronDelegateService.MIN_INTERVAL);
|
||||
|
||||
throw new CoreError(message);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -18,6 +18,17 @@ import { Network } from '@ionic-native/network/ngx';
|
|||
import { makeSingleton } from '@singletons';
|
||||
import { Observable, Subject, merge } from 'rxjs';
|
||||
|
||||
export enum CoreNetworkConnection {
|
||||
UNKNOWN = 'unknown',
|
||||
ETHERNET = 'ethernet',
|
||||
WIFI = 'wifi',
|
||||
CELL_2G = '2g',
|
||||
CELL_3G = '3g',
|
||||
CELL_4G = '4g',
|
||||
CELL = 'cellular',
|
||||
NONE = 'none',
|
||||
};
|
||||
|
||||
/**
|
||||
* Service to manage network connections.
|
||||
*/
|
||||
|
@ -28,9 +39,21 @@ export class CoreNetworkService extends Network {
|
|||
|
||||
protected connectObservable = new Subject<'connected'>();
|
||||
protected disconnectObservable = new Subject<'disconnected'>();
|
||||
protected forceOffline = false;
|
||||
protected forceConnectionMode?: CoreNetworkConnection;
|
||||
protected online = false;
|
||||
|
||||
get connectionType(): CoreNetworkConnection {
|
||||
if (this.forceConnectionMode !== undefined) {
|
||||
return this.forceConnectionMode;
|
||||
}
|
||||
|
||||
if (CorePlatform.isMobile()) {
|
||||
return this.type as CoreNetworkConnection;
|
||||
}
|
||||
|
||||
return this.online ? CoreNetworkConnection.WIFI : CoreNetworkConnection.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the service.
|
||||
*/
|
||||
|
@ -38,20 +61,25 @@ export class CoreNetworkService extends Network {
|
|||
this.checkOnline();
|
||||
|
||||
if (CorePlatform.isMobile()) {
|
||||
this.onChange().subscribe(() => {
|
||||
// We cannot directly listen to onChange because it depends on
|
||||
// onConnect and onDisconnect that have been already overriden.
|
||||
super.onConnect().subscribe(() => {
|
||||
this.fireObservable();
|
||||
});
|
||||
super.onDisconnect().subscribe(() => {
|
||||
this.fireObservable();
|
||||
});
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(<any> window).Connection = {
|
||||
UNKNOWN: 'unknown', // eslint-disable-line @typescript-eslint/naming-convention
|
||||
ETHERNET: 'ethernet', // eslint-disable-line @typescript-eslint/naming-convention
|
||||
WIFI: 'wifi', // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CELL_2G: '2g', // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CELL_3G: '3g', // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CELL_4G: '4g', // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CELL: 'cellular', // eslint-disable-line @typescript-eslint/naming-convention
|
||||
NONE: 'none', // eslint-disable-line @typescript-eslint/naming-convention
|
||||
UNKNOWN: CoreNetworkConnection.UNKNOWN, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
ETHERNET: CoreNetworkConnection.ETHERNET, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
WIFI: CoreNetworkConnection.WIFI, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CELL_2G: CoreNetworkConnection.CELL_2G, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CELL_3G: CoreNetworkConnection.CELL_3G, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CELL_4G: CoreNetworkConnection.CELL_4G, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CELL: CoreNetworkConnection.CELL, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
NONE: CoreNetworkConnection.NONE, // eslint-disable-line @typescript-eslint/naming-convention
|
||||
};
|
||||
|
||||
window.addEventListener('online', () => {
|
||||
|
@ -65,12 +93,13 @@ export class CoreNetworkService extends Network {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set value of forceOffline flag. If true, the app will think the device is offline.
|
||||
* Set value of forceConnectionMode flag.
|
||||
* The app will think the device is offline or limited connection.
|
||||
*
|
||||
* @param value Value to set.
|
||||
*/
|
||||
setForceOffline(value: boolean): void {
|
||||
this.forceOffline = !!value;
|
||||
setForceConnectionMode(value: CoreNetworkConnection): void {
|
||||
this.forceConnectionMode = value;
|
||||
this.fireObservable();
|
||||
}
|
||||
|
||||
|
@ -89,20 +118,15 @@ export class CoreNetworkService extends Network {
|
|||
* @return Whether the app is online.
|
||||
*/
|
||||
checkOnline(): void {
|
||||
if (this.forceOffline) {
|
||||
if (this.forceConnectionMode === CoreNetworkConnection.NONE) {
|
||||
this.online = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CorePlatform.isMobile()) {
|
||||
this.online = navigator.onLine;
|
||||
const type = this.connectionType;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let online = this.type !== null && this.type != this.Connection.NONE &&
|
||||
this.type != this.Connection.UNKNOWN;
|
||||
let online = type !== null && type !== CoreNetworkConnection.NONE && type !== CoreNetworkConnection.UNKNOWN;
|
||||
|
||||
// Double check we are not online because we cannot rely 100% in Cordova APIs.
|
||||
if (!online && navigator.onLine) {
|
||||
|
@ -123,6 +147,7 @@ export class CoreNetworkService extends Network {
|
|||
|
||||
/**
|
||||
* Returns an observable to notify when the app is connected.
|
||||
* It will also be fired when connection type changes.
|
||||
*
|
||||
* @return Observable.
|
||||
*/
|
||||
|
@ -143,12 +168,11 @@ export class CoreNetworkService extends Network {
|
|||
* Fires the correct observable depending on the connection status.
|
||||
*/
|
||||
protected fireObservable(): void {
|
||||
const previousOnline = this.online;
|
||||
|
||||
this.checkOnline();
|
||||
if (this.online && !previousOnline) {
|
||||
|
||||
if (this.online) {
|
||||
this.connectObservable.next('connected');
|
||||
} else if (!this.online && previousOnline) {
|
||||
} else {
|
||||
this.disconnectObservable.next('disconnected');
|
||||
}
|
||||
}
|
||||
|
@ -159,18 +183,16 @@ export class CoreNetworkService extends Network {
|
|||
* @return Whether the device uses a limited connection.
|
||||
*/
|
||||
isNetworkAccessLimited(): boolean {
|
||||
if (!CorePlatform.isMobile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const limited = [
|
||||
this.Connection.CELL_2G,
|
||||
this.Connection.CELL_3G,
|
||||
this.Connection.CELL_4G,
|
||||
this.Connection.CELL,
|
||||
const limited: CoreNetworkConnection[] = [
|
||||
CoreNetworkConnection.CELL_2G,
|
||||
CoreNetworkConnection.CELL_3G,
|
||||
CoreNetworkConnection.CELL_4G,
|
||||
CoreNetworkConnection.CELL,
|
||||
];
|
||||
|
||||
return limited.indexOf(this.type) > -1;
|
||||
const type = this.connectionType;
|
||||
|
||||
return limited.indexOf(type) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,7 +51,7 @@ import { CoreRedirectPayload } from './navigator';
|
|||
import { CoreSitesFactory } from './sites-factory';
|
||||
import { CoreText } from '@singletons/text';
|
||||
import { CoreLoginHelper } from '@features/login/services/login-helper';
|
||||
import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle';
|
||||
import { CoreErrorWithOptions } from '@classes/errors/errorwithtitle';
|
||||
import { CoreAjaxError } from '@classes/errors/ajaxerror';
|
||||
import { CoreAjaxWSError } from '@classes/errors/ajaxwserror';
|
||||
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
|
||||
|
@ -870,7 +870,7 @@ export class CoreSitesProvider {
|
|||
|
||||
const siteUrlAllowed = await CoreLoginHelper.isSiteUrlAllowed(site.getURL(), false);
|
||||
if (!siteUrlAllowed) {
|
||||
throw new CoreErrorWithTitle(Translate.instant('core.login.sitenotallowed'));
|
||||
throw new CoreErrorWithOptions(Translate.instant('core.login.sitenotallowed'));
|
||||
}
|
||||
|
||||
this.currentSite = site;
|
||||
|
@ -1185,6 +1185,7 @@ export class CoreSitesProvider {
|
|||
siteName: CoreConstants.CONFIG.sitename == '' ? siteInfo?.sitename: CoreConstants.CONFIG.sitename,
|
||||
avatar: siteInfo?.userpictureurl,
|
||||
siteHomeId: siteInfo?.siteid || 1,
|
||||
loggedOut: !!site.loggedOut,
|
||||
};
|
||||
formattedSites.push(basicInfo);
|
||||
}
|
||||
|
@ -1277,7 +1278,7 @@ export class CoreSitesProvider {
|
|||
/**
|
||||
* Logout the user.
|
||||
*
|
||||
* @param forceLogout If true, site will be marked as logged out, no matter the value tool_mobile_forcelogout.
|
||||
* @param options Logout options.
|
||||
* @return Promise resolved when the user is logged out.
|
||||
*/
|
||||
async logout(options: CoreSitesLogoutOptions = {}): Promise<void> {
|
||||
|
@ -1923,6 +1924,7 @@ export type CoreSiteBasicInfo = {
|
|||
avatar?: string; // User's avatar.
|
||||
badge?: number; // Badge to display in the site.
|
||||
siteHomeId?: number; // Site home ID.
|
||||
loggedOut: boolean; // If Site is logged out.
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -46,7 +46,6 @@ import { CoreViewerImageComponent } from '@features/viewer/components/image/imag
|
|||
import { CoreFormFields, CoreForms } from '../../singletons/form';
|
||||
import { CoreModalLateralTransitionEnter, CoreModalLateralTransitionLeave } from '@classes/modal-lateral-transition';
|
||||
import { CoreZoomLevel } from '@features/settings/services/settings-helper';
|
||||
import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle';
|
||||
import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/handlers/multilang';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { NavigationStart } from '@angular/router';
|
||||
|
@ -1345,17 +1344,22 @@ export class CoreDomUtilsProvider {
|
|||
|
||||
const alertOptions: AlertOptions = {
|
||||
message: message,
|
||||
buttons: [Translate.instant('core.ok')],
|
||||
};
|
||||
|
||||
if (this.isNetworkError(message, error)) {
|
||||
alertOptions.cssClass = 'core-alert-network-error';
|
||||
} else if (error instanceof CoreErrorWithTitle) {
|
||||
} else if (typeof error !== 'string' && 'title' in error) {
|
||||
alertOptions.header = error.title || undefined;
|
||||
} else {
|
||||
alertOptions.header = Translate.instant('core.error');
|
||||
}
|
||||
|
||||
if (typeof error !== 'string' && 'buttons' in error && typeof error.buttons !== 'undefined') {
|
||||
alertOptions.buttons = error.buttons;
|
||||
} else {
|
||||
alertOptions.buttons = [Translate.instant('core.ok')];
|
||||
}
|
||||
|
||||
return this.showAlertWithOptions(alertOptions, autocloseTime);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import { CoreFileHelper } from '@services/file-helper';
|
|||
import { CoreDomUtils } from './dom';
|
||||
import { CoreText } from '@singletons/text';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
import { AlertButton } from '@ionic/angular';
|
||||
|
||||
/**
|
||||
* Different type of errors the app can treat.
|
||||
|
@ -37,6 +38,8 @@ export type CoreTextErrorObject = {
|
|||
body?: string;
|
||||
debuginfo?: string;
|
||||
backtrace?: string;
|
||||
title?: string;
|
||||
buttons?: AlertButton[];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -149,6 +152,27 @@ export class CoreTextUtilsProvider {
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some title to an error message.
|
||||
*
|
||||
* @param error Error message or object.
|
||||
* @param title Title to add.
|
||||
* @return Modified error.
|
||||
*/
|
||||
addTitleToError(error: string | CoreError | CoreTextErrorObject | undefined | null, title: string): CoreTextErrorObject {
|
||||
let improvedError: CoreTextErrorObject = {};
|
||||
|
||||
if (typeof error === 'string') {
|
||||
improvedError.message = error;
|
||||
} else if (error && 'message' in error) {
|
||||
improvedError = error;
|
||||
}
|
||||
|
||||
improvedError.title = improvedError.title || title;
|
||||
|
||||
return improvedError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an address as a string, return a URL to open the address in maps.
|
||||
*
|
||||
|
|
|
@ -18,9 +18,6 @@ import { CoreUtils } from '@services/utils/utils';
|
|||
import { makeSingleton, NgZone } from '@singletons';
|
||||
import { TestingBehatElementLocator, TestingBehatFindOptions } from './behat-runtime';
|
||||
|
||||
// Containers that block containers behind them.
|
||||
const blockingContainers = ['ION-ALERT', 'ION-POPOVER', 'ION-ACTION-SHEET', 'CORE-USER-TOURS-USER-TOUR', 'ION-PAGE'];
|
||||
|
||||
/**
|
||||
* Behat Dom Utils helper functions.
|
||||
*/
|
||||
|
@ -331,13 +328,14 @@ export class TestingBehatDomUtilsService {
|
|||
}
|
||||
|
||||
// Get containers until one blocks other views.
|
||||
containers.find(container => {
|
||||
containers.some(container => {
|
||||
if (container.tagName === 'ION-TOAST') {
|
||||
container = container.shadowRoot?.querySelector('.toast-container') || container;
|
||||
}
|
||||
topContainers.push(container);
|
||||
|
||||
return blockingContainers.includes(container.tagName);
|
||||
// If container has backdrop it blocks the rest of the UI.
|
||||
return container.querySelector(':scope > ion-backdrop') || container.classList.contains('backdrop');
|
||||
});
|
||||
|
||||
return topContainers;
|
||||
|
|
|
@ -25,9 +25,8 @@ import { CoreCronDelegate, CoreCronDelegateService } from '@services/cron';
|
|||
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreDom } from '@singletons/dom';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
import { CoreCoursesDashboardPage } from '@features/courses/pages/dashboard/dashboard';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreSites, CoreSitesProvider } from '@services/sites';
|
||||
|
||||
/**
|
||||
* Behat runtime servive with public API.
|
||||
|
@ -53,6 +52,10 @@ export class TestingBehatRuntimeService {
|
|||
return CorePushNotifications.instance;
|
||||
}
|
||||
|
||||
get sites(): CoreSitesProvider {
|
||||
return CoreSites.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init behat functions and set options like skipping onboarding.
|
||||
*
|
||||
|
@ -348,23 +351,17 @@ export class TestingBehatRuntimeService {
|
|||
this.log('Action - pullToRefresh');
|
||||
|
||||
try {
|
||||
// TODO We should generalize this to work with other pages. It's not possible to use
|
||||
// an IonRefresher instance because it doesn't expose any methods to trigger refresh,
|
||||
// so we'll have to find another way.
|
||||
|
||||
const dashboard = this.getAngularInstance<CoreCoursesDashboardPage>(
|
||||
'page-core-courses-dashboard',
|
||||
'CoreCoursesDashboardPage',
|
||||
// 'el' is protected, but there's no other way to trigger refresh programatically.
|
||||
const ionRefresher = this.getAngularInstance<{ el: HTMLIonRefresherElement }>(
|
||||
'ion-refresher',
|
||||
'IonRefresher',
|
||||
);
|
||||
|
||||
if (!dashboard) {
|
||||
return 'ERROR: It\'s not possible to pull to refresh the current page '
|
||||
+ '(the dashboard page is the only one supported at the moment).';
|
||||
if (!ionRefresher) {
|
||||
return 'ERROR: It\'s not possible to pull to refresh the current page.';
|
||||
}
|
||||
|
||||
await new Promise(resolve => {
|
||||
dashboard.refreshDashboard({ complete: resolve } as IonRefresher);
|
||||
});
|
||||
ionRefresher.el.dispatchEvent(new CustomEvent('ionRefresh'));
|
||||
|
||||
return 'OK';
|
||||
} catch (error) {
|
||||
|
|
|
@ -25,9 +25,7 @@ Feature: It navigates properly using deep links.
|
|||
|
||||
Scenario: Receive a push notification
|
||||
Given I entered the app as "student2"
|
||||
When I press the user menu button in the app
|
||||
And I press "Log out" in the app
|
||||
And I wait the app to restart
|
||||
When I log out in the app
|
||||
And I press "Add" 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
|
||||
|
@ -67,9 +65,7 @@ Feature: It navigates properly using deep links.
|
|||
|
||||
Scenario: Open a link with a custom URL that calls WebServices for a logged out site
|
||||
Given I entered the app as "student2"
|
||||
When I press the user menu button in the app
|
||||
And I press "Log out" in the app
|
||||
And I wait the app to restart
|
||||
When I log out in the app
|
||||
And I open a custom link in the app for:
|
||||
| forum |
|
||||
| Test forum |
|
||||
|
|
Loading…
Reference in New Issue