first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+337
View File
@@ -0,0 +1,337 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Behat message-related steps definitions.
*
* @package core_message
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
/**
* Messaging system steps definitions.
*
* @package core_message
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_message extends behat_base {
/**
* Return the list of partial named selectors.
*
* @return array
*/
public static function get_partial_named_selectors(): array {
return [
new behat_component_named_selector('Message', [".//*[@data-conversation-id]//img[%altMatch%]/.."]),
new behat_component_named_selector('Message conversation', [
<<<XPATH
.//*[@data-region='message-drawer' and contains(., %locator%)]//div[@data-region='content-message-container']
XPATH
], false),
new behat_component_named_selector('Message header', [
<<<XPATH
.//*[@data-region='message-drawer']//div[@data-region='header-content' and contains(., %locator%)]
XPATH
]),
new behat_component_named_selector('Message member', [
<<<XPATH
.//*[@data-region='message-drawer']//div[@data-region='group-info-content-container']
//div[@class='list-group' and not(contains(@class, 'hidden'))]//*[%core_message/textMatch%]
XPATH
, <<<XPATH
.//*[@data-region='message-drawer']//div[@data-region='group-info-content-container']
//div[@data-region='empty-message-container' and not(contains(@class, 'hidden')) and contains(., %locator%)]
XPATH
], false),
new behat_component_named_selector('Message tab', [
<<<XPATH
.//*[@data-region='message-drawer']//button[@data-toggle='collapse' and contains(string(), %locator%)]
XPATH
], false),
new behat_component_named_selector('Message list area', [
<<<XPATH
.//*[@data-region='message-drawer']//*[contains(@data-region, concat('view-overview-', %locator%))]
XPATH
], false),
new behat_component_named_selector('Message content', [
<<<XPATH
.//*[@data-region='message-drawer']//*[@data-region='message' and @data-message-id and contains(., %locator%)]
XPATH
], false),
];
}
/**
* Return a list of the Mink named replacements for the component.
*
* Named replacements allow you to define parts of an xpath that can be reused multiple times, or in multiple
* xpaths.
*
* This method should return a list of {@link behat_component_named_replacement} and the docs on that class explain
* how it works.
*
* @return behat_component_named_replacement[]
*/
public static function get_named_replacements(): array {
return [
new behat_component_named_replacement('textMatch', 'text()[contains(., %locator%)]'),
];
}
/**
* Open the messaging UI.
*
* @Given /^I open messaging$/
*/
public function i_open_messaging() {
// Visit home page and follow messages.
$this->execute("behat_general::i_am_on_homepage");
$this->execute("behat_general::i_click_on", [get_string('togglemessagemenu', 'core_message'), 'link']);
}
/**
* Open the messaging conversation list.
*
* @Given /^I open the "(?P<tab_string>(?:[^"]|\\")*)" conversations list/
* @param string $tab
*/
public function i_open_the_conversations_list(string $tab) {
$this->execute('behat_general::i_click_on', [
$this->escape($tab),
'core_message > Message tab'
]);
}
/**
* Open the messaging UI.
*
* @Given /^I open messaging information$/
*/
public function i_open_messaging_information() {
$this->execute('behat_general::i_click_on', ["[data-action='view-group-info']", 'css_element']);
}
/**
* View the contact information of a user in the messages ui.
*
* @Given /^I view the "(?P<user_full_name_string>(?:[^"]|\\")*)" contact in the message area$/
* @param string $userfullname
*/
public function i_view_contact_in_messages($userfullname) {
// Visit home page and follow messages.
$this->execute('behat_message::i_select_user_in_messaging', [$userfullname]);
$this->execute('behat_general::i_click_on_in_the',
array(
"//a[@data-action='view-contact']",
"xpath_element",
"//*[@data-region='message-drawer']//div[@data-region='header-container']",
"xpath_element",
)
);
$this->execute('behat_general::i_click_on_in_the',
array(
"//img[@title='Picture of ". $this->escape($userfullname) . "']",
"xpath_element",
"//*[@data-region='message-drawer']//*[@data-region='view-contact']",
"xpath_element",
)
);
$this->execute('behat_general::wait_until_the_page_is_ready');
}
/**
* Select a user in the messaging UI.
*
* @Given /^I select "(?P<user_full_name_string>(?:[^"]|\\")*)" user in messaging$/
* @param string $userfullname
*/
public function i_select_user_in_messaging($userfullname) {
$this->execute('behat_message::i_open_messaging', []);
$this->execute('behat_message::i_search_for_string_in_messaging', [$userfullname]);
// Need to limit the click to the search results because the 'view-contact-profile' elements
// can occur in two separate divs on the page.
$this->execute('behat_general::i_click_on_in_the',
[
$this->escape($userfullname),
'link',
"[data-region='message-drawer'] [data-region='search-results-container']",
"css_element",
]
);
$this->execute('behat_general::wait_until_the_page_is_ready');
}
/**
* Search for a string using the messaging search.
*
* @Given /^I search for "(?P<string>(?:[^"]|\\")*)" in messaging$/
* @param string $string the search string.
*/
public function i_search_for_string_in_messaging($string) {
$messagedrawer = $this->find('css', '[data-region="message-drawer"]');
$this->execute('behat_general::i_click_on_in_the', [
get_string('search', 'core'), 'field',
$messagedrawer, 'NodeElement'
]);
$this->execute('behat_forms::i_set_the_field_with_xpath_to', [
"//*[@data-region='message-drawer']//input[@data-region='search-input']",
$this->escape($string)
]);
$this->execute('behat_general::i_click_on_in_the', [
'[data-action="search"]', 'css_element',
$messagedrawer, 'NodeElement'
]);
$this->execute('behat_general::wait_until_the_page_is_ready');
}
/**
* Sends a message to the specified user from the logged user. The user full name should contain the first and last names.
*
* @Given /^I send "(?P<message_contents_string>(?:[^"]|\\")*)" message to "(?P<user_full_name_string>(?:[^"]|\\")*)" user$/
* @param string $messagecontent
* @param string $userfullname
*/
public function i_send_message_to_user($messagecontent, $userfullname) {
$this->execute('behat_message::i_select_user_in_messaging', [$userfullname]);
$this->execute('behat_forms::i_set_the_field_with_xpath_to',
array("//textarea[@data-region='send-message-txt']", $this->escape($messagecontent))
);
$this->execute('behat_general::i_click_on_in_the',
[
'[data-action="send-message"]',
'css_element',
"[data-region='message-drawer'] [data-region='footer-container'] [data-region='view-conversation']",
"css_element",
]
);
}
/**
* Select messages from a user in the messaging ui.
*
* @Given /^I send "(?P<message_contents_string>(?:[^"]|\\")*)" message in the message area$/
* @param string $messagecontent
*/
public function i_send_message_in_the_message_area($messagecontent) {
$this->execute('behat_general::wait_until_the_page_is_ready');
$this->execute('behat_forms::i_set_the_field_with_xpath_to',
array("//textarea[@data-region='send-message-txt']", $this->escape($messagecontent))
);
$this->execute("behat_forms::press_button", get_string('sendmessage', 'message'));
}
/**
* Navigate back in the messages ui drawer.
*
* @Given /^I go back in "(?P<parent_element_string>(?:[^"]|\\")*)" message drawer$/
* @param string $parentelement
*/
public function i_go_back_in_message_drawer($parentelement) {
$this->execute('behat_general::i_click_on_in_the',
array(
'a[data-route-back]',
'css_element',
'[data-region="'.$this->escape($parentelement).'"]',
'css_element',
)
);
}
/**
* Select a user in the messaging UI.
*
* @Given /^I select "(?P<conversation_name_string>(?:[^"]|\\")*)" conversation in messaging$/
* @param string $conversationname
*/
public function i_select_conversation_in_messaging($conversationname) {
$this->execute('behat_general::i_click_on',
array(
$this->escape($conversationname),
'core_message > Message',
)
);
}
/**
* Open the contact menu.
*
* @Given /^I open contact menu$/
*/
public function i_open_contact_menu() {
$this->execute('behat_general::wait_until_the_page_is_ready');
$this->execute('behat_general::i_click_on_in_the',
array(
'button',
'css_element',
'[data-region="message-drawer"] [data-region="header-container"]',
'css_element',
)
);
}
/**
* Select a user in a specific messaging UI conversations list.
*
* @Given /^I select "(?P<conv_name_string>(?:[^"]|\\")*)" conversation in the "(?P<list_name_string>(?:[^"]|\\")*)" conversations list$/
* @param string $convname
* @param string $listname
*/
public function i_select_conversation_in_the_conversations_list(string $convname, string $listname) {
$xpath = '//*[@data-region="message-drawer"]//div[@data-region="view-overview-'.
$this->escape($listname).
'"]//*[@data-conversation-id]//img[contains(@alt,"'.
$this->escape($convname).'")]';
$this->execute('behat_general::i_click_on', array($xpath, 'xpath_element'));
}
/**
* Open the settings preferences.
*
* @Given /^I open messaging settings preferences$/
*/
public function i_open_messaging_settings_preferences() {
$this->execute('behat_general::wait_until_the_page_is_ready');
$this->execute('behat_general::i_click_on',
array(
'//*[@data-region="message-drawer"]//a[@data-route="view-settings"]',
'xpath_element',
'',
'',
)
);
}
}
+67
View File
@@ -0,0 +1,67 @@
@core @core_message @javascript
Feature: To be able to block users that we are able to or to see a message if we can not
In order to attempt to block a user
As a user
I need to be able to block a user or to see a message if we can not
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@emample.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | teacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following config values are set as admin:
| messaging | 1 |
Scenario: Block a user
Given I log in as "student1"
And I select "Student 2" user in messaging
And I open contact menu
And I click on "Block" "link" in the "[data-region='header-container']" "css_element"
And I should see "Are you sure you want to block Student 2?"
And I click on "Block" "button" in the "[data-region='confirm-dialogue']" "css_element"
And I should see "You have blocked this user."
And I log out
When I log in as "student2"
And I open messaging
And I select "Student 1" user in messaging
Then I should see "You are unable to message this user"
Scenario: Unable to block a user
Given I log in as "student1"
And I select "Teacher 1" user in messaging
And I open contact menu
When I click on "Block" "link" in the "[data-region='header-container']" "css_element"
Then I should see "You can't block Teacher 1"
Scenario: Block a user who then gets an elevated role
Given I log in as "student1"
And I select "Student 2" user in messaging
And I open contact menu
And I click on "Block" "link" in the "[data-region='header-container']" "css_element"
And I click on "Block" "button" in the "[data-region='confirm-dialogue']" "css_element"
And I log out
And I log in as "admin"
And I am on "Course 1" course homepage
And I navigate to course participants
And I click on "Unenrol" "icon" in the "student2" "table_row"
And I click on "Unenrol" "button" in the "Unenrol" "dialogue"
And I enrol "Student 2" user as "Teacher"
And I log out
And I log in as "student2"
And I select "Student 1" user in messaging
And I should not see "You are unable to message this user"
And I log out
And I log in as "student1"
And I select "Student 2" user in messaging
And I open contact menu
When I click on "Block" "link" in the "[data-region='header-container']" "css_element"
Then I should see "You can't block Student 2"
@@ -0,0 +1,42 @@
@core @core_message @javascript
Feature: Capability test for 'moodle/site:messageanyuser'
In order to test that the 'moodle/site:messageanyuser' works as expected
As a user with or without the capability 'moodle/site:messageanyuser'
I should either be able to message anyone regardless of their messaging preferences, or not
Background:
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | teacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following config values are set as admin:
| messaging | 1 |
| messagingallusers | 1 |
And I log in as "student1"
And I open messaging
And I open messaging settings preferences
And I click on "//label[text()[contains(.,'My contacts only')]]" "xpath_element"
And I log out
Scenario: Allow a message to be sent as the user has the correct capabilities
Given I log in as "teacher1"
And I open messaging
When I send "Hi!" message to "Student 1" user
Then I should see "Hi!" in the "//div[@data-region='message-drawer']//div[@data-region='content-message-container']" "xpath_element"
Scenario: Do not allow a message to be sent as the user does not have the correct capabilities
Given I log in as "student2"
And I am on "Course 1" course homepage
And I follow "Participants"
And I follow "Student 1"
When I click on "Message" "link" in the ".header-button-group" "css_element"
Then I should see "Student 1 is not in your contacts"
+271
View File
@@ -0,0 +1,271 @@
@core @core_message @javascript
Feature: Delete messages from conversations
In order to manage a course group in a course
As a user
I need to be able to delete messages from conversations
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | enablemessaging |
| Group 1 | C1 | G1 | 1 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G1 |
And the following "group messages" exist:
| user | group | message |
| student1 | G1 | Hi! |
| student2 | G1 | How are you? |
| student1 | G1 | Can somebody help me? |
And the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
| student2 | student1 | Hello! |
| student1 | student2 | Are you free? |
And the following config values are set as admin:
| messaging | 1 |
| messagingminpoll | 1 |
Scenario: Delete a message sent by the user from a group conversation
Given I log in as "student1"
And I open messaging
And "Group 1" "core_message > Message" should exist
And I select "Group 1" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I click on "How are you?" "core_message > Message content"
And I click on "Can somebody help me?" "core_message > Message content"
And I should see "3" in the "[data-region='message-selected-court']" "css_element"
# Clicking to unselect
And I click on "How are you?" "core_message > Message content"
And I click on "Can somebody help me?" "core_message > Message content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should see "##today##%d %B##" in the "Group 1" "core_message > Message conversation"
And I should see "How are you?" in the "Group 1" "core_message > Message conversation"
And I should see "Can somebody help me?" in the "Group 1" "core_message > Message conversation"
And I should not see "Messages selected"
Scenario: Delete two messages from a group conversation; one sent by another user.
Given I log in as "student1"
And I open messaging
And "Group 1" "core_message > Message" should exist
And I select "Group 1" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And I click on "How are you?" "core_message > Message content"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should see "##today##%d %B##" in the "Group 1" "core_message > Message conversation"
And I should not see "How are you?" in the "Group 1" "core_message > Message conversation"
And I should see "Can somebody help me?" in the "Group 1" "core_message > Message conversation"
And I should not see "Messages selected"
# Check messages were not deleted for other users
And I log out
And I log in as "student2"
And I open messaging
And I select "Group 1" conversation in messaging
And I should see "Hi!"
And I should see "How are you?"
And I should see "Can somebody help me?"
Scenario: Cancel deleting two messages from a group conversation
Given I log in as "student1"
And I open messaging
And "Group 1" "core_message > Message" should exist
And I select "Group 1" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I click on "How are you?" "core_message > Message content"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Canceling deletion, so messages should be there
And I should see "Cancel"
And I click on "//button[@data-action='cancel-confirm']" "xpath_element"
Then I should not see "Cancel"
And I should see "Hi!"
And I should see "How are you?" in the "Group 1" "core_message > Message conversation"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
Scenario: Delete a message sent by the user from a private conversation
Given I log in as "student1"
And I open messaging
And I should see "Private"
And I open the "Private" conversations list
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
And I should see "Hello!" in the "Student 2" "core_message > Message conversation"
And I should see "Are you free?" in the "Student 2" "core_message > Message conversation"
And I should not see "Messages selected"
Scenario: Delete two messages from a private conversation; one sent by another user
Given I log in as "student1"
And I open messaging
And I should see "Private"
And I open the "Private" conversations list
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And I click on "Hello!" "core_message > Message content"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should not see "Hello!" in the "Student 2" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
And I should see "Are you free?" in the "Student 2" "core_message > Message conversation"
And I should not see "Messages selected"
# Check messages were not deleted for the other user
And I log out
And I log in as "student2"
And I open messaging
And I open the "Private" conversations list
And I select "Student 1" conversation in messaging
And I should see "Hi!"
And I should see "Hello!"
And I should see "Are you free?"
Scenario: Cancel deleting two messages from a private conversation
Given I log in as "student1"
And I open messaging
And I should see "Private"
And I open the "Private" conversations list
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I click on "Hello!" "core_message > Message content"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Canceling deletion, so messages should be there
And I should see "Cancel"
And I click on "//button[@data-action='cancel-confirm']" "xpath_element"
Then I should not see "Cancel"
And I should see "Hi!"
And I should see "Hello!" in the "Student 2" "core_message > Message conversation"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
Scenario: Delete a message sent by the user from a favorite conversation
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
And I should see "Hello!" in the "Student 2" "core_message > Message conversation"
And I should not see "Messages selected"
Scenario: Delete two messages from a favourite conversation; one sent by another user
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And I click on "Hello!" "core_message > Message content"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should not see "Hello!" in the "Student 2" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
And I should see "Are you free?" in the "Student 2" "core_message > Message conversation"
And I should not see "Messages selected"
Scenario: Cancel deleting two messages from a favourite conversation
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "core_message > Message content"
And I click on "Hello!" "core_message > Message content"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Canceling deletion, so messages should be there
And I should see "Cancel"
And I click on "//button[@data-action='cancel-confirm']" "xpath_element"
Then I should not see "Cancel"
And I should see "Hi!"
And I should see "Hello!" in the "Student 2" "core_message > Message conversation"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
Scenario: Check an empty favourite conversation is still favourite
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I should see "Student 2"
And I select "Student 2" conversation in the "favourites" conversations list
And I click on "Hi!" "core_message > Message content"
And I click on "Hello!" "core_message > Message content"
And I click on "Are you free?" "core_message > Message content"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
And I go back in "view-conversation" message drawer
Then I should not see "Student 2" in the "//*[@data-region='message-drawer']//div[@data-region='view-overview-favourites']" "xpath_element"
And the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
And I open messaging
And I open the "Starred" conversations list
And I select "Student 2" conversation in the "favourites" conversations list
And I should see "Hi!" in the "Student 2" "core_message > Message conversation"
@@ -0,0 +1,98 @@
@core @core_message @javascript
Feature: Star and unstar conversations
In order to manage a course group in a course
As a user
I need to be able to star and unstar conversations
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | enablemessaging |
| Group 1 | C1 | G1 | 1 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G1 |
And the following config values are set as admin:
| messaging | 1 |
| messagingminpoll | 1 |
Scenario: Star a group conversation
Given I log in as "student1"
Then I open messaging
And I open the "Group" conversations list
And "Group 1" "core_message > Message" should exist
And I select "Group 1" conversation in messaging
And I open contact menu
And I click on "Star conversation" "link" in the "conversation-actions-menu" "region"
And I go back in "view-conversation" message drawer
And I open the "Starred" conversations list
And I should see "Group 1" in the "favourites" "core_message > Message list area"
And I open the "Group" conversations list
And I should not see "Group 1" in the "group-messages" "core_message > Message list area"
Scenario: Unstar a group conversation
Given I log in as "student1"
Then I open messaging
And I open the "Group" conversations list
And "Group 1" "core_message > Message" should exist
And I select "Group 1" conversation in messaging
And I open contact menu
And I click on "Star conversation" "link" in the "conversation-actions-menu" "region"
And I go back in "view-conversation" message drawer
And I open the "Starred" conversations list
And I should see "Group 1" in the "favourites" "core_message > Message list area"
And I select "Group 1" conversation in messaging
And I open contact menu
And I click on "Unstar" "link" in the "conversation-actions-menu" "region"
And I go back in "view-conversation" message drawer
And I open the "Starred" conversations list
And I should not see "Group 1" in the "favourites" "core_message > Message list area"
And I open the "Group" conversations list
And I should see "Group 1" in the "group-messages" "core_message > Message list area"
Scenario: Star a private conversation
Given the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
Then I log in as "student1"
And I open messaging
And I open the "Private" conversations list
And "Student 2" "core_message > Message" should exist
And I select "Student 2" conversation in messaging
And I open contact menu
And I click on "Star conversation" "link" in the "conversation-actions-menu" "region"
And I go back in "view-conversation" message drawer
And I open the "Starred" conversations list
And I should see "Student 2" in the "favourites" "core_message > Message list area"
And I open the "Private" conversations list
And I should not see "Student 2" in the "messages" "core_message > Message list area"
Scenario: Unstar a private conversation
Given the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
Then I log in as "student1"
And I open messaging
And I should see "Student 2" in the "favourites" "core_message > Message list area"
And I select "Student 2" conversation in messaging
And I open contact menu
And I click on "Unstar" "link" in the "conversation-actions-menu" "region"
And I go back in "view-conversation" message drawer
And I open the "Starred" conversations list
And I should not see "Group 1" in the "favourites" "core_message > Message list area"
And I open the "Private" conversations list
And I should see "Student 2" in the "messages" "core_message > Message list area"
@@ -0,0 +1,125 @@
@core @core_message @javascript
Feature: Create conversations for course's groups
In order to manage a course group in a course
As a user
I need to be able to ensure group conversations reflect the memberships of course groups
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student0 | Student | 0 | student0@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
| student4 | Student | 4 | student4@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student0 | C1 | student |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | enablemessaging |
| Group 1 | C1 | G1 | 1 |
| Group 2 | C1 | G2 | 1 |
| Group 3 | C1 | G3 | 0 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| student0 | G1 |
| student1 | G1 |
| student2 | G1 |
| student3 | G1 |
| teacher1 | G2 |
| teacher1 | G3 |
| student0 | G3 |
And the following config values are set as admin:
| messaging | 1 |
| messagingminpoll | 1 |
Scenario: Group conversations are restricted to members
Given I log in as "teacher1"
Then I open messaging
And I open the "Group" conversations list
And "Group 1" "core_message > Message" should exist
And "Group 2" "core_message > Message" should exist
And "Group 3" "core_message > Message" should not exist
And I log out
And I log in as "student1"
And I open messaging
And I open the "Group" conversations list
And "Group 1" "core_message > Message" should exist
And "Group 2" "core_message > Message" should not exist
And "Group 3" "core_message > Message" should not exist
Scenario: View group conversation's participants numbers
Given I log in as "teacher1"
Then I open messaging
And I open the "Group" conversations list
And I select "Group 1" conversation in messaging
And I should see "5 participants" in the "Group 1" "core_message > Message header"
And I go back in "view-conversation" message drawer
And I select "Group 2" conversation in messaging
And I should see "1 participants" in the "Group 2" "core_message > Message header"
Scenario: View group conversation's participants list
Given I log in as "teacher1"
Then I open messaging
And I open the "Group" conversations list
# Check Group 1 participants list.
And I select "Group 1" conversation in messaging
And I open messaging information
And "Teacher 1" "core_message > Message member" should not exist
And "Student 0" "core_message > Message member" should exist
And "Student 1" "core_message > Message member" should exist
And "Student 2" "core_message > Message member" should exist
And "Student 3" "core_message > Message member" should exist
And "Student 4" "core_message > Message member" should not exist
And I go back in "group-info-content-container" message drawer
And I go back in "view-conversation" message drawer
# Check Group 2 participants list.
And I select "Group 2" conversation in messaging
And I open messaging information
And "Teacher 1" "core_message > Message member" should not exist
And "No participants" "core_message > Message member" should exist
And "Student 4" "core_message > Message member" should not exist
Scenario: Check group conversation members are synced when a new group member is added
Given I log in as "teacher1"
Then I am on the "Course 1" "groups" page
And I add "Student 4 (student4@example.com)" user to "Group 1" group members
And I add "Student 4 (student4@example.com)" user to "Group 2" group members
And I open messaging
And I open the "Group" conversations list
And I select "Group 1" conversation in messaging
And I should see "6 participants" in the "Group 1" "core_message > Message header"
And I open messaging information
And "Student 4" "core_message > Message member" should exist
And I go back in "group-info-content-container" message drawer
And I go back in "view-conversation" message drawer
And I select "Group 2" conversation in messaging
And I should see "2 participants" in the "Group 2" "core_message > Message header"
And I open messaging information
And "No participants" "core_message > Message member" should not exist
And "Student 4" "core_message > Message member" should exist
Scenario: Disable messaging for private groups
Given the following "groups" exist:
| name | course | idnumber | visibility | enablemessaging |
| Messaging group | C1 | MG | 0 | 1 |
| No messaging group | C1 | NM | 2 | 1 |
And the following "group members" exist:
| user | group |
| student1 | MG |
| student1 | NM |
When I log in as "student1"
And I open messaging
And I open the "Group" conversations list
Then "Messaging group" "core_message > Message" should exist
Then "No messaging group" "core_message > Message" should not exist
@@ -0,0 +1,39 @@
@core @core_message @javascript
Feature: Message admin settings
In order to manage the messaging system
As an admin
I need to be able to enabled/disabled site-wide messaging system
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | One | one@example.com |
And the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
Scenario: enable site messaging
Given the following config values are set as admin:
| messaging | 1 |
When I log in as "admin"
Then "Toggle messaging drawer" "icon" should exist
And I navigate to "Users > Accounts > Browse list of users" in site administration
And I should see "User One"
And I follow "User One"
And "Add to contacts" "link" should exist
And I am on "Course 1" course homepage
And I navigate to course participants
And the "With selected users..." select box should contain "Send a message"
Scenario: disable site messaging
Given the following config values are set as admin:
| messaging | 0 |
When I log in as "admin"
Then "Toggle messaging drawer" "icon" should not exist
And I navigate to "Users > Accounts > Browse list of users" in site administration
And I should see "User One"
And I follow "User One"
And "Add to contacts" "link" should not exist
And I am on "Course 1" course homepage
And I navigate to course participants
And the "With selected users..." select box should not contain "Send a message"
@@ -0,0 +1,125 @@
@core @core_message @javascript
Feature: Message delete conversations
In order to communicate with fellow users
As a user
I need to be able to delete conversations
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| name | shortname |
| course1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following config values are set as admin:
| messaging | 1 |
| messagingallusers | 1 |
| messagingminpoll | 1 |
And the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
| student2 | student1 | What do you need? |
Scenario: Delete a private conversation
And I log in as "student2"
And I open messaging
And I select "Student 1" conversation in the "messages" conversations list
And I open contact menu
And I click on "Delete conversation" "link" in the "//div[@data-region='header-container']" "xpath_element"
# Confirm deletion, so conversation should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-conversation']" "xpath_element"
And I should not see "Delete"
And I should not see "Hi!" in the "Student 1" "core_message > Message conversation"
And I should not see "What do you need?" in the "Student 1" "core_message > Message conversation"
And I should not see "##today##%d %B##" in the "Student 1" "core_message > Message conversation"
# Check user is deleting private conversation only for them
And I log out
And I log in as "student1"
And I open messaging
And I select "Student 2" conversation in the "messages" conversations list
And I should see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should see "What do you need?" in the "Student 2" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
Scenario: Cancel deleting a private conversation
Given I log in as "student1"
And I open messaging
And I select "Student 2" conversation in the "messages" conversations list
And I open contact menu
And I click on "Delete conversation" "link" in the "//div[@data-region='header-container']" "xpath_element"
# Cancel deletion, so conversation should be there
And I should see "Cancel"
And I click on "//button[@data-action='cancel-confirm']" "xpath_element"
And I should not see "Cancel"
And I should see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
Scenario: Delete a starred conversation
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I select "Student 2" conversation in the "favourites" conversations list
And I open contact menu
And I click on "Delete conversation" "link" in the "//div[@data-region='header-container']" "xpath_element"
# Confirm deletion, so conversation should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-conversation']" "xpath_element"
And I should not see "Delete"
And I should not see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should not see "What do you need?" in the "Student 2" "core_message > Message conversation"
And I should not see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
# Check user is deleting private conversation only for them
And I log out
And I log in as "student2"
And I open messaging
And I select "Student 1" conversation in the "messages" conversations list
And I should see "Hi!" in the "Student 1" "core_message > Message conversation"
And I should see "What do you need?" in the "Student 1" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 1" "core_message > Message conversation"
Scenario: Cancel deleting a starred conversation
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
When I log in as "student1"
And I open messaging
And I select "Student 2" conversation in the "favourites" conversations list
Then I should see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
And I open contact menu
And I click on "Delete conversation" "link" in the "//div[@data-region='header-container']" "xpath_element"
# Cancel deletion, so conversation should be there
And I should see "Cancel"
And I click on "//button[@data-action='cancel-confirm']" "xpath_element"
And I should not see "Cancel"
And I should see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
Scenario: Check a deleted starred conversation is still starred
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
When I log in as "student1"
And I open messaging
And I select "Student 2" conversation in the "favourites" conversations list
And I open contact menu
And I click on "Delete conversation" "link" in the "//div[@data-region='header-container']" "xpath_element"
Then I should see "Delete"
And I click on "//button[@data-action='confirm-delete-conversation']" "xpath_element"
And I should not see "Delete"
And I should not see "Hi!" in the "Student 2" "core_message > Message conversation"
And I go back in "view-conversation" message drawer
And I should not see "Student 2" in the "favourites" "core_message > Message list area"
And the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
And I open messaging
And I should see "Student 2" in the "favourites" "core_message > Message list area"
@@ -0,0 +1,112 @@
@core @core_message @javascript
Feature: Manage contacts
In order to communicate with fellow users
As a user
I need to be able to add, decline and remove users
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
| student4 | Student | 4 | student4@example.com |
And the following "courses" exist:
| fullname | shortname |
| course1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
And the following "message contacts" exist:
| user | contact |
| student1 | student2 |
And the following config values are set as admin:
| messaging | 1 |
| messagingallusers | 1 |
| messagingminpoll | 1 |
Scenario: Send a 'contact request' to someone to add a contact
Given I log in as "student1"
Then I open messaging
And I select "Student 4" user in messaging
And I open contact menu
And I click on "Add to contacts" "link"
And I click on "Add" "button"
And I log out
And I log in as "student3"
And I select "Student 4" user in messaging
And I open contact menu
And I click on "Add to contacts" "link"
And I click on "Add" "button"
And I should see "Contact request sent"
And I log out
And I log in as "student4"
Then I should see "2" in the "//div[@data-region='popover-region-messages']//*[@data-region='count-container']" "xpath_element"
And I open messaging
And I click on "Contacts" "link"
Then I should see "2" in the "//div[@data-region='view-contacts']//*[@data-region='contact-request-count']" "xpath_element"
And I click on "Requests" "link_or_button"
And I click on "Student 1 Would like to contact you" "link"
Then I should see "Accept and add to contacts"
And I click on "Accept and add to contacts" "link_or_button"
And I should not see "Accept and add to contacts"
And I log out
And I log in as "student1"
And I open messaging
And I click on "Contacts" "link"
And I should see "Student 4" in the "//*[@data-section='contacts']" "xpath_element"
Scenario: Send a 'contact request' to someone to add a contact in the profile page
Given I am on the "student4" "user > profile" page logged in as student3
And I should see "Add to contacts"
When I click on "Add to contacts" "link"
Then I should see "Contact request sent"
And I log out
And I am on the "student3" "user > profile" page logged in as student4
And I should see "Waiting to be added as contact"
And I open messaging
And I click on "Contacts" "link"
And I click on "Requests" "link_or_button"
And I click on "Student 3 Would like to contact you" "link"
And I should see "Accept and add to contacts"
And I click on "Accept and add to contacts" "link_or_button"
And I should not see "Accept and add to contacts"
And I log out
And I am on the "student4" "user > profile" page logged in as student3
And I should see "Remove from contacts"
Scenario: Decline a 'contact request' from someone
Given I log in as "student1"
Then I open messaging
And I select "Student 3" user in messaging
And I open contact menu
And I click on "Add to contacts" "link"
And I click on "Add" "button"
And I should see "Contact request sent"
And I log out
And I log in as "student3"
Then I should see "1" in the "//div[@data-region='popover-region-messages']//*[@data-region='count-container']" "xpath_element"
And I open messaging
And I click on "Contacts" "link"
Then I should see "1" in the "//div[@data-region='view-contacts']//*[@data-region='contact-request-count']" "xpath_element"
And I click on "Requests" "link_or_button"
And I click on "Student 1 Would like to contact you" "link"
Then I should see "Accept and add to contacts"
And I click on "Decline" "link_or_button"
And I should not see "Accept and add to contacts"
And I open contact menu
Then I should see "Add to contacts" in the "//div[@data-region='header-container']" "xpath_element"
Scenario: Remove existing contact
Given I log in as "student1"
Then I open messaging
And I click on "Contacts" "link"
And I click on "Student 2" "link" in the "//*[@data-section='contacts']" "xpath_element"
And I open contact menu
And I click on "Remove from contacts" "link"
And I click on "Remove" "button"
And I go back in "view-conversation" message drawer
And I should see "No contacts" in the "//*[@data-region='empty-message-container']" "xpath_element"
@@ -0,0 +1,217 @@
@core @core_message @javascript
Feature: Manage notification preferences - Email
In order to be notified of messages
As a user
I need to be able to update my messaging notification preferences
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
# Turn off the course welcome message, so we can easily test other messages.
And the following config values are set as admin:
| messaging | 1 | core |
| sendcoursewelcomemessage | 0 | enrol_manual |
Scenario: Disable email notifications for everybody
Given I log in as "admin"
When I navigate to "Messaging > Notification settings" in site administration
And I set the field "email" to "0"
And I press "Save changes"
And I log out
And I log in as "student1"
And I open messaging
And I open messaging settings preferences
Then I should not see "Notification preferences"
And I should not see "Email"
Scenario: Enable email notifications
# Disable email default value
Given the following "user preferences" exist:
| user | preference | value |
| student1 | message_provider_moodle_instantmessage_enabled | none |
When I log in as "admin"
And I navigate to "Messaging > Notification settings" in site administration
And I set the field "email" to "1"
And I press "Save changes"
And I log out
And I log in as "student1"
And I open messaging
And I open messaging settings preferences
Then I should see "Notification preferences"
And I should see "Email"
And the field "Email" matches value "0"
And I set the field "Email" to "1"
And I follow "Preferences" in the user menu
And I click on "Message preferences" "link"
And the field "Email" matches value "1"
Scenario: Disable email notifications
Given I log in as "admin"
When I navigate to "Messaging > Notification settings" in site administration
And I set the field "email" to "1"
And I press "Save changes"
And I log out
And I log in as "student1"
And I open messaging
And I open messaging settings preferences
Then I should see "Notification preferences"
And I should see "Email"
And the field "Email" matches value "1"
And I set the field "Email" to "0"
And I follow "Preferences" in the user menu
And I click on "Message preferences" "link"
And the field "Email" matches value "0"
Scenario: Disable email notifications for Assignment notifications
Given I log in as "admin"
When I navigate to "Messaging > Notification settings" in site administration
And I set the field "email" to "1"
And I press "Save changes"
Then the field "email" matches value "1"
And I set the field "mod_assign_assign_notification_disable" to "0"
And I press "Save changes"
And the field "mod_assign_assign_notification_disable" matches value "0"
And I follow "Preferences" in the user menu
And I click on "Notification preferences" "link" in the "#page-content" "css_element"
And I should not see "Assignment notifications"
Scenario: User can disable email notifications for Assignment notifications
Given I log in as "admin"
And I navigate to "Messaging > Notification settings" in site administration
And I set the field "email" to "1"
And I press "Save changes"
And I follow "Preferences" in the user menu
And I click on "Notification preferences" "link" in the "#page-content" "css_element"
And I should not see "Enabled" in the "Assignment notifications" "table_row"
When I set the field "message_provider_mod_assign_assign_notification_email" to "0"
And I reload the page
Then the field "message_provider_mod_assign_assign_notification_email" matches value "0"
And I should not see "Enabled" in the "Assignment notifications" "table_row"
Scenario: Lock email notifications for Forum providers
Given I log in as "admin"
When I navigate to "Messaging > Notification settings" in site administration
And I set the field "email" to "1"
And I press "Save changes"
Then the field "email" matches value "1"
And I set the field "mod_forum_posts_enabled[email]" to "1"
And I set the field "mod_forum_posts_locked[email]" to "1"
And I set the field "mod_forum_digests_enabled[email]" to "0"
And I set the field "mod_forum_digests_locked[email]" to "1"
And I press "Save changes"
And the field "mod_forum_posts_enabled[email]" matches value "1"
And the field "mod_forum_posts_locked[email]" matches value "1"
And the field "mod_forum_digests_enabled[email]" matches value "0"
And the field "mod_forum_digests_locked[email]" matches value "1"
And I follow "Preferences" in the user menu
And I click on "Notification preferences" "link" in the "#page-content" "css_element"
And I should see "Locked on" in the "[data-preference-key=message_provider_mod_forum_posts]" "css_element"
And I should see "Locked off" in the "[data-preference-key=message_provider_mod_forum_digests]" "css_element"
Scenario: User can disable notification preferences
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following config values are set as admin:
| popup_provider_mod_assign_assign_notification_locked | 0 | message |
| message_provider_mod_assign_assign_notification_enabled | popup | message |
And the following "user preferences" exist:
| user | preference | value |
| student1 | message_provider_mod_assign_assign_notification_enabled | none |
| student2 | message_provider_mod_assign_assign_notification_enabled | popup |
And the following "activity" exists:
| activity | assign |
| course | C1 |
| name | Test assignment name |
| assignsubmission_onlinetext_enabled | 1 |
| assignsubmission_file_enabled | 0 |
| submissiondrafts | 0 |
# This should generate a notification.
And the following "mod_assign > submissions" exist:
| assign | user | onlinetext |
| Test assignment name | student1 | I'm the student1 submission |
| Test assignment name | student2 | I'm the student2 submission |
When I log in as "student1"
# Confirm the popover is not showing any unread notifications.
Then I should not see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element"
# Open the popover.
And I open the notification popover
# Confirm the submission notification is NOT visible.
And I should not see "You have submitted your assignment submission for Test assignment name" in the "#nav-notification-popover-container" "css_element"
And I log in as "student2"
# Confirm the popover is showing the unread notifications.
Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element"
# Open the popover.
And I open the notification popover
# Confirm the submission notification is visible.
And I should see "You have submitted your assignment submission for Test assignment name" in the "#nav-notification-popover-container" "css_element"
Scenario: User cannot disable forced notification preferences
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following config values are set as admin:
| popup_provider_mod_assign_assign_notification_locked | 1 | message |
| message_provider_mod_assign_assign_notification_enabled | popup | message |
And the following "user preferences" exist:
| user | preference | value |
| student1 | message_provider_mod_assign_assign_notification_enabled | none |
And the following "activity" exists:
| activity | assign |
| course | C1 |
| name | Test assignment name |
| assignsubmission_onlinetext_enabled | 1 |
| assignsubmission_file_enabled | 0 |
| submissiondrafts | 0 |
# This should generate a notification.
And the following "mod_assign > submissions" exist:
| assign | user | onlinetext |
| Test assignment name | student1 | I'm the student1 submission |
When I log in as "student1"
# Confirm the popover is saying 1 unread notifications.
Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element"
# Open the popover.
And I open the notification popover
# Confirm the submission notification is visible.
And I should see "You have submitted your assignment submission for Test assignment name" in the "#nav-notification-popover-container" "css_element"
Scenario: User cannot disable disallowed notification preferences
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following config values are set as admin:
| popup_provider_mod_assign_assign_notification_locked | 1 | message |
| message_provider_mod_assign_assign_notification_enabled | none | message |
And the following "user preferences" exist:
| user | preference | value |
| student1 | message_provider_mod_assign_assign_notification_enabled | popup |
And the following "activity" exists:
| activity | assign |
| course | C1 |
| name | Test assignment name |
| assignsubmission_onlinetext_enabled | 1 |
| assignsubmission_file_enabled | 0 |
| submissiondrafts | 0 |
# This should generate a notification.
And the following "mod_assign > submissions" exist:
| assign | user | onlinetext |
| Test assignment name | student1 | I'm the student1 submission |
When I log in as "student1"
# Confirm the popover is not showing any unread notifications.
Then I should not see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element"
# Open the popover.
And I open the notification popover
# Confirm the submission notification is NOT visible.
And I should not see "You have submitted your assignment submission for Test assignment name" in the "#nav-notification-popover-container" "css_element"
@@ -0,0 +1,119 @@
@core @core_message @javascript
Feature: Manage preferences
In order to control whether I'm contactable
As a user
I need to be able to update my messaging preferences
Background:
# Note: This course is using separate groups mode.
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
| student4 | Student | 4 | student4@example.com |
| student5 | Student | 5 | student5@example.com |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | enablemessaging |
| Group 1 | C1 | G1 | 1 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student4 | G1 |
And the following "message contacts" exist:
| user | contact |
| student1 | student2 |
And the following config values are set as admin:
| messaging | 1 |
| messagingallusers | 1 |
| messagingminpoll | 1 |
# Recipient has 'My contacts only' set.
Scenario: Allow sending a message when you are a contact
Given I log in as "student1"
And I open messaging
And I open messaging settings preferences
When I click on "//label[text()[contains(.,'My contacts only')]]" "xpath_element"
And I log out
Then I log in as "student2"
And I open messaging
And I send "Hi!" message to "Student 1" user
And I should see "Hi!" in the "//*[@data-region='message-drawer']//div[@data-region='content-message-container']" "xpath_element"
# Recipient has 'My contacts and anyone in my courses' set.
Scenario: Disallow sending a message if you are neither contacts with the recipient nor do you share a course
Given I log in as "student1"
And I open messaging
And I open messaging settings preferences
When I click on "//label[text()[contains(.,'My contacts and anyone in my courses')]]" "xpath_element"
And I log out
Then I log in as "student5"
And I open messaging
And I search for "Student 1" in messaging
And I should see "No results"
# Recipient has 'My contacts and anyone in my courses' set.
Scenario: Allow sending a message if you share a group in a shared course
Given I log in as "student1"
And I open messaging
And I open messaging settings preferences
When I click on "//label[text()[contains(.,'My contacts and anyone in my courses')]]" "xpath_element"
And I log out
Then I log in as "student4"
And I open messaging
And I send "Hi!" message to "Student 1" user
And I should see "Hi!" in the "//*[@data-region='message-drawer']//div[@data-region='content-message-container']" "xpath_element"
# Recipient has 'My contacts and anyone in my courses' set.
Scenario: Disallow sending a message if you are neither a contact, nor are in the same group in a shared course
Given I log in as "student1"
And I open messaging
And I open messaging settings preferences
When I click on "//label[text()[contains(.,'My contacts and anyone in my courses')]]" "xpath_element"
And I log out
Then I log in as "student3"
And I open messaging
And I search for "Student 1" in messaging
And I should see "No results"
# Recipient has 'Anyone on the site' set. Only users whose profiles are visible can be found via the search.
Scenario: Disallow sending a message if you are neither a contact nor do you share a course with the user.
Given I log in as "student1"
And I open messaging
And I open messaging settings preferences
When I click on "//label[text()[contains(.,'Anyone on the site')]]" "xpath_element"
And I log out
Then I log in as "student5"
And I open messaging
And I search for "Student 1" in messaging
And I should see "No results"
Scenario: Sending a message when 'User enter to send' is enabled
Given I log in as "student1"
And I open messaging
And I select "Student 2" user in messaging
And I set the field with xpath "//textarea[@data-region='send-message-txt']" to "Hi!"
And I press the enter key
Then I should see "Hi!" in the "//*[@data-region='message-drawer']//div[@data-region='content-message-container']" "xpath_element"
Scenario: Sending a message after 'Use enter to send' is disabled
Given I log in as "student1"
And I open messaging
And I open messaging settings preferences
When I click on "//label[text()[contains(.,'Use enter to send')]]" "xpath_element"
And I go back in "view-settings" message drawer
Then I select "Student 2" user in messaging
And I set the field with xpath "//textarea[@data-region='send-message-txt']" to "Hi!"
And I press the enter key
And I should not see "Hi!" in the "//*[@data-region='message-drawer']//div[@data-region='content-message-container']" "xpath_element"
And I press "Send message"
And I should see "Hi!" in the "//*[@data-region='message-drawer']//div[@data-region='content-message-container']" "xpath_element"
@@ -0,0 +1,29 @@
@core @core_message
Feature: To be able to see and save user message preferences as admin
As an admin
I need to be able to view and edit message preferences for other users
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@emample.com |
And the following "user preferences" exist:
| user | preference | value |
| student1 | message_provider_moodle_instantmessage_enabled | email |
@javascript
Scenario: As an admin I can view and edit message preferences for a user
Given I log in as "admin"
And I navigate to "Messaging > Notification settings" in site administration
And I set the field "email" to "1"
And I press "Save changes"
And I am on the "student1" "user > profile" page
And I click on "Preferences" "link" in the "#region-main-box" "css_element"
And I click on "Message preferences" "link" in the "#region-main-box" "css_element"
And I should not see "Enabled" in the "Email" "table_row"
And I click on "//div[@class='preference-state']" "xpath_element"
And I log out
And I log in as "student1"
And I follow "Preferences" in the user menu
And I click on "Message preferences" "link"
And the field "Email" matches value "0"
@@ -0,0 +1,87 @@
@core @core_message @javascript
Feature: Message send messages
In order to communicate with fellow users
As a user
I need to be able to send a message
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | enablemessaging |
| Group 1 | C1 | G1 | 1 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G1 |
And the following config values are set as admin:
| messaging | 1 |
| messagingminpoll | 1 |
Scenario: Send a message to a group conversation
Given I log in as "student1"
And I open messaging
And I open the "Group" conversations list
And "Group 1" "core_message > Message" should exist
And I select "Group 1" conversation in messaging
When I send "Hi!" message in the message area
Then I should see "Hi!" in the "Group 1" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Group 1" "core_message > Message conversation"
And I log out
And I log in as "student2"
And I open messaging
And "Group 1" "core_message > Message" should exist
And I select "Group 1" conversation in messaging
And I should see "Hi!" in the "Group 1" "core_message > Message conversation"
Scenario: Send a message to a starred conversation
Given I log in as "student1"
When I open messaging
And I open the "Group" conversations list
Then "Group 1" "core_message > Message" should exist
And I select "Group 1" conversation in the "group-messages" conversations list
And I open contact menu
And I click on "Star conversation" "link" in the "conversation-actions-menu" "region"
And I go back in "view-conversation" message drawer
And I open the "Starred" conversations list
And I should see "Group 1"
And I select "Group 1" conversation in the "favourites" conversations list
And I send "Hi!" message in the message area
And I should see "Hi!" in the "Group 1" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Group 1" "core_message > Message conversation"
And I go back in "view-conversation" message drawer
And I open the "Group" conversations list
And I should not see "Group 1" in the "Group" "core_message > Message tab"
Scenario: Send a message to a private conversation via contact tab
Given the following "message contacts" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I click on "Contacts" "link"
And I click on "Student 2" "link" in the "//*[@data-section='contacts']" "xpath_element"
When I send "Hi!" message in the message area
Then I should see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
Scenario: Try to send a message to a private conversation is not contact but you are allowed to send a message
Given I log in as "student1"
And I open messaging
When I send "Hi!" message to "Student 2" user
Then I should see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 2" "core_message > Message conversation"
And I log out
And I log in as "student2"
And I open messaging
And I select "Student 1" conversation in messaging
And I should see "Hi!" in the "Student 1" "core_message > Message conversation"
@@ -0,0 +1,94 @@
@core @core_message @javascript
Feature: Mute and unmute conversations
In order to manage my conversations
As a user
I need to be able to mute and unmute conversations
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | enablemessaging |
| Group 1 | C1 | G1 | 1 |
And the following "group members" exist:
| user | group | course |
| student1 | G1 | C1 |
| student2 | G1 | C1 |
And the following config values are set as admin:
| messaging | 1 |
And the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
Scenario: Mute a group conversation
Given I log in as "student1"
When I open messaging
And I open the "Group" conversations list
Then "Group 1" "core_message > Message" should exist
And "muted" "icon_container" in the "Group 1" "core_message > Message" should not be visible
And I select "Group 1" conversation in messaging
And "muted" "icon_container" in the "Group 1" "core_message > Message header" should not be visible
And I open contact menu
And I click on "Mute" "link" in the "conversation-actions-menu" "region"
And "muted" "icon_container" in the "Group 1" "core_message > Message header" should be visible
And I go back in "view-conversation" message drawer
And "muted" "icon_container" in the "Group 1" "core_message > Message" should be visible
Scenario: Mute a private conversation
When I log in as "student1"
And I open messaging
Then I should see "Private"
And I open the "Private" conversations list
And I should see "Student 2"
And "muted" "icon_container" in the "Student 2" "core_message > Message" should not be visible
And I select "Student 2" conversation in messaging
And "muted" "icon_container" in the "[data-action='view-contact']" "css_element" should not be visible
And I open contact menu
And I click on "Mute" "link" in the "conversation-actions-menu" "region"
And "muted" "icon_container" in the "[data-action='view-contact']" "css_element" should be visible
And I go back in "view-conversation" message drawer
And "muted" "icon_container" in the "Student 2" "core_message > Message" should be visible
Scenario: Unmute a group conversation
Given the following "muted group conversations" exist:
| user | group | course |
| student1 | G1 | C1 |
When I log in as "student1"
And I open messaging
And I open the "Group" conversations list
Then "Group 1" "core_message > Message" should exist
And "muted" "icon_container" in the "Group 1" "core_message > Message" should be visible
And I select "Group 1" conversation in messaging
And "muted" "icon_container" in the "Group 1" "core_message > Message header" should be visible
And I open contact menu
And I click on "Unmute" "link" in the "conversation-actions-menu" "region"
And "muted" "icon_container" in the "Group 1" "core_message > Message header" should not be visible
And I go back in "view-conversation" message drawer
And "muted" "icon_container" in the "Group 1" "core_message > Message" should not be visible
Scenario: Unmute a private conversation
Given the following "muted private conversations" exist:
| user | contact |
| student1 | student2 |
When I log in as "student1"
And I open messaging
Then I should see "Private"
And I open the "Private" conversations list
And I should see "Student 2"
And "muted" "icon_container" in the "Student 2" "core_message > Message" should be visible
And I select "Student 2" conversation in messaging
And "muted" "icon_container" in the "[data-action='view-contact']" "css_element" should be visible
And I open contact menu
And I click on "Unmute" "link" in the "conversation-actions-menu" "region"
And "muted" "icon_container" in the "[data-action='view-contact']" "css_element" should not be visible
And I go back in "view-conversation" message drawer
And "muted" "icon_container" in the "Student 2" "core_message > Message" should not be visible
@@ -0,0 +1,67 @@
@core @core_message @javascript
Feature: Self conversation
In order to have self-conversations
As a user
I need to be able to send messages to myself and read them
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following config values are set as admin:
| messaging | 1 |
| messagingminpoll | 1 |
Scenario: Self conversation exists
Given I log in as "student1"
When I open messaging
Then "Student 1" "core_message > Message" should exist
And I select "Student" conversation in messaging
And I should see "Personal space"
Scenario: Self conversation can be unstarred
Given I log in as "student1"
When I open messaging
Then "Student 1" "core_message > Message" should exist
And I select "Student" conversation in messaging
And I open contact menu
And I click on "Unstar" "link" in the "Student 1" "core_message > Message header"
And I go back in "view-conversation" message drawer
And I open the "Starred" conversations list
And I should not see "Student 1" in the "favourites" "core_message > Message list area"
And I open the "Private" conversations list
And I should see "Student 1" in the "messages" "core_message > Message list area"
Scenario: Self conversation can be deleted
Given I log in as "student1"
When I open messaging
Then "Student 1" "core_message > Message" should exist
And I select "Student 1" conversation in messaging
And I open contact menu
And I click on "Delete conversation" "link" in the "Student 1" "core_message > Message header"
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-conversation']" "xpath_element"
And I should not see "Delete"
And I go back in "view-conversation" message drawer
And I open the "Starred" conversations list
And I should not see "Student 1" in the "favourites" "core_message > Message list area"
And I open the "Private" conversations list
And I should not see "Student 1" in the "messages" "core_message > Message list area"
Scenario: Send a message to a self-conversation via message drawer
Given I log in as "student1"
When I open messaging
Then "Student 1" "core_message > Message" should exist
And I select "Student 1" conversation in messaging
And I send "Hi!" message in the message area
And I should see "Hi!" in the "Student 1" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 1" "core_message > Message conversation"
Scenario: Send a message to a self-conversation via user profile
Given I log in as "student1"
When I follow "Profile" in the user menu
Then I should see "Message"
And I press "Message"
And I send "Hi!" message in the message area
And I should see "Hi!" in the "Student 1" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "Student 1" "core_message > Message conversation"
@@ -0,0 +1,87 @@
@core @core_message @javascript
Feature: Unread messages
In order to know how many unread messages I have
As a user
I need to be able to view an unread message
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | enablemessaging |
| New group | C1 | NG | 1 |
And the following "group members" exist:
| user | group |
| student1 | NG |
| student2 | NG |
And the following config values are set as admin:
| messaging | 1 |
| messagingminpoll | 1 |
Scenario: Unread messages for group conversation
Given I log in as "student1"
When I open messaging
And I open the "Group" conversations list
Then "New group" "core_message > Message" should exist
And I select "New group" conversation in messaging
And I send "Hi!" message in the message area
And I should see "Hi!" in the "New group" "core_message > Message conversation"
And I should see "##today##%d %B##" in the "New group" "core_message > Message conversation"
And I log out
And I log in as "student2"
And I should see "1" in the "//*[@title='Toggle messaging drawer']/../*[@data-region='count-container']" "xpath_element"
And I open messaging
And I should see "1" in the "Group" "core_message > Message tab"
And "New group" "core_message > Message" should exist
And I should see "1" in the "New group" "core_message > Message"
And I select "New group" conversation in messaging
And I should see "Hi!" in the "New group" "core_message > Message conversation"
And I should not see "1" in the "//*[@title='Toggle messaging drawer']/../*[@data-region='count-container']" "xpath_element"
And I should not see "1" in the "Group" "core_message > Message tab"
And I should not see "1" in the "New group" "core_message > Message"
Scenario: Unread messages for private conversation
Given the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
| student2 | student1 | What do you need? |
When I log in as "student1"
Then I should see "1" in the "//*[@title='Toggle messaging drawer']/../*[@data-region='count-container']" "xpath_element"
And I open messaging
And I should see "1" in the "Private" "core_message > Message tab"
And "Student 2" "core_message > Message" should exist
And I should see "1" in the "Student 2" "core_message > Message"
And I select "Student 2" conversation in messaging
And I should see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should not see "1" in the "//*[@title='Toggle messaging drawer']/../*[@data-region='count-container']" "xpath_element"
And I should not see "1" in the "Private" "core_message > Message tab"
And I should not see "1" in the "Student 2" "core_message > Message"
Scenario: Unread messages for starred conversation
Given the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
| student2 | student1 | What do you need? |
And the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
When I log in as "student1"
Then I should see "1" in the "//*[@title='Toggle messaging drawer']/../*[@data-region='count-container']" "xpath_element"
And I open messaging
And I should see "1" in the "Starred" "core_message > Message tab"
And "Student 2" "core_message > Message" should exist
And I should see "1" in the "Student 2" "core_message > Message"
And I select "Student 2" conversation in messaging
And I should see "Hi!" in the "Student 2" "core_message > Message conversation"
And I should not see "1" in the "//*[@title='Toggle messaging drawer']/../*[@data-region='count-container']" "xpath_element"
And I should not see "1" in the "Starred" "core_message > Message tab"
And I should not see "1" in the "Student 2" "core_message > Message"
+578
View File
@@ -0,0 +1,578 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Events tests.
*
* @package core_message
* @category test
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_message\event;
use core_message\tests\helper as testhelper;
/**
* Class containing the tests for message related events.
*
* @package core_message
* @category test
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class events_test extends \advanced_testcase {
/**
* Test the message contact added event.
*/
public function test_message_contact_added(): void {
global $USER;
$this->resetAfterTest();
// Set this user as the admin.
$this->setAdminUser();
// Create a user to add to the admin's contact list.
$user = $this->getDataGenerator()->create_user();
// Trigger and capture the event when adding a contact.
$sink = $this->redirectEvents();
\core_message\api::add_contact($USER->id, $user->id);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_contact_added', $event);
$this->assertEquals(\context_user::instance(2), $event->get_context());
$url = new \moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
$this->assertEquals($url, $event->get_url());
}
/**
* Test the message contact removed event.
*/
public function test_message_contact_removed(): void {
global $USER;
$this->resetAfterTest();
// Set this user as the admin.
$this->setAdminUser();
// Create a user to add to the admin's contact list.
$user = $this->getDataGenerator()->create_user();
// Add the user to the admin's contact list.
\core_message\api::add_contact($USER->id, $user->id);
// Trigger and capture the event when adding a contact.
$sink = $this->redirectEvents();
\core_message\api::remove_contact($USER->id, $user->id);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_contact_removed', $event);
$this->assertEquals(\context_user::instance(2), $event->get_context());
$url = new \moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
$this->assertEquals($url, $event->get_url());
}
/**
* Test the message user blocked event.
*/
public function test_message_user_blocked(): void {
global $USER;
$this->resetAfterTest();
// Set this user as the admin.
$this->setAdminUser();
// Create a user to add to the admin's contact list.
$user = $this->getDataGenerator()->create_user();
// Add the user to the admin's contact list.
\core_message\api::add_contact($USER->id, $user->id);
// Trigger and capture the event when blocking a contact.
$sink = $this->redirectEvents();
\core_message\api::block_user($USER->id, $user->id);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_user_blocked', $event);
$this->assertEquals(\context_user::instance(2), $event->get_context());
}
/**
* Test the message user unblocked event.
*/
public function test_message_user_unblocked(): void {
global $USER;
$this->resetAfterTest();
// Set this user as the admin.
$this->setAdminUser();
// Create a user to add to the admin's contact list.
$user = $this->getDataGenerator()->create_user();
// Add the user to the admin's contact list.
\core_message\api::add_contact($USER->id, $user->id);
// Block the user.
\core_message\api::block_user($USER->id, $user->id);
// Make sure that we have 1 blocked user.
$this->assertEquals(1, \core_message\api::count_blocked_users());
// Trigger and capture the event when unblocking a contact.
$sink = $this->redirectEvents();
\core_message\api::unblock_user($USER->id, $user->id);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_user_unblocked', $event);
$this->assertEquals(\context_user::instance(2), $event->get_context());
// Make sure that we have no blocked users.
$this->assertEmpty(\core_message\api::count_blocked_users());
}
/**
* Test the message sent event.
*
* We can not use the message_send() function in the unit test to check that the event was fired as there is a
* conditional check to ensure a fake message is sent during unit tests when calling that particular function.
*/
public function test_message_sent(): void {
$event = \core\event\message_sent::create(array(
'objectid' => 3,
'userid' => 1,
'context' => \context_system::instance(),
'relateduserid' => 2,
'other' => array(
'courseid' => 4
)
));
// Trigger and capturing the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_sent', $event);
$this->assertEquals(\context_system::instance(), $event->get_context());
$url = new \moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
$this->assertEquals($url, $event->get_url());
$this->assertEquals(3, $event->objectid);
$this->assertEquals(4, $event->other['courseid']);
}
public function test_mesage_sent_without_other_courseid(): void {
// Creating a message_sent event without other[courseid] leads to exception.
$this->expectException('coding_exception');
$this->expectExceptionMessage('The \'courseid\' value must be set in other');
$event = \core\event\message_sent::create(array(
'userid' => 1,
'context' => \context_system::instance(),
'relateduserid' => 2,
'other' => array(
'messageid' => 3,
)
));
}
public function test_mesage_sent_via_create_from_ids(): void {
$this->resetAfterTest();
// Containing courseid.
$event = \core\event\message_sent::create_from_ids(1, 2, 3, 4);
// Trigger and capturing the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_sent', $event);
$this->assertEquals(\context_system::instance(), $event->get_context());
$url = new \moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
$this->assertEquals($url, $event->get_url());
$this->assertEquals(3, $event->objectid);
$this->assertEquals(4, $event->other['courseid']);
}
/**
* Test the group message sent event.
*
* We can't test events in any testing of the message_send() function as there is a conditional PHPUNIT check in message_send,
* resulting in fake messages being generated and captured under test. As a result, none of the events code, nor message
* processor code is called during testing.
*/
public function test_group_message_sent(): void {
$this->resetAfterTest();
$event = \core\event\group_message_sent::create([
'objectid' => 3,
'userid' => 1,
'context' => \context_system::instance(),
'other' => [
'courseid' => 4,
'conversationid' => 54
]
]);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\group_message_sent', $event);
$this->assertEquals(\context_system::instance(), $event->get_context());
$url = new \moodle_url('/message/index.php');
$this->assertEquals($url, $event->get_url());
$this->assertEquals(3, $event->objectid);
$this->assertEquals(4, $event->other['courseid']);
$this->assertEquals(54, $event->other['conversationid']);
}
/**
* Test the group message sent event when created without a courseid.
*/
public function test_group_message_sent_without_other_courseid(): void {
// Creating a message_sent event without other[courseid] leads to exception.
$this->expectException('coding_exception');
$this->expectExceptionMessage('The \'courseid\' value must be set in other');
$event = \core\event\group_message_sent::create([
'userid' => 1,
'objectid' => 3,
'context' => \context_system::instance(),
'relateduserid' => 2,
'other' => [
'conversationid' => 34
]
]);
}
/**
* Test the group message sent event when created without a conversationid.
*/
public function test_group_message_sent_without_other_conversationid(): void {
// Creating a message_sent event without other[courseid] leads to exception.
$this->expectException('coding_exception');
$this->expectExceptionMessage('The \'conversationid\' value must be set in other');
$event = \core\event\group_message_sent::create([
'userid' => 1,
'objectid' => 3,
'context' => \context_system::instance(),
'relateduserid' => 2,
'other' => [
'courseid' => 44,
]
]);
}
/**
* Test the group message sent event using the create_from_ids() method.
*/
public function test_group_message_sent_via_create_from_ids(): void {
$this->resetAfterTest();
// Fields are: userfromid, conversationid, messageid, courseid.
$event = \core\event\group_message_sent::create_from_ids(1, 2, 3, 4);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\group_message_sent', $event);
$this->assertEquals(\context_system::instance(), $event->get_context());
$this->assertEquals(new \moodle_url('/message/index.php'), $event->get_url());
$this->assertEquals(1, $event->userid);
$this->assertEquals(2, $event->other['conversationid']);
$this->assertEquals(3, $event->objectid);
$this->assertEquals(4, $event->other['courseid']);
}
/**
* Test the message viewed event.
*/
public function test_message_viewed(): void {
global $DB;
$this->resetAfterTest();
// Create users to send messages between.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$messageid = testhelper::send_fake_message($user1, $user2);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$message = $DB->get_record('messages', ['id' => $messageid]);
\core_message\api::mark_message_as_read($user1->id, $message);
$events = $sink->get_events();
$event = reset($events);
// Get the usage action.
$mua = $DB->get_record('message_user_actions', ['userid' => $user1->id, 'messageid' => $messageid,
'action' => \core_message\api::MESSAGE_ACTION_READ]);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_viewed', $event);
$this->assertEquals(\context_user::instance($user1->id), $event->get_context());
$this->assertEquals($mua->id, $event->objectid);
$this->assertEquals($messageid, $event->other['messageid']);
$url = new \moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
$this->assertEquals($url, $event->get_url());
}
/**
* Test the message deleted event.
*/
public function test_message_deleted(): void {
global $DB, $USER;
$this->resetAfterTest();
$this->setAdminUser();
// Create users to send messages between.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$messageid = testhelper::send_fake_message($user1, $user2);
// Trigger and capture the event.
$sink = $this->redirectEvents();
\core_message\api::delete_message($user1->id, $messageid);
$events = $sink->get_events();
$event = reset($events);
// Get the usage action.
$mua = $DB->get_record('message_user_actions', ['userid' => $user1->id, 'messageid' => $messageid,
'action' => \core_message\api::MESSAGE_ACTION_DELETED]);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_deleted', $event);
$this->assertEquals($USER->id, $event->userid); // The user who deleted it.
$this->assertEquals($user1->id, $event->relateduserid);
$this->assertEquals($mua->id, $event->objectid);
$this->assertEquals($messageid, $event->other['messageid']);
$this->setUser($user1);
// Create a read message.
$messageid = testhelper::send_fake_message($user1, $user2);
$m = $DB->get_record('messages', ['id' => $messageid]);
\core_message\api::mark_message_as_read($user2->id, $m);
// Trigger and capture the event.
$sink = $this->redirectEvents();
\core_message\api::delete_message($user2->id, $messageid);
$events = $sink->get_events();
$event = reset($events);
// Get the usage action.
$mua = $DB->get_record('message_user_actions', ['userid' => $user2->id, 'messageid' => $messageid,
'action' => \core_message\api::MESSAGE_ACTION_DELETED]);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\message_deleted', $event);
$this->assertEquals($user1->id, $event->userid);
$this->assertEquals($user2->id, $event->relateduserid);
$this->assertEquals($mua->id, $event->objectid);
$this->assertEquals($messageid, $event->other['messageid']);
}
/**
* Test the message deleted event is fired when deleting a conversation.
*/
public function test_message_deleted_whole_conversation(): void {
global $DB;
$this->resetAfterTest();
// Create some users.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
// The person doing the deletion.
$this->setUser($user1);
// Send some messages back and forth.
$time = 1;
$messages = [];
$messages[] = testhelper::send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
$messages[] = testhelper::send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
$messages[] = testhelper::send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
$messages[] = testhelper::send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
$messages[] = testhelper::send_fake_message($user1, $user2, 'You doing much?', 0, $time + 5);
$messages[] = testhelper::send_fake_message($user2, $user1, 'Nah', 0, $time + 6);
$messages[] = testhelper::send_fake_message($user1, $user2, 'You nubz0r!', 0, $time + 7);
$messages[] = testhelper::send_fake_message($user2, $user1, 'Ouch.', 0, $time + 8);
// Mark the last 4 messages as read.
$m5 = $DB->get_record('messages', ['id' => $messages[4]]);
$m6 = $DB->get_record('messages', ['id' => $messages[5]]);
$m7 = $DB->get_record('messages', ['id' => $messages[6]]);
$m8 = $DB->get_record('messages', ['id' => $messages[7]]);
\core_message\api::mark_message_as_read($user2->id, $m5);
\core_message\api::mark_message_as_read($user1->id, $m6);
\core_message\api::mark_message_as_read($user2->id, $m7);
\core_message\api::mark_message_as_read($user1->id, $m8);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
\core_message\api::delete_conversation_by_id($user1->id, $conversationid);
$events = $sink->get_events();
// Get the user actions for the messages deleted by that user.
$muas = $DB->get_records('message_user_actions', ['userid' => $user1->id,
'action' => \core_message\api::MESSAGE_ACTION_DELETED], 'timecreated ASC');
$this->assertCount(8, $muas);
// Create a list we can use for testing.
$muatest = [];
foreach ($muas as $mua) {
$muatest[$mua->messageid] = $mua;
}
// Check that there were the correct number of events triggered.
$this->assertEquals(8, count($events));
// Check that the event data is valid.
$i = 1;
foreach ($events as $event) {
$messageid = $messages[$i - 1];
$this->assertInstanceOf('\core\event\message_deleted', $event);
$this->assertEquals($muatest[$messageid]->id, $event->objectid);
$this->assertEquals($user1->id, $event->userid);
$this->assertEquals($user1->id, $event->relateduserid);
$this->assertEquals($messageid, $event->other['messageid']);
$i++;
}
}
/**
* Test the notification sent event.
*/
public function test_notification_sent(): void {
$this->resetAfterTest();
// Create a course.
$course = $this->getDataGenerator()->create_course();
// Create users to send notification between.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
// Send a notification.
$notificationid = testhelper::send_fake_message($user1, $user2, 'Hello world!', 1);
// Containing courseid.
$event = \core\event\notification_sent::create_from_ids($user1->id, $user2->id, $notificationid, $course->id);
// Trigger and capturing the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\notification_sent', $event);
$this->assertEquals($notificationid, $event->objectid);
$this->assertEquals($user1->id, $event->userid);
$this->assertEquals($user2->id, $event->relateduserid);
$this->assertEquals(\context_system::instance(), $event->get_context());
$this->assertEquals($course->id, $event->other['courseid']);
$url = new \moodle_url('/message/output/popup/notifications.php', array('notificationid' => $event->objectid));
$this->assertEquals($url, $event->get_url());
}
/**
* Test the notification sent event when null passed as course.
*/
public function test_notification_sent_with_null_course(): void {
$event = \core\event\notification_sent::create_from_ids(1, 1, 1, null);
$this->resetAfterTest();
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\notification_sent', $event);
$this->assertEquals(SITEID, $event->other['courseid']);
}
/**
* Test the notification viewed event.
*/
public function test_notification_viewed(): void {
global $DB;
$this->resetAfterTest();
// Create users to send notifications between.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
// Send a notification.
$notificationid = testhelper::send_fake_message($user1, $user2, 'Hello world!', 1);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$notification = $DB->get_record('notifications', ['id' => $notificationid]);
\core_message\api::mark_notification_as_read($notification);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\core\event\notification_viewed', $event);
$this->assertEquals($notificationid, $event->objectid);
$this->assertEquals($user2->id, $event->userid);
$this->assertEquals($user1->id, $event->relateduserid);
$this->assertEquals(\context_user::instance($user2->id), $event->get_context());
$url = new \moodle_url('/message/output/popup/notifications.php', array('notificationid' => $event->objectid));
$this->assertEquals($url, $event->get_url());
}
}
@@ -0,0 +1,90 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_message\external;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
use core_external\external_api;
use externallib_advanced_testcase;
use \core_message\tests\helper as testhelper;
/**
* External function test for get_unread_notification_count.
*
* @package core_message
* @category test
* @copyright 2021 Dani Palou <dani@moodle.com>, based on Ryan Wyllie <ryan@moodle.com> code
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.0
*/
class get_unread_notification_count_test extends externallib_advanced_testcase {
/**
* get_unread_notification should throw an exception for an invalid user.
*/
public function test_get_unread_notification_count_invalid_user_exception(): void {
$this->resetAfterTest(true);
$this->expectException('moodle_exception');
$result = get_unread_notification_count::execute(-2132131);
}
/**
* get_unread_notification_count should throw exception if being requested for non-logged in user.
*/
public function test_get_unread_notification_count_access_denied_exception(): void {
$this->resetAfterTest(true);
$sender = $this->getDataGenerator()->create_user();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException('moodle_exception');
$result = get_unread_notification_count::execute($sender->id);
}
/**
* Test get_unread_notification_count.
*/
public function test_get_unread_notification_count(): void {
$this->resetAfterTest(true);
$sender1 = $this->getDataGenerator()->create_user();
$sender2 = $this->getDataGenerator()->create_user();
$sender3 = $this->getDataGenerator()->create_user();
$recipient = $this->getDataGenerator()->create_user();
$this->setUser($recipient);
$notificationids = [
testhelper::send_fake_unread_notification($sender1, $recipient, 'Notification', 1),
testhelper::send_fake_unread_notification($sender1, $recipient, 'Notification', 2),
testhelper::send_fake_unread_notification($sender2, $recipient, 'Notification', 3),
testhelper::send_fake_unread_notification($sender2, $recipient, 'Notification', 4),
testhelper::send_fake_unread_notification($sender3, $recipient, 'Notification', 5),
testhelper::send_fake_unread_notification($sender3, $recipient, 'Notification', 6),
];
$count = get_unread_notification_count::execute($recipient->id);
$this->assertEquals($count, 6);
}
}
File diff suppressed because it is too large Load Diff
+91
View File
@@ -0,0 +1,91 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Fixtures for Inbound Message tests.
*
* @package core_message
* @copyright 2014 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\test;
defined('MOODLE_INTERNAL') || die();
/**
* A base handler for unit testing.
*
* @copyright 2014 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class handler_base extends \core\message\inbound\handler {
/**
* Get the description for unit tests.
*/
public function get_description() {
}
/**
* Get the name for unit tests.
*/
public function get_name() {
}
/**
* Process a message for unit tests.
*
* @param stdClass $record The record to process
* @param stdClass $messagedata The message data
*/
public function process_message(\stdClass $record, \stdClass $messagedata) {
}
}
/**
* A handler for unit testing.
*
* @copyright 2014 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class handler_one extends handler_base {
}
/**
* A handler for unit testing.
*
* @copyright 2014 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class handler_two extends handler_base {
}
/**
* A handler for unit testing.
*
* @copyright 2014 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class handler_three extends handler_base {
}
/**
* A handler for unit testing.
*
* @copyright 2014 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class handler_four extends handler_base {
}
+252
View File
@@ -0,0 +1,252 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_message;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/message/tests/messagelib_test.php');
/**
* Tests for the message helper class.
*
* @package core_message
* @category test
* @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \core_message\helper
*/
class helper_test extends \advanced_testcase {
public function setUp(): void {
$this->resetAfterTest(true);
}
public function test_get_member_info_ordering(): void {
// Create a conversation with several users.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$user4 = self::getDataGenerator()->create_user();
\core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[
$user1->id,
$user2->id,
$user3->id,
$user4->id,
],
'Group conversation'
);
// Verify that the member information comes back in the same order that we specified in the input array.
$memberinfo = \core_message\helper::get_member_info($user1->id, [$user3->id, $user4->id, $user2->id]);
$this->assertEquals($user3->id, array_shift($memberinfo)->id);
$this->assertEquals($user4->id, array_shift($memberinfo)->id);
$this->assertEquals($user2->id, array_shift($memberinfo)->id);
}
/**
* Test search_get_user_details returns the correct profile data when $CFG->messagingallusers is disabled.
*/
public function test_search_get_user_details_sitewide_disabled(): void {
global $DB;
set_config('messagingallusers', false);
// Two students sharing course 1, visible profile within course (no groups).
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$course1 = $this->getDataGenerator()->create_course((object) ['groupmode' => 0]);
$this->getDataGenerator()->enrol_user($user1->id, $course1->id);
$this->getDataGenerator()->enrol_user($user2->id, $course1->id);
// A teacher in course 1.
$user3 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user3->id, $course1->id, 'editingteacher');
// Two students sharing course 2, separate groups (profiles not visible to one another).
// Note: no groups are created here, but separate groups mode alone is enough to restrict profile access.
$user4 = $this->getDataGenerator()->create_user();
$user5 = $this->getDataGenerator()->create_user();
$course2 = $this->getDataGenerator()->create_course((object) ['groupmode' => 1]);
$this->getDataGenerator()->enrol_user($user4->id, $course2->id);
$this->getDataGenerator()->enrol_user($user5->id, $course2->id);
// A teacher in course 2.
$user6 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user6->id, $course2->id, 'editingteacher');
// Teacher and course contact in course 3.
$user7 = $this->getDataGenerator()->create_user();
$course3 = $this->getDataGenerator()->create_course();
$this->getDataGenerator()->enrol_user($user7->id, $course3->id, 'editingteacher');
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
// Make teachers course contacts.
set_config('coursecontact', $teacherrole->id);
// User 1 should be able to see users within their course, but not course contacts or students in other courses.
$this->setUser($user1);
$this->assertNotEmpty(\core_message\helper::search_get_user_details($user2)); // Student in same course.
$this->assertEmpty(\core_message\helper::search_get_user_details($user4)); // Student in another course.
$this->assertNotEmpty(\core_message\helper::search_get_user_details($user3)); // Teacher in same course.
$this->assertEmpty(\core_message\helper::search_get_user_details($user7)); // Teacher (course contact) in another course.
// User 3 should be able to see the teacher in their own course, but not other students in that course nor course contacts
// or students in other courses.
$this->setUser($user4);
$this->assertEmpty(\core_message\helper::search_get_user_details($user5)); // Student in same course.
$this->assertEmpty(\core_message\helper::search_get_user_details($user1)); // Student in another course.
$this->assertNotEmpty(\core_message\helper::search_get_user_details($user6)); // Teacher in same course.
$this->assertEmpty(\core_message\helper::search_get_user_details($user7)); // Teacher (course contact) in another course.
}
/**
* Test search_get_user_details returns the correct profile data we limit the data we wish to be returned.
*/
public function test_search_get_user_details_limited_data(): void {
set_config('messagingallusers', false);
// Two students sharing course 1, visible profile within course (no groups).
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$course1 = $this->getDataGenerator()->create_course((object) ['groupmode' => 0]);
$this->getDataGenerator()->enrol_user($user1->id, $course1->id);
$this->getDataGenerator()->enrol_user($user2->id, $course1->id);
// Calculate the minimum fields that can be returned.
$namefields = \core_user\fields::for_name()->get_required_fields();
$fields = array_intersect($namefields, user_get_default_fields());
$minimaluser = (object) [
'id' => $user2->id,
'deleted' => $user2->deleted,
];
foreach ($namefields as $field) {
$minimaluser->$field = $user2->$field;
}
// Test that less data is returned using the filter.
$this->setUser($user1);
$fulldetails = helper::search_get_user_details($user2);
$limiteddetails = helper::search_get_user_details($minimaluser, $fields);
$fullcount = count($fulldetails);
$limitedcount = count($limiteddetails);
$this->assertLessThan($fullcount, $limitedcount);
$this->assertNotEquals($fulldetails, $limiteddetails);
}
/**
* Test search_get_user_details returns the correct profile data when $CFG->messagingallusers is enabled.
*/
public function test_search_get_user_details_sitewide_enabled(): void {
global $DB;
set_config('messagingallusers', true);
// Two students sharing course 1, visible profile within course (no groups).
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$course1 = $this->getDataGenerator()->create_course((object) ['groupmode' => 0]);
$this->getDataGenerator()->enrol_user($user1->id, $course1->id);
$this->getDataGenerator()->enrol_user($user2->id, $course1->id);
// A teacher in course 1.
$user3 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user3->id, $course1->id, 'editingteacher');
// Two students sharing course 2, separate groups (profiles not visible to one another).
// Note: no groups are created here, but separate groups mode alone is enough to restrict profile access.
$user4 = $this->getDataGenerator()->create_user();
$user5 = $this->getDataGenerator()->create_user();
$course2 = $this->getDataGenerator()->create_course((object) ['groupmode' => 1]);
$this->getDataGenerator()->enrol_user($user4->id, $course2->id);
$this->getDataGenerator()->enrol_user($user5->id, $course2->id);
// A teacher in course 2.
$user6 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user6->id, $course2->id, 'editingteacher');
// Teacher and course contact in course 3.
$user7 = $this->getDataGenerator()->create_user();
$course3 = $this->getDataGenerator()->create_course();
$this->getDataGenerator()->enrol_user($user7->id, $course3->id, 'editingteacher');
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
// Make teachers course contacts.
set_config('coursecontact', $teacherrole->id);
// User 1 should be able to see users within their course and course contacts, but not students in other courses.
$this->setUser($user1);
$this->assertNotEmpty(\core_message\helper::search_get_user_details($user2)); // Student in same course.
$this->assertEmpty(\core_message\helper::search_get_user_details($user4)); // Student in another course.
$this->assertNotEmpty(\core_message\helper::search_get_user_details($user3)); // Teacher in same course.
$this->assertNotEmpty(\core_message\helper::search_get_user_details($user7)); // Teacher (course contact) in another course.
// User 3 should be able to see the teacher in their own course, but not other students in that course nor course contacts
// or students in other courses.
$this->setUser($user4);
$this->assertEmpty(\core_message\helper::search_get_user_details($user5)); // Student in same course.
$this->assertEmpty(\core_message\helper::search_get_user_details($user1)); // Student in another course.
$this->assertNotEmpty(\core_message\helper::search_get_user_details($user6)); // Teacher in same course.
$this->assertNotEmpty(\core_message\helper::search_get_user_details($user7)); // Teacher (course contact) in another course.
}
/**
* Test prevent_unclosed_html_tags returns the correct html.
*
* @dataProvider prevent_unclosed_html_tags_data
* @param string $text text to preview unclosed html tags.
* @param string $goodhtml html good structured.
* @param bool $removebody true if we want to remove tag body.
*/
public function test_prevent_unclosed_html_tags(string $message, string $goodhtml, bool $removebody): void {
$this->setAdminUser();
$html = \core_message\helper::prevent_unclosed_html_tags($message, $removebody);
$this->assertSame($goodhtml, $html);
}
/**
* Data provider for the test_prevent_unclosed_html_tags_data tests.
*
* @return array
*/
public function prevent_unclosed_html_tags_data(): array {
return [
'Prevent unclosed html elements' => [
'<h1>Title</h1><p>Paragraph</p><b>Bold', '<h1>Title</h1><p>Paragraph</p><b>Bold</b>', true
],
'Prevent unclosed html elements including comments' => [
'<h1>Title</h1><p>Paragraph</p><!-- Comments //--><b>Bold', '<h1>Title</h1><p>Paragraph</p><!-- Comments //--><b>Bold</b>', true
],
'Prevent unclosed comments' => ['<h1>Title</h1><p>Paragraph</p><!-- Comments', '<h1>Title</h1><p>Paragraph</p>', true
],
'Prevent unclosed html elements without removing tag body' => [
'<body><h2>Title 2</h2><p>Paragraph</p><b>Bold</body>', '<body><h2>Title 2</h2><p>Paragraph</p><b>Bold</b></body>', false
],
'Empty html' => [
'', '', false
],
'Check encoding UTF-8 is working' => [
'<body><h1>Title</h1><p>السلام عليكم</p></body>', '<body><h1>Title</h1><p>السلام عليكم</p></body>', false
],
];
}
}
+670
View File
@@ -0,0 +1,670 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tests for core_message_inbound to test Variable Envelope Return Path functionality.
*
* @package core_message
* @copyright 2014 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_message;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/fixtures/inbound_fixtures.php');
/**
* Tests for core_message_inbound to test Variable Envelope Return Path functionality.
*
* @package core_message
* @copyright 2014 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class inbound_test extends \advanced_testcase {
/**
* Perform setup tasks generic to each test.
* This includes:
* * configuring the messageinbound_mailbox.
*/
public function setUp(): void {
global $CFG;
$this->resetAfterTest(true);
// Setup the default Inbound Message mailbox settings.
$CFG->messageinbound_domain = 'example.com';
$CFG->messageinbound_enabled = true;
// Must be no longer than 15 characters.
$CFG->messageinbound_mailbox = 'moodlemoodle123';
}
/**
* Helper to create a new Inbound Message handler.
*
* @param $handlerclass The class of the handler to create
* @param $enabled Whether the handler should be enabled
* @param $component The component
* @param $namepace The namepace
*/
public function helper_create_handler($handlerclass, $enabled = true, $component = 'core_test', $namespace = '\\core\\test\\') {
global $DB;
$classname = $namespace . $handlerclass;
$record = \core\message\inbound\manager::record_from_handler(new $classname());
$record->component = $component;
$record->enabled = $enabled;
$record->id = $DB->insert_record('messageinbound_handlers', $record);
$handler = core_message_inbound_test_manager::handler_from_record($record);
return $handler;
}
/**
* Test that the enabled check perform as expected.
*/
public function test_is_enabled(): void {
global $CFG;
// First clear all of the settings set in the setUp.
$CFG->messageinbound_domain = null;
$CFG->messageinbound_enabled = null;
$CFG->messageinbound_mailbox = null;
$this->assertFalse(\core\message\inbound\manager::is_enabled());
// Check whether only setting the enabled flag keeps it disabled.
$CFG->messageinbound_enabled = true;
$this->assertFalse(\core\message\inbound\manager::is_enabled());
// Check that the mailbox entry on it's own does not enable Inbound Message handling.
$CFG->messageinbound_mailbox = 'moodlemoodle123';
$CFG->messageinbound_domain = null;
$this->assertFalse(\core\message\inbound\manager::is_enabled());
// And that the domain on it's own does not.
$CFG->messageinbound_domain = 'example.com';
$CFG->messageinbound_mailbox = null;
$this->assertFalse(\core\message\inbound\manager::is_enabled());
// And that an invalid mailbox does not.
$CFG->messageinbound_mailbox = '';
$CFG->messageinbound_domain = 'example.com';
$this->assertFalse(\core\message\inbound\manager::is_enabled());
// And that an invalid domain does not.
$CFG->messageinbound_domain = '';
$CFG->messageinbound_mailbox = 'moodlemoodle123';
$this->assertFalse(\core\message\inbound\manager::is_enabled());
// Finally a test that ensures that all settings correct enables the system.
$CFG->messageinbound_mailbox = 'moodlemoodle123';
$CFG->messageinbound_domain = 'example.com';
$CFG->messageinbound_enabled = true;
$this->assertTrue(\core\message\inbound\manager::is_enabled());
}
/**
* Test that data items conform to RFCs 5231, and 5322 standards for
* addressing, and to RFC 5233 for sub-addressing.
*/
public function test_address_constraints(): void {
$handler = $this->helper_create_handler('handler_one');
// Using the handler created, generate an address for our data entry.
$processor = new core_message_inbound_test_helper();
$processor->set_handler($handler->classname);
// Generate some IDs for the data and generate addresses for them.
$dataids = array(
-1,
0,
42,
1073741823,
2147483647,
);
$user = $this->getDataGenerator()->create_user();
foreach ($dataids as $dataid) {
$processor->set_data($dataid);
$address = $processor->generate($user->id);
$this->assertNotNull($address);
$this->assertTrue(strlen($address) > 0, 'No address generated.');
$this->assertTrue(strpos($address, '@') !== false, 'No domain found.');
$this->assertTrue(strpos($address, '+') !== false, 'No subaddress found.');
// The localpart must be less than 64 characters.
list($localpart) = explode('@', $address);
$this->assertTrue(strlen($localpart) <= 64, 'Localpart section of address too long');
// And the data section should be no more than 48 characters.
list(, $datasection) = explode('+', $localpart);
$this->assertTrue(strlen($datasection) <= 48, 'Data section of address too long');
}
}
/**
* Test that the generated e-mail addresses are sufficiently random by
* testing the multiple handlers, multiple users, and multiple data
* items.
*/
public function test_address_uniqueness(): void {
// Generate a set of handlers. These are in two components, and each
// component has two different generators.
$handlers = array();
$handlers[] = $this->helper_create_handler('handler_one', true, 'core_test');
$handlers[] = $this->helper_create_handler('handler_two', true, 'core_test');
$handlers[] = $this->helper_create_handler('handler_three', true, 'core_test_example');
$handlers[] = $this->helper_create_handler('handler_four', true, 'core_test_example');
// Generate some IDs for the data and generate addresses for them.
$dataids = array(
0,
42,
1073741823,
2147483647,
);
$users = array();
for ($i = 0; $i < 5; $i++) {
$users[] = $this->getDataGenerator()->create_user();
}
// Store the addresses for later comparison.
$addresses = array();
foreach ($handlers as $handler) {
$processor = new core_message_inbound_test_helper();
$processor->set_handler($handler->classname);
// Check each dataid.
foreach ($dataids as $dataid) {
$processor->set_data($dataid);
// Check each user.
foreach ($users as $user) {
$address = $processor->generate($user->id);
$this->assertFalse(isset($addresses[$address]));
$addresses[$address] = true;
}
}
}
}
/**
* Test address parsing of a generated address.
*/
public function test_address_parsing(): void {
$dataid = 42;
// Generate a handler to use for this set of tests.
$handler = $this->helper_create_handler('handler_one');
// And a user.
$user = $this->getDataGenerator()->create_user();
// Using the handler created, generate an address for our data entry.
$processor = new core_message_inbound_test_helper();
$processor->set_handler($handler->classname);
$processor->set_data($dataid);
$address = $processor->generate($user->id);
// We should be able to parse the address.
$parser = new core_message_inbound_test_helper();
$parser->process($address);
$parsedresult = $parser->get_data();
$this->assertEquals($user->id, $parsedresult->userid);
$this->assertEquals($dataid, $parsedresult->datavalue);
$this->assertEquals($dataid, $parsedresult->data->datavalue);
$this->assertEquals($handler->id, $parsedresult->handlerid);
$this->assertEquals($handler->id, $parsedresult->data->handler);
}
/**
* Test address parsing of an address with an unrecognised format.
*/
public function test_address_validation_invalid_format_failure(): void {
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
// Check that validation fails when no address has been processed.
$result = $parser->validate($user->email);
$this->assertEquals(\core\message\inbound\address_manager::VALIDATION_INVALID_ADDRESS_FORMAT, $result);
// Test that an address without data fails validation.
$parser->process('bob@example.com');
$result = $parser->validate($user->email);
$this->assertEquals(\core\message\inbound\address_manager::VALIDATION_INVALID_ADDRESS_FORMAT, $result);
// Test than address with a subaddress but invalid data fails with VALIDATION_UNKNOWN_DATAKEY.
$parser->process('bob+nodata@example.com');
$result = $parser->validate($user->email);
$this->assertEquals(\core\message\inbound\address_manager::VALIDATION_INVALID_ADDRESS_FORMAT, $result);
}
/**
* Test address parsing of an address with an unknown handler.
*/
public function test_address_validation_unknown_handler(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Remove the handler record to invalidate it.
$DB->delete_records('messageinbound_handlers', array(
'id' => $handler->id,
));
$parser->process($address);
$result = $parser->validate($user->email);
$expectedfail = \core\message\inbound\address_manager::VALIDATION_UNKNOWN_HANDLER;
$this->assertEquals($expectedfail, $result & $expectedfail);
}
/**
* Test address parsing of an address with a disabled handler.
*/
public function test_address_validation_disabled_handler(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Disable the handler.
$record = \core\message\inbound\manager::record_from_handler($handler);
$record->enabled = false;
$DB->update_record('messageinbound_handlers', $record);
$parser->process($address);
$result = $parser->validate($user->email);
$expectedfail = \core\message\inbound\address_manager::VALIDATION_DISABLED_HANDLER;
$this->assertEquals($expectedfail, $result & $expectedfail);
}
/**
* Test address parsing of an address for an invalid user.
*/
public function test_address_validation_invalid_user(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate(-1);
$parser->process($address);
$result = $parser->validate($user->email);
$expectedfail = \core\message\inbound\address_manager::VALIDATION_UNKNOWN_USER;
$this->assertEquals($expectedfail, $result & $expectedfail);
}
/**
* Test address parsing of an address for a disabled user.
*/
public function test_address_validation_disabled_user(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Unconfirm the user.
$user->confirmed = 0;
$DB->update_record('user', $user);
$parser->process($address);
$result = $parser->validate($user->email);
$expectedfail = \core\message\inbound\address_manager::VALIDATION_DISABLED_USER;
$this->assertEquals($expectedfail, $result & $expectedfail);
}
/**
* Test address parsing of an address for an invalid key.
*/
public function test_address_validation_invalid_key(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Remove the data record to invalidate it.
$DB->delete_records('messageinbound_datakeys', array(
'handler' => $handler->id,
'datavalue' => $dataid,
));
$parser->process($address);
$result = $parser->validate($user->email);
$expectedfail = \core\message\inbound\address_manager::VALIDATION_UNKNOWN_DATAKEY;
$this->assertEquals($expectedfail, $result & $expectedfail);
}
/**
* Test address parsing of an address for an expired key.
*/
public function test_address_validation_expired_key(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Expire the key by setting it's expiry time in the past.
$key = $DB->get_record('messageinbound_datakeys', array(
'handler' => $handler->id,
'datavalue' => $dataid,
));
$key->expires = time() - 3600;
$DB->update_record('messageinbound_datakeys', $key);
$parser->process($address);
$result = $parser->validate($user->email);
$expectedfail = \core\message\inbound\address_manager::VALIDATION_EXPIRED_DATAKEY;
$this->assertEquals($expectedfail, $result & $expectedfail);
}
/**
* Test address parsing of an address for an invalid hash.
*/
public function test_address_validation_invalid_hash(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Expire the key by setting it's expiry time in the past.
$key = $DB->get_record('messageinbound_datakeys', array(
'handler' => $handler->id,
'datavalue' => $dataid,
));
$key->datakey = 'invalid value';
$DB->update_record('messageinbound_datakeys', $key);
$parser->process($address);
$result = $parser->validate($user->email);
$expectedfail = \core\message\inbound\address_manager::VALIDATION_INVALID_HASH;
$this->assertEquals($expectedfail, $result & $expectedfail);
}
/**
* Test address parsing of an address for an invalid sender.
*/
public function test_address_validation_invalid_sender(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
$parser->process($address);
$result = $parser->validate('incorrectuser@example.com');
$expectedfail = \core\message\inbound\address_manager::VALIDATION_ADDRESS_MISMATCH;
$this->assertEquals($expectedfail, $result & $expectedfail);
}
/**
* Test address parsing of an address for an address which is correct.
*/
public function test_address_validation_success(): void {
global $DB;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$dataid = 42;
$parser = new core_message_inbound_test_helper();
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
$parser->process($address);
$result = $parser->validate($user->email);
$this->assertEquals(\core\message\inbound\address_manager::VALIDATION_SUCCESS, $result);
}
/**
* Test that a handler with no default expiration does not have an
* expiration time applied.
*/
public function test_default_hander_expiry_unlimited(): void {
global $DB;
// Set the default expiry of the handler to 0 - no expiration.
$expiration = 0;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$record = \core\message\inbound\manager::record_from_handler($handler);
$record->defaultexpiration = $expiration;
$DB->update_record('messageinbound_handlers', $record);
// Generate an address for the handler.
$dataid = 42;
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Check that the datakey created matches the expirytime.
$key = $DB->get_record('messageinbound_datakeys', array('handler' => $record->id, 'datavalue' => $dataid));
$this->assertNull($key->expires);
}
/**
* Test application of the default expiry on a handler.
*/
public function test_default_hander_expiry_low(): void {
global $DB;
// Set the default expiry of the handler to 60 seconds.
$expiration = 60;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$record = \core\message\inbound\manager::record_from_handler($handler);
$record->defaultexpiration = $expiration;
$DB->update_record('messageinbound_handlers', $record);
// Generate an address for the handler.
$dataid = 42;
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Check that the datakey created matches the expirytime.
$key = $DB->get_record('messageinbound_datakeys', array('handler' => $record->id, 'datavalue' => $dataid));
$this->assertEquals($key->timecreated + $expiration, $key->expires);
}
/**
* Test application of the default expiry on a handler.
*/
public function test_default_hander_expiry_medium(): void {
global $DB;
// Set the default expiry of the handler to 3600 seconds.
$expiration = 3600;
// Create test data.
$user = $this->getDataGenerator()->create_user();
$handler = $this->helper_create_handler('handler_one');
$record = \core\message\inbound\manager::record_from_handler($handler);
$record->defaultexpiration = $expiration;
$DB->update_record('messageinbound_handlers', $record);
// Generate an address for the handler.
$dataid = 42;
$generator = new core_message_inbound_test_helper();
$generator->set_handler($handler->classname);
$generator->set_data($dataid);
$address = $generator->generate($user->id);
// Check that the datakey created matches the expirytime.
$key = $DB->get_record('messageinbound_datakeys', array('handler' => $record->id, 'datavalue' => $dataid));
$this->assertEquals($key->timecreated + $expiration, $key->expires);
}
}
/**
* A helper function for unit testing to expose protected functions in the core_message_inbound API for testing.
*
* @copyright 2014 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_message_inbound_test_helper extends \core\message\inbound\address_manager {
/**
* The validate function.
*
* @param string $address
* @return int
*/
public function validate($address) {
return parent::validate($address);
}
/**
* The get_data function.
*
* @return stdClass
*/
public function get_data() {
return parent::get_data();
}
/**
* The address processor function.
*
* @param string $address
* @return void
*/
public function process($address) {
return parent::process($address);
}
}
/**
* A helper function for unit testing to expose protected functions in the core_message_inbound API for testing.
*
* @copyright 2014 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_message_inbound_test_manager extends \core\message\inbound\manager {
/**
* Helper to fetch make the handler_from_record public for unit testing.
*
* @param $record The handler record to fetch
*/
public static function handler_from_record($record) {
return parent::handler_from_record($record);
}
}
+183
View File
@@ -0,0 +1,183 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_message;
use core_message\tests\helper as testhelper;
/**
* Test api's in message lib.
*
* @package core_message
* @category test
* @copyright 2014 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class messagelib_test extends \advanced_testcase {
public static function setUpBeforeClass(): void {
global $CFG;
require_once($CFG->dirroot . '/message/lib.php');
parent::setUpBeforeClass();
}
/**
* Test message_get_blocked_users throws an exception because has been removed.
*/
public function test_message_get_blocked_users(): void {
$this->expectException('coding_exception');
$this->expectExceptionMessage(
'message_get_blocked_users() has been removed, please use \core_message\api::get_blocked_users() instead.'
);
message_get_blocked_users();
}
/**
* Test message_get_contacts throws an exception because has been removed.
*/
public function test_message_get_contacts(): void {
$this->expectException('coding_exception');
$this->expectExceptionMessage('message_get_contacts() has been removed.');
message_get_contacts();
}
/**
* Test message_search_users.
*/
public function test_message_search_users(): void {
global $USER;
$this->resetAfterTest();
// Set this user as the admin.
$this->setAdminUser();
// Create a user to add to the admin's contact list.
$user1 = $this->getDataGenerator()->create_user(array('firstname' => 'Test1', 'lastname' => 'user1'));
$user2 = $this->getDataGenerator()->create_user(array('firstname' => 'Test2', 'lastname' => 'user2'));
// Add users to the admin's contact list.
\core_message\api::add_contact($USER->id, $user1->id);
\core_message\api::add_contact($USER->id, $user2->id);
$this->assertCount(1, message_search_users(0, 'Test1'));
$this->assertCount(2, message_search_users(0, 'Test'));
$this->assertCount(1, message_search_users(0, 'user1'));
$this->assertCount(2, message_search_users(0, 'user'));
}
/**
* Test message_get_messages.
*/
public function test_message_get_messages(): void {
global $DB;
$this->resetAfterTest();
// Set this user as the admin.
$this->setAdminUser();
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
\core_message\api::add_contact($user1->id, $user2->id);
\core_message\api::add_contact($user1->id, $user3->id);
// Create some individual conversations.
$ic1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
$ic2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user3->id]);
// Send some messages to individual conversations.
$im1 = testhelper::send_fake_message_to_conversation($user1, $ic1->id, 'Message 1');
$im2 = testhelper::send_fake_message_to_conversation($user2, $ic1->id, 'Message 2');
$im3 = testhelper::send_fake_message_to_conversation($user1, $ic1->id, 'Message 3');
$im4 = testhelper::send_fake_message_to_conversation($user1, $ic2->id, 'Message 4');
// Mark a message as read by user2.
$message = $DB->get_record('messages', ['id' => $im1]);
\core_message\api::mark_message_as_read($user2->id, $message);
// Retrieve unread messages sent from user1 to user2.
$lastmessages = message_get_messages($user2->id, $user1->id, 0, MESSAGE_GET_UNREAD);
$this->assertCount(1, $lastmessages);
$this->assertArrayHasKey($im3, $lastmessages);
// Get only read messages.
$lastmessages = message_get_messages($user2->id, $user1->id, 0, MESSAGE_GET_READ);
$this->assertCount(1, $lastmessages);
$this->assertArrayHasKey($im1, $lastmessages);
// Get both read and unread.
$lastmessages = message_get_messages($user2->id, $user1->id, 0, MESSAGE_GET_READ_AND_UNREAD);
$this->assertCount(2, $lastmessages);
$this->assertArrayHasKey($im1, $lastmessages);
$this->assertArrayHasKey($im3, $lastmessages);
// Repeat retrieve read/unread messages but using a bool to test backwards compatibility.
$lastmessages = message_get_messages($user2->id, $user1->id, 0, false);
$this->assertCount(1, $lastmessages);
$this->assertArrayHasKey($im3, $lastmessages);
$lastmessages = message_get_messages($user2->id, $user1->id, 0, true);
$this->assertCount(1, $lastmessages);
$this->assertArrayHasKey($im1, $lastmessages);
// Create some group conversations.
$gc1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$user1->id, $user2->id, $user3->id], 'Group chat');
// Send some messages to group conversations.
$gm1 = testhelper::send_fake_message_to_conversation($user1, $gc1->id, 'Group message 1');
// Retrieve all messages sent from user1 to user2 (the result should be the same as before, because only individual
// conversations should be considered by the message_get_messages function).
$lastmessages = message_get_messages($user2->id, $user1->id, 0, MESSAGE_GET_READ_AND_UNREAD);
$this->assertCount(2, $lastmessages);
$this->assertArrayHasKey($im1, $lastmessages);
$this->assertArrayHasKey($im3, $lastmessages);
}
/**
* Test message_get_messages with only group conversations between users.
*/
public function test_message_get_messages_only_group_conversations(): void {
$this->resetAfterTest();
// Set this user as the admin.
$this->setAdminUser();
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
// Create some group conversations.
$gc1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$user1->id, $user2->id, $user3->id], 'Group chat');
// Send some messages to group conversations.
$gm1 = testhelper::send_fake_message_to_conversation($user1, $gc1->id, 'Group message 1');
$gm2 = testhelper::send_fake_message_to_conversation($user2, $gc1->id, 'Group message 2');
// Retrieve all messages sent from user1 to user2. There shouldn't be messages, because only individual
// conversations should be considered by the message_get_messages function.
$lastmessages = message_get_messages($user2->id, $user1->id, 0, MESSAGE_GET_READ_AND_UNREAD);
$this->assertCount(0, $lastmessages);
}
}
File diff suppressed because it is too large Load Diff
+359
View File
@@ -0,0 +1,359 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_message;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
/**
* Provides the unit tests for received messages global search.
*
* @package core_message
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class search_received_test extends \advanced_testcase {
/**
* @var string Area id
*/
protected $messagereceivedareaid = null;
/**
* Setting up the test environment
* @return void
*/
public function setUp(): void {
$this->resetAfterTest(true);
set_config('enableglobalsearch', true);
$this->messagereceivedareaid = \core_search\manager::generate_areaid('core_message', 'message_received');
// Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this.
$search = \testable_core_search::instance();
}
/**
* Indexing messages contents.
*
* @return void
*/
public function test_message_received_indexing(): void {
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->messagereceivedareaid);
$this->assertInstanceOf('\core_message\search\message_received', $searcharea);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = "Test Subject";
$message->smallmessage = "Test small messsage";
$message->fullmessage = "Test full messsage";
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = "moodle";
$message->name = "instantmessage";
message_send($message);
$messages = $sink->get_messages();
$this->assertEquals(1, count($messages));
// All records.
$recordset = $searcharea->get_recordset_by_timestamp(0);
$this->assertTrue($recordset->valid());
$nrecords = 0;
foreach ($recordset as $record) {
$this->assertInstanceOf('stdClass', $record);
$doc = $searcharea->get_document($record);
$this->assertInstanceOf('\core_search\document', $doc);
$nrecords++;
}
// If there would be an error/failure in the foreach above the recordset would be closed on shutdown.
$recordset->close();
$this->assertEquals(1, $nrecords);
// The +2 is to prevent race conditions.
$recordset = $searcharea->get_recordset_by_timestamp(time() + 2);
// No new records.
$this->assertFalse($recordset->valid());
$recordset->close();
}
/**
* Indexing messages, with restricted contexts.
*/
public function test_message_received_indexing_contexts(): void {
global $SITE;
require_once(__DIR__ . '/search_sent_test.php');
$searcharea = \core_search\manager::get_search_area($this->messagereceivedareaid);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
// Send first message.
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = 'Test1';
$message->smallmessage = 'Test small messsage';
$message->fullmessage = 'Test full messsage';
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = 'moodle';
$message->name = 'instantmessage';
message_send($message);
// Ensure that ordering by timestamp will return in consistent order.
$this->waitForSecond();
// Send second message in opposite direction.
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user2;
$message->userto = $user1;
$message->subject = 'Test2';
$message->smallmessage = 'Test small messsage';
$message->fullmessage = 'Test full messsage';
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = 'moodle';
$message->name = 'instantmessage';
message_send($message);
// Test function with null context and system context (same).
$rs = $searcharea->get_document_recordset(0, null);
$this->assertEquals(['Test1', 'Test2'], search_sent_test::recordset_to_subjects($rs));
$rs = $searcharea->get_document_recordset(0, \context_system::instance());
$this->assertEquals(['Test1', 'Test2'], search_sent_test::recordset_to_subjects($rs));
// Test with user context for each user.
$rs = $searcharea->get_document_recordset(0, \context_user::instance($user1->id));
$this->assertEquals(['Test2'], search_sent_test::recordset_to_subjects($rs));
$rs = $searcharea->get_document_recordset(0, \context_user::instance($user2->id));
$this->assertEquals(['Test1'], search_sent_test::recordset_to_subjects($rs));
// Test with a course context (should return null).
$this->assertNull($searcharea->get_document_recordset(0,
\context_course::instance($SITE->id)));
}
/**
* Document contents.
*
* @return void
*/
public function test_message_received_document(): void {
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->messagereceivedareaid);
$this->assertInstanceOf('\core_message\search\message_received', $searcharea);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = "Test Subject";
$message->smallmessage = "Test small messsage";
$message->fullmessage = "Test full messsage";
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = "moodle";
$message->name = "instantmessage";
message_send($message);
$messages = $sink->get_messages();
$message = $messages[0];
$doc = $searcharea->get_document($message);
$this->assertInstanceOf('\core_search\document', $doc);
$this->assertEquals($message->id, $doc->get('itemid'));
$this->assertEquals($this->messagereceivedareaid . '-' . $message->id, $doc->get('id'));
$this->assertEquals(SITEID, $doc->get('courseid'));
$this->assertEquals($message->useridfrom, $doc->get('userid'));
$this->assertEquals($message->useridto, $doc->get('owneruserid'));
$this->assertEquals(content_to_text($message->subject, false), $doc->get('title'));
$this->assertEquals(content_to_text($message->smallmessage, false), $doc->get('content'));
}
/**
* Document accesses.
*
* @return void
*/
public function test_message_received_access(): void {
global $CFG;
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->messagereceivedareaid);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = "Test Subject";
$message->smallmessage = "Test small messsage";
$message->fullmessage = "Test full messsage";
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = "moodle";
$message->name = "instantmessage";
$messageid = message_send($message);
$messages = $sink->get_messages();
$message = $messages[0];
$this->setUser($user1);
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access(-123));
$this->setUser($user2);
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($messageid));
if ($CFG->messaging) {
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($messageid));
} else {
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
}
\core_message\api::delete_message($user2->id, $message->id);
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access($messageid));
$this->setUser($user3);
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
$this->setGuestUser();
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
$this->setAdminUser();
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
delete_user($user1);
$this->setUser($user2);
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access($messageid));
}
/**
* Test received deleted user.
* Tests the case where a received message for a deleted user
* is attempted to be added to the index.
*
* @return void
*/
public function test_message_received_deleted_user(): void {
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->messagereceivedareaid);
$this->assertInstanceOf('\core_message\search\message_received', $searcharea);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = "Test Subject";
$message->smallmessage = "Test small messsage";
$message->fullmessage = "Test full messsage";
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = "moodle";
$message->name = "instantmessage";
message_send($message);
$messages = $sink->get_messages();
$message = $messages[0];
// Delete user.
delete_user($user2);
$doc = $searcharea->get_document($message);
$this->assertFalse($doc);
}
/**
* Test document icon.
*/
public function test_get_doc_icon(): void {
$searcharea = \core_search\manager::get_search_area($this->messagereceivedareaid);
$document = $this->getMockBuilder('\core_search\document')
->disableOriginalConstructor()
->getMock();
$result = $searcharea->get_doc_icon($document);
$this->assertEquals('t/message', $result->get_name());
$this->assertEquals('moodle', $result->get_component());
}
/**
* Test assigned search categories.
*/
public function test_get_category_names(): void {
$searcharea = \core_search\manager::get_search_area($this->messagereceivedareaid);
$expected = ['core-users'];
$this->assertEquals($expected, $searcharea->get_category_names());
}
}
+374
View File
@@ -0,0 +1,374 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_message;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
/**
* Provides the unit tests for sent message global search.
*
* @package core_message
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class search_sent_test extends \advanced_testcase {
/**
* @var string Area id
*/
protected $messagesentareaid = null;
/**
* Setting up the test environment
* @return void
*/
public function setUp(): void {
$this->resetAfterTest(true);
set_config('enableglobalsearch', true);
$this->messagesentareaid = \core_search\manager::generate_areaid('core_message', 'message_sent');
// Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this.
$search = \testable_core_search::instance();
}
/**
* Indexing messages contents.
*
* @return void
*/
public function test_message_sent_indexing(): void {
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
$this->assertInstanceOf('\core_message\search\message_sent', $searcharea);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = "Test Subject";
$message->smallmessage = "Test small messsage";
$message->fullmessage = "Test full messsage";
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = 'moodle';
$message->name = "instantmessage";
message_send($message);
$messages = $sink->get_messages();
$this->assertEquals(1, count($messages));
// All records.
$recordset = $searcharea->get_recordset_by_timestamp(0);
$this->assertTrue($recordset->valid());
$nrecords = 0;
foreach ($recordset as $record) {
$this->assertInstanceOf('stdClass', $record);
$doc = $searcharea->get_document($record);
$this->assertInstanceOf('\core_search\document', $doc);
$nrecords++;
}
// If there would be an error/failure in the foreach above the recordset would be closed on shutdown.
$recordset->close();
$this->assertEquals(1, $nrecords);
// The +2 is to prevent race conditions.
$recordset = $searcharea->get_recordset_by_timestamp(time() + 2);
// No new records.
$this->assertFalse($recordset->valid());
$recordset->close();
}
/**
* Indexing messages, with restricted contexts.
*/
public function test_message_sent_indexing_contexts(): void {
global $SITE;
$searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
// Send first message.
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = 'Test1';
$message->smallmessage = 'Test small messsage';
$message->fullmessage = 'Test full messsage';
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = 'moodle';
$message->name = 'instantmessage';
message_send($message);
// Ensure that ordering by timestamp will return in consistent order.
$this->waitForSecond();
// Send second message in opposite direction.
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user2;
$message->userto = $user1;
$message->subject = 'Test2';
$message->smallmessage = 'Test small messsage';
$message->fullmessage = 'Test full messsage';
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = 'moodle';
$message->name = 'instantmessage';
message_send($message);
// Test function with null context and system context (same).
$rs = $searcharea->get_document_recordset(0, null);
$this->assertEquals(['Test1', 'Test2'], self::recordset_to_subjects($rs));
$rs = $searcharea->get_document_recordset(0, \context_system::instance());
$this->assertEquals(['Test1', 'Test2'], self::recordset_to_subjects($rs));
// Test with user context for each user.
$rs = $searcharea->get_document_recordset(0, \context_user::instance($user1->id));
$this->assertEquals(['Test1'], self::recordset_to_subjects($rs));
$rs = $searcharea->get_document_recordset(0, \context_user::instance($user2->id));
$this->assertEquals(['Test2'], self::recordset_to_subjects($rs));
// Test with a course context (should return null).
$this->assertNull($searcharea->get_document_recordset(0,
\context_course::instance($SITE->id)));
}
/**
* Utility function to convert recordset to array of message subjects for testing.
*
* @param moodle_recordset $rs Recordset to convert (and close)
* @return array Array of IDs from records indexed by number (0, 1, 2, ...)
*/
public static function recordset_to_subjects(\moodle_recordset $rs) {
$results = [];
foreach ($rs as $rec) {
$results[] = $rec->subject;
}
$rs->close();
return $results;
}
/**
* Document contents.
*
* @return void
*/
public function test_message_sent_document(): void {
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
$this->assertInstanceOf('\core_message\search\message_sent', $searcharea);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = "Test Subject";
$message->smallmessage = "Test small messsage";
$message->fullmessage = "Test full messsage";
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = "moodle";
$message->name = "instantmessage";
message_send($message);
$messages = $sink->get_messages();
$message = $messages[0];
$doc = $searcharea->get_document($message);
$this->assertInstanceOf('\core_search\document', $doc);
$this->assertEquals($message->id, $doc->get('itemid'));
$this->assertEquals($this->messagesentareaid . '-' . $message->id, $doc->get('id'));
$this->assertEquals(SITEID, $doc->get('courseid'));
$this->assertEquals($message->useridfrom, $doc->get('owneruserid'));
$this->assertEquals($message->useridto, $doc->get('userid'));
$this->assertEquals(content_to_text($message->subject, false), $doc->get('title'));
$this->assertEquals(content_to_text($message->smallmessage, false), $doc->get('content'));
}
/**
* Document accesses.
*
* @return void
*/
public function test_message_sent_access(): void {
global $CFG;
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = "Test Subject";
$message->smallmessage = "Test small messsage";
$message->fullmessage = "Test full messsage";
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = "moodle";
$message->name = "instantmessage";
$messageid = message_send($message);
$messages = $sink->get_messages();
$message = $messages[0];
$this->setUser($user1);
if ($CFG->messaging) {
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($messageid));
} else {
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
}
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access(-123));
\core_message\api::delete_message($user1->id, $message->id);
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access($messageid));
$this->setUser($user2);
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
$this->setUser($user3);
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
$this->setGuestUser();
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
$this->setAdminUser();
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($messageid));
delete_user($user2);
$this->setUser($user1);
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access($messageid));
}
/**
* Test sent deleted user.
* Tests the case where a sent message for a deleted user
* is attempted to be added to the index.
*
* @return void
*/
public function test_message_sent_deleted_user(): void {
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
$this->assertInstanceOf('\core_message\search\message_sent', $searcharea);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->preventResetByRollback();
$sink = $this->redirectMessages();
$message = new \core\message\message();
$message->courseid = SITEID;
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = "Test Subject";
$message->smallmessage = "Test small messsage";
$message->fullmessage = "Test full messsage";
$message->fullmessageformat = 0;
$message->fullmessagehtml = null;
$message->notification = 0;
$message->component = "moodle";
$message->name = "instantmessage";
message_send($message);
$messages = $sink->get_messages();
$message = $messages[0];
// Delete user.
delete_user($user1);
$doc = $searcharea->get_document($message);
$this->assertFalse($doc);
}
/**
* Test document icon.
*/
public function test_get_doc_icon(): void {
$searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
$document = $this->getMockBuilder('\core_search\document')
->disableOriginalConstructor()
->getMock();
$result = $searcharea->get_doc_icon($document);
$this->assertEquals('t/message', $result->get_name());
$this->assertEquals('moodle', $result->get_component());
}
/**
* Test assigned search categories.
*/
public function test_get_category_names(): void {
$searcharea = \core_search\manager::get_search_area($this->messagesentareaid);
$expected = ['core-users'];
$this->assertEquals($expected, $searcharea->get_category_names());
}
}
@@ -0,0 +1,414 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_message\task;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/message/tests/messagelib_test.php');
/**
* Class for testing the migrate message data task.
*
* @package core_message
* @category test
* @copyright 2018 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class migrate_message_data_test extends \advanced_testcase {
/**
* Test set up.
*
* This is executed before running any test in this file.
*/
public function setUp(): void {
$this->resetAfterTest();
}
/**
* Test migrating legacy messages.
*/
public function test_migrating_messages(): void {
global $DB;
// Create users to test with.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
// Get the current time minus some, to make sure our data is migrated accurately and just not using the current timestamp.
$now = time();
$timedeleted1 = $now - (2 * DAYSECS);
$timedeleted2 = $now - (2 * DAYSECS) + 1;
$timeread1 = $now - DAYSECS;
$timeread2 = $now - DAYSECS + 1;
$timeread3 = $now - DAYSECS + 2;
// Send messages from user 1 to user 2.
$m1 = $this->create_legacy_message_or_notification($user1->id, $user2->id, 1, false, $timeread1);
$m2 = $this->create_legacy_message_or_notification($user1->id, $user2->id, 2);
$m3 = $this->create_legacy_message_or_notification($user1->id, $user2->id, 3);
// Send messages from user 3 to user 1.
$m4 = $this->create_legacy_message_or_notification($user3->id, $user1->id, 4, false, $timeread2);
$m5 = $this->create_legacy_message_or_notification($user3->id, $user1->id, 5);
$m6 = $this->create_legacy_message_or_notification($user3->id, $user1->id, 6);
// Send messages from user 3 to user 2.
$m7 = $this->create_legacy_message_or_notification($user3->id, $user2->id, 7, false, $timeread3);
$m8 = $this->create_legacy_message_or_notification($user3->id, $user2->id, 8);
$m9 = $this->create_legacy_message_or_notification($user3->id, $user2->id, 9);
// Let's delete some messages, not using API here as it does not use the legacy tables.
$messageupdate = new \stdClass();
$messageupdate->id = $m1;
$messageupdate->timeusertodeleted = $timedeleted1;
$DB->update_record('message_read', $messageupdate);
$messageupdate = new \stdClass();
$messageupdate->id = $m5;
$messageupdate->timeuserfromdeleted = $timedeleted2;
$DB->update_record('message', $messageupdate);
// Now, let's execute the task for user 1.
$task = new migrate_message_data();
$task->set_custom_data(
[
'userid' => $user1->id
]
);
$task->execute();
// Ok, now we need to confirm all is good.
// Remember - we are only converting the messages related to user 1.
$this->assertEquals(2, $DB->count_records('message'));
$this->assertEquals(1, $DB->count_records('message_read'));
$this->assertEquals(6, $DB->count_records('messages'));
$this->assertEquals(0, $DB->count_records('notifications'));
$this->assertEquals(0, $DB->count_records('message_popup_notifications'));
// Get the conversations.
$conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
$conversation2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
// Confirm what we have in the messages table is correct.
$messages = $DB->get_records('messages', [], 'timecreated ASC');
$i = 1;
foreach ($messages as $message) {
$useridfrom = $user1->id;
$conversationid = $conversation1;
if ($i > 3) {
$useridfrom = $user3->id;
$conversationid = $conversation2;
}
if ($i == 1) {
$messagereadid1 = $message->id;
$messagedeletedid1 = $message->id;
} else if ($i == 4) {
$messagereadid2 = $message->id;
} else if ($i == 5) {
$messagedeletedid2 = $message->id;
}
$this->assertEquals($useridfrom, $message->useridfrom);
$this->assertEquals($conversationid, $message->conversationid);
$this->assertEquals('Subject ' . $i, $message->subject);
$this->assertEquals('Full message ' . $i, $message->fullmessage);
$this->assertEquals(FORMAT_PLAIN, $message->fullmessageformat);
$this->assertEquals('Full message HTML '. $i, $message->fullmessagehtml);
$this->assertEquals('Small message ' . $i, $message->smallmessage);
$this->assertEquals($i, $message->timecreated);
$i++;
}
// Confirm there are 4 actions.
$this->assertEquals(4, $DB->count_records('message_user_actions'));
// Confirm the messages that were marked as read have actions associated with them.
$muas = $DB->get_records('message_user_actions', ['action' => \core_message\api::MESSAGE_ACTION_READ], 'timecreated DESC');
$this->assertCount(2, $muas);
// Message user action for message read by user 1 (referring to $m4).
$mua1 = array_shift($muas);
// Message user action for message read by user 2 (referring to $m1).
$mua2 = array_shift($muas);
$this->assertEquals($user1->id, $mua1->userid);
$this->assertEquals($messagereadid2, $mua1->messageid);
$this->assertEquals($timeread2, $mua1->timecreated);
$this->assertEquals($user2->id, $mua2->userid);
$this->assertEquals($messagereadid1, $mua2->messageid);
$this->assertEquals($timeread1, $mua2->timecreated);
// Confirm the messages that were deleted have actions associated with them.
$muas = $DB->get_records('message_user_actions', ['action' => \core_message\api::MESSAGE_ACTION_DELETED],
'timecreated DESC');
$this->assertCount(2, $muas);
// Message user action for message deleted by user 3 (referring to $m5).
$mua1 = array_shift($muas);
// Message user action for message deleted by user 2 (referring to $m1).
$mua2 = array_shift($muas);
$this->assertEquals($user3->id, $mua1->userid);
$this->assertEquals($messagedeletedid2, $mua1->messageid);
$this->assertEquals($timedeleted2, $mua1->timecreated);
$this->assertEquals($user2->id, $mua2->userid);
$this->assertEquals($messagedeletedid1, $mua2->messageid);
$this->assertEquals($timedeleted1, $mua2->timecreated);
}
/**
* Test migrating legacy notifications.
*/
public function test_migrating_notifications(): void {
global $DB;
// Create users to test with.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
// Get the current time minus some, to make sure our data is migrated accurately and just not using the current timestamp.
$timeread = time() - DAYSECS;
// Send notifications from user 1 to user 2.
$this->create_legacy_message_or_notification($user1->id, $user2->id, 1, true, $timeread);
$this->create_legacy_message_or_notification($user1->id, $user2->id, 2, true);
$this->create_legacy_message_or_notification($user1->id, $user2->id, 3, true);
// Send notifications from user 3 to user 1.
$this->create_legacy_message_or_notification($user3->id, $user1->id, 4, true, $timeread);
$this->create_legacy_message_or_notification($user3->id, $user1->id, 5, true);
$this->create_legacy_message_or_notification($user3->id, $user1->id, 6, true);
// Send notifications from user 3 to user 2.
$this->create_legacy_message_or_notification($user3->id, $user2->id, 7, true, $timeread);
$this->create_legacy_message_or_notification($user3->id, $user2->id, 8, true);
$this->create_legacy_message_or_notification($user3->id, $user2->id, 9, true);
// Now, let's execute the task for user 1.
$task = new migrate_message_data();
$task->set_custom_data(
[
'userid' => $user1->id
]
);
$task->execute();
// Ok, now we need to confirm all is good.
// Remember - we are only converting the notifications related to user 1.
$this->assertEquals(2, $DB->count_records('message'));
$this->assertEquals(1, $DB->count_records('message_read'));
$this->assertEquals(3, $DB->count_records('message_popup'));
$this->assertEquals(6, $DB->count_records('notifications'));
$this->assertEquals(6, $DB->count_records('message_popup_notifications'));
// Confirm what we have in the notifications table is correct.
$notifications = $DB->get_records('notifications', [], 'timecreated ASC');
$popupnotifications = $DB->get_records('message_popup_notifications', [], 'notificationid ASC', 'notificationid');
$i = 1;
foreach ($notifications as $notification) {
// Assert the correct id is stored in the 'message_popup_notifications' table.
$this->assertArrayHasKey($notification->id, $popupnotifications);
$useridfrom = $user1->id;
$useridto = $user2->id;
if ($i > 3) {
$useridfrom = $user3->id;
$useridto = $user1->id;
}
$this->assertEquals($useridfrom, $notification->useridfrom);
$this->assertEquals($useridto, $notification->useridto);
$this->assertEquals('Subject ' . $i, $notification->subject);
$this->assertEquals('Full message ' . $i, $notification->fullmessage);
$this->assertEquals(FORMAT_PLAIN, $notification->fullmessageformat);
$this->assertEquals('Full message HTML '. $i, $notification->fullmessagehtml);
$this->assertEquals('Small message ' . $i, $notification->smallmessage);
$this->assertEquals('mod_assign', $notification->component);
$this->assertEquals('assign_notification', $notification->eventtype);
$this->assertEquals('https://www.google.com', $notification->contexturl);
$this->assertEquals('google', $notification->contexturlname);
$this->assertEquals($i, $notification->timecreated);
if (($i == 1) || ($i == 4)) {
$this->assertEquals($timeread, $notification->timeread);
} else {
$this->assertNull($notification->timeread);
}
$i++;
}
}
/**
* Test migrating a legacy message that contains null as the format.
*/
public function test_migrating_message_null_format(): void {
global $DB;
// Create users to test with.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->create_legacy_message_or_notification($user1->id, $user2->id, null, false, null, null);
// Now, let's execute the task for user 1.
$task = new migrate_message_data();
$task->set_custom_data(
[
'userid' => $user1->id
]
);
$task->execute();
$messages = $DB->get_records('messages');
$this->assertCount(1, $messages);
$message = reset($messages);
$this->assertEquals(FORMAT_MOODLE, $message->fullmessageformat);
}
/**
* Test migrating a legacy notification that contains null as the format.
*/
public function test_migrating_notification_null_format(): void {
global $DB;
// Create users to test with.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->create_legacy_message_or_notification($user1->id, $user2->id, null, true, null, null);
// Now, let's execute the task for user 1.
$task = new migrate_message_data();
$task->set_custom_data(
[
'userid' => $user1->id
]
);
$task->execute();
$notifications = $DB->get_records('notifications');
$this->assertCount(1, $notifications);
$notification = reset($notifications);
$this->assertEquals(FORMAT_MOODLE, $notification->fullmessageformat);
}
/**
* Test migrating a legacy message that a user sent to themselves then deleted.
*/
public function test_migrating_message_deleted_message_sent_to_self(): void {
global $DB;
// Create user to test with.
$user1 = $this->getDataGenerator()->create_user();
$m1 = $this->create_legacy_message_or_notification($user1->id, $user1->id, null, false, null, null);
// Let's delete the message for the 'user to' and 'user from' which in this case is the same user.
$messageupdate = new \stdClass();
$messageupdate->id = $m1;
$messageupdate->timeuserfromdeleted = time();
$messageupdate->timeusertodeleted = time();
$DB->update_record('message', $messageupdate);
// Now, let's execute the task for the user.
$task = new migrate_message_data();
$task->set_custom_data(
[
'userid' => $user1->id
]
);
$task->execute();
$this->assertEquals(0, $DB->count_records('message'));
$this->assertEquals(1, $DB->count_records('message_user_actions'));
}
/**
* Creates a legacy message or notification to be used for testing.
*
* @param int $useridfrom The user id from
* @param int $useridto The user id to
* @param int $timecreated
* @param bool $notification
* @param int|null $timeread The time the message/notification was read, null if it hasn't been.
* @param string|int|null $format The format of the message.
* @return int The id of the message (in either the message or message_read table)
* @throws dml_exception
*/
private function create_legacy_message_or_notification($useridfrom, $useridto, $timecreated = null,
$notification = false, $timeread = null, $format = FORMAT_PLAIN) {
global $DB;
$tabledata = new \stdClass();
if (is_null($timecreated)) {
$timecreated = time();
}
if (!is_null($timeread)) {
$table = 'message_read';
$tabledata->timeread = $timeread;
} else {
$table = 'message';
}
if ($notification) {
$tabledata->eventtype = 'assign_notification';
$tabledata->component = 'mod_assign';
$tabledata->notification = 1;
$tabledata->contexturl = 'https://www.google.com';
$tabledata->contexturlname = 'google';
} else {
$tabledata->eventtype = 'instantmessage';
$tabledata->component = 'moodle';
$tabledata->notification = 0;
}
$tabledata->useridfrom = $useridfrom;
$tabledata->useridto = $useridto;
$tabledata->subject = 'Subject ' . $timecreated;
$tabledata->fullmessage = 'Full message ' . $timecreated;
$tabledata->fullmessageformat = $format;
$tabledata->fullmessagehtml = 'Full message HTML ' . $timecreated;
$tabledata->smallmessage = 'Small message ' . $timecreated;
$tabledata->timecreated = $timecreated;
$id = $DB->insert_record($table, $tabledata);
// Insert into the legacy 'message_popup' table if it is a notification.
if ($notification) {
$mp = new \stdClass();
$mp->messageid = $id;
$mp->isread = (!is_null($timeread)) ? 1 : 0;
$DB->insert_record('message_popup', $mp);
}
return $id;
}
}