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
@@ -0,0 +1,104 @@
<?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 mod_forum\backup;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php");
require_once($CFG->dirroot . '/rating/lib.php');
/**
* Restore date tests.
*
* @package mod_forum
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_date_test extends \restore_date_testcase {
/**
* Test restore dates.
*/
public function test_restore_dates(): void {
global $DB, $USER;
$gg = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$record = ['assesstimefinish' => 100, 'assesstimestart' => 100, 'ratingtime' => 1, 'assessed' => 2, 'scale' => 1];
list($course, $forum) = $this->create_course_and_module('forum', $record);
// Forum Discussions/posts/ratings.
$timestamp = 996699;
$diff = $this->get_diff();
$record = new \stdClass();
$record->course = $course->id;
$record->userid = $USER->id;
$record->forum = $forum->id;
$record->timestart = $record->timeend = $record->timemodified = $timestamp;
$discussion = $gg->create_discussion($record);
$record = new \stdClass();
$record->discussion = $discussion->id;
$record->parent = $discussion->firstpost;
$record->userid = $USER->id;
$record->created = $record->modified = $timestamp;
$post = $gg->create_post($record);
// Time modified is changed internally.
$DB->set_field('forum_discussions', 'timemodified', $timestamp);
// Ratings.
$ratingoptions = new \stdClass;
$ratingoptions->context = \context_module::instance($forum->cmid);
$ratingoptions->ratingarea = 'post';
$ratingoptions->component = 'mod_forum';
$ratingoptions->itemid = $post->id;
$ratingoptions->scaleid = 2;
$ratingoptions->userid = $USER->id;
$rating = new \rating($ratingoptions);
$rating->update_rating(2);
$rating = $DB->get_record('rating', ['itemid' => $post->id]);
// Do backup and restore.
$newcourseid = $this->backup_and_restore($course);
$newforum = $DB->get_record('forum', ['course' => $newcourseid]);
$this->assertFieldsNotRolledForward($forum, $newforum, ['timemodified']);
$props = ['assesstimefinish', 'assesstimestart'];
$this->assertFieldsRolledForward($forum, $newforum, $props);
$newdiscussion = $DB->get_record('forum_discussions', ['forum' => $newforum->id]);
$newposts = $DB->get_records('forum_posts', ['discussion' => $newdiscussion->id]);
$newcm = $DB->get_record('course_modules', ['course' => $newcourseid, 'instance' => $newforum->id]);
// Forum discussion time checks.
$this->assertEquals($timestamp + $diff, $newdiscussion->timestart);
$this->assertEquals($timestamp + $diff, $newdiscussion->timeend);
$this->assertEquals($timestamp, $newdiscussion->timemodified);
// Posts test.
foreach ($newposts as $post) {
$this->assertEquals($timestamp, $post->created);
$this->assertEquals($timestamp, $post->modified);
}
// Rating test.
$newrating = $DB->get_record('rating', ['contextid' => \context_module::instance($newcm->id)->id]);
$this->assertEquals($rating->timecreated, $newrating->timecreated);
$this->assertEquals($rating->timemodified, $newrating->timemodified);
}
}
@@ -0,0 +1,114 @@
<?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 mod_forum;
use backup_forum_activity_task;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_stepslib.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_activity_task.class.php');
require_once($CFG->dirroot . '/mod/forum/backup/moodle2/backup_forum_activity_task.class.php');
/**
* Tests for mod_forum_backup_forum_activity_task.
*
* @package mod_forum
* @category test
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_forum_activity_task_test extends \advanced_testcase {
/**
* Test the encoding of forum content links.
*
* @param string $content The incoming content
* @param string $expectation The expected result
*
* @dataProvider encode_content_links_provider
*/
public function test_encode_content_links($content, $expectation): void {
$this->assertEquals($expectation, backup_forum_activity_task::encode_content_links($content));
}
public function encode_content_links_provider() {
global $CFG;
$altwwwroot = 'http://invalid.example.com/';
return [
'Link to the list of forums for current wwwroot' => [
sprintf('%s/mod/forum/index.php?id=42', $CFG->wwwroot),
'$@FORUMINDEX*42@$',
],
'Link to forum view by moduleid for current wwwroot' => [
sprintf('%s/mod/forum/view.php?id=29', $CFG->wwwroot),
'$@FORUMVIEWBYID*29@$',
],
'Link to forum view by forumid for current wwwroot' => [
sprintf('%s/mod/forum/view.php?f=31', $CFG->wwwroot),
'$@FORUMVIEWBYF*31@$',
],
'Link to forum discussion with parent syntax for current wwwroot' => [
sprintf('%s/mod/forum/discuss.php?d=26&parent=99', $CFG->wwwroot),
'$@FORUMDISCUSSIONVIEWPARENT*26*99@$',
],
'Link to forum discussion with parent syntax for current wwwroot encoded' => [
sprintf('%s/mod/forum/discuss.php?d=26&amp;parent=99', $CFG->wwwroot),
'$@FORUMDISCUSSIONVIEWPARENT*26*99@$',
],
'Link to forum discussion with relative syntax for current wwwroot' => [
sprintf('%s/mod/forum/discuss.php?d=1040#9930', $CFG->wwwroot),
'$@FORUMDISCUSSIONVIEWINSIDE*1040*9930@$',
],
'Link to forum discussion by discussionid for current wwwroot' => [
sprintf('%s/mod/forum/discuss.php?d=9304', $CFG->wwwroot),
'$@FORUMDISCUSSIONVIEW*9304@$',
],
'Link to the list of forums for other wwwroot' => [
sprintf('%s/mod/forum/index.php?id=42', $altwwwroot),
sprintf('%s/mod/forum/index.php?id=42', $altwwwroot),
],
'Link to forum view by moduleid for other wwwroot' => [
sprintf('%s/mod/forum/view.php?id=29', $altwwwroot),
sprintf('%s/mod/forum/view.php?id=29', $altwwwroot),
],
'Link to forum view by forumid for other wwwroot' => [
sprintf('%s/mod/forum/view.php?f=31', $altwwwroot),
sprintf('%s/mod/forum/view.php?f=31', $altwwwroot),
],
'Link to forum discussion with parent syntax for other wwwroot' => [
sprintf('%s/mod/forum/discuss.php?d=26&parent=99', $altwwwroot),
sprintf('%s/mod/forum/discuss.php?d=26&parent=99', $altwwwroot),
],
'Link to forum discussion with parent syntax for other wwwroot encoded' => [
sprintf('%s/mod/forum/discuss.php?d=26&amp;parent=99', $altwwwroot),
sprintf('%s/mod/forum/discuss.php?d=26&amp;parent=99', $altwwwroot),
],
'Link to forum discussion with relative syntax for other wwwroot' => [
sprintf('%s/mod/forum/discuss.php?d=1040#9930', $altwwwroot),
sprintf('%s/mod/forum/discuss.php?d=1040#9930', $altwwwroot),
],
'Link to forum discussion by discussionid for other wwwroot' => [
sprintf('%s/mod/forum/discuss.php?d=9304', $altwwwroot),
sprintf('%s/mod/forum/discuss.php?d=9304', $altwwwroot),
],
];
}
}
+56
View File
@@ -0,0 +1,56 @@
@mod @mod_forum @_file_upload
Feature: Add forum activities and discussions
In order to discuss topics with other users
As a teacher
I need to add forum activities to moodle courses
@javascript
Scenario: Add a forum and a discussion attaching files
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "teacher1"
And I add a forum activity to course "Course 1" section "1" and I fill the form with:
| Forum name | Test forum name |
| Forum type | Standard forum for general use |
| Description | Test forum description |
And I add a new discussion to "Test forum name" forum with:
| Subject | Forum post 1 |
| Message | This is the body |
And I am on the "Course 1" course page logged in as student1
When I add a new discussion to "Test forum name" forum with:
| Subject | Post with attachment |
| Message | This is the body |
| Attachment | lib/tests/fixtures/empty.txt |
And I reply "Forum post 1" post from "Test forum name" forum with:
| Subject | Reply with attachment |
| Message | This is the body |
| Attachment | lib/tests/fixtures/upload_users.csv |
Then I should see "Reply with attachment"
And I should see "upload_users.csv"
And I am on the "Test forum name" "forum activity" page
And I follow "Post with attachment"
And I should see "empty.txt"
And I follow "Edit"
And the field "Attachment" matches value "empty.txt"
Scenario: Test forum settings validation
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And I log in as "admin"
And I add a forum activity to course "Course 1" section "0" and I fill the form with:
| Forum name | Test forum |
| Forum type | single |
| Group mode | 1 |
When I press "Save and display"
Then I should see "Separate groups can't be used with a single simple discussion."
And I should see "A single simple discussion can't be used with separate groups."
@@ -0,0 +1,33 @@
@mod @mod_forum @javascript
Feature: Add forum activities and discussions utilizing the inline add discussion form
Background: Add a forum and a discussion attaching files
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | 0001 |
| name | Test forum name |
| type | general |
And the following "mod_forum > discussion" exists:
| forum | 0001 |
| course | C1 |
| user | teacher1 |
| name | Forum post 1 |
| message | This is the body |
Scenario: Student can add a discussion via the inline form
Given I am on the "Course 1" course page logged in as student1
Then I add a new discussion to "Test forum name" forum inline with:
| Subject | Post with attachment |
| Message | This is the body |
@@ -0,0 +1,130 @@
@mod @mod_forum
Feature: The forum search allows users to perform advanced searches for forum posts
In order to perform an advanced search for a forum post
As a teacher
I can use the search feature
Background:
Given the following "users" exist:
| username | firstname | lastname | email | idnumber |
| teacher1 | Teacher | ONE | teacher1@example.com | T1 |
| teacher2 | Teacher | TWO | teacher2@example.com | T1 |
| student1 | Student | 1 | student1@example.com | S1 |
And the following "courses" exist:
| fullname | shortname | category | newsitems |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | editingteacher |
| student1 | C1 | student |
And the following "tags" exist:
| name | isstandard |
| SearchedTag | 1 |
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| news_items | Course | C1 | course-view-* | side-pre |
And the following "mod_forum > discussions" exist:
| user | forum | name | subject | message |
| teacher1 | Announcements | My subject | My subject | My message |
| teacher1 | Announcements | Your subjective | Your subjective | Your long message |
Scenario: Perform an advanced search using any term
Given I am on the "Announcements" "forum activity" page logged in as student1
And I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "words" to "subject"
When I press "Search"
Then I should see "My subject"
And I should see "Your subjective"
Scenario: Perform an advanced search avoiding words
Given I am on the "Announcements" "forum activity" page logged in as student1
And I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "words" to "My"
And I set the field "notwords" to "subjective"
When I press "Search"
Then I should see "My subject"
And I should not see "Your subjective"
Scenario: Perform an advanced search using whole words
Given database family used is one of the following:
| mysql |
| postgres |
And I am on the "Announcements" "forum activity" page logged in as student1
And I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "fullwords" to "subject"
When I press "Search"
Then I should see "My subject"
And I should not see "Your subjective"
Scenario: Perform an advanced search matching the subject
Given I am on the "Announcements" "forum activity" page logged in as student1
And I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "subject" to "subjective"
When I press "Search"
Then I should not see "My message"
And I should see "Your subjective"
Scenario: Perform an advanced search matching the author
Given the following "mod_forum > discussions" exist:
| user | forum | name | subject | message |
| teacher2 | Announcements | My Subjects | My Subjects | My message |
And I am on the "Announcements" "forum activity" page logged in as student1
When I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "user" to "TWO"
And I press "Search"
Then I should see "Teacher TWO"
And I should not see "Teacher ONE"
Scenario: Perform an advanced search with multiple words
Given I am on the "Announcements" "forum activity" page logged in as student1
And I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "subject" to "your subjective"
When I press "Search"
Then I should not see "My message"
And I should see "Your subjective"
@javascript
Scenario: Perform an advanced search using tags
Given I am on the "Announcements" "forum activity" page logged in as teacher1
And I follow "My subject"
And I follow "Edit"
And I set the following fields to these values:
| Tags | SearchedTag |
And I press "Save changes"
And I am on the "Announcements" "forum activity" page logged in as student1
And I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "Is tagged with" to "SearchedTag"
When I press "Search"
Then I should see "My subject"
And I should not see "Your subjective"
@javascript
Scenario: Perform an advanced search on starred discussions without text
Given I am on the "Announcements" "forum activity" page logged in as student1
And I click on "Star this discussion" "link" in the "Your subjective" "table_row"
And I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "starredonly" to "1"
When I press "Search"
Then I should not see "My message"
And I should see "Your subjective"
@javascript
Scenario: Perform an advanced search on starred discussions with text
Given I am on the "Announcements" "forum activity" page logged in as student1
And I click on "Star this discussion" "link" in the "Your subjective" "table_row"
And I press "Search"
And I should see "Please enter search terms into one or more of the following fields"
And I set the field "words" to "message"
And I set the field "starredonly" to "1"
When I press "Search"
Then I should not see "My message"
And I should see "Your subjective"
+521
View File
@@ -0,0 +1,521 @@
<?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/>.
/**
* Steps definitions related with the forum activity.
*
* @package mod_forum
* @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');
use Behat\Gherkin\Node\TableNode;
/**
* Forum-related steps definitions.
*
* @package mod_forum
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_mod_forum extends behat_base {
/**
* Reset forum caches between tests.
*
* @BeforeScenario @mod_forum
*/
public function reset_forum_caches(): void {
\mod_forum\subscriptions::reset_discussion_cache();
\mod_forum\subscriptions::reset_forum_cache();
}
/**
* Adds a topic to the forum specified by it's name. Useful for the Announcements and blog-style forums.
*
* @Given /^I add a new topic to "(?P<forum_name_string>(?:[^"]|\\")*)" forum with:$/
* @param string $forumname
* @param TableNode $table
*/
public function i_add_a_new_topic_to_forum_with($forumname, TableNode $table) {
$this->add_new_discussion($forumname, $table, get_string('addanewdiscussion', 'forum'));
}
/**
* Adds a Q&A discussion to the Q&A-type forum specified by it's name with the provided table data.
*
* @Given /^I add a new question to "(?P<forum_name_string>(?:[^"]|\\")*)" forum with:$/
* @param string $forumname
* @param TableNode $table
*/
public function i_add_a_new_question_to_forum_with($forumname, TableNode $table) {
$this->add_new_discussion($forumname, $table, get_string('addanewdiscussion', 'forum'));
}
/**
* Adds a discussion to the forum specified by it's name with the provided table data (usually Subject and Message). The step begins from the forum's course page.
*
* @Given /^I add a new discussion to "(?P<forum_name_string>(?:[^"]|\\")*)" forum with:$/
* @param string $forumname
* @param TableNode $table
*/
public function i_add_a_forum_discussion_to_forum_with($forumname, TableNode $table) {
$this->add_new_discussion($forumname, $table, get_string('addanewdiscussion', 'forum'));
}
/**
* Adds a discussion to the forum specified by it's name with the provided table data (usually Subject and Message).
* The step begins from the forum's course page.
*
* @Given /^I add a new discussion to "(?P<forum_name_string>(?:[^"]|\\")*)" forum inline with:$/
* @param string $forumname
* @param TableNode $table
*/
public function i_add_a_forum_discussion_to_forum_inline_with($forumname, TableNode $table) {
$this->add_new_discussion_inline($forumname, $table, get_string('addanewdiscussion', 'forum'));
}
/**
* Adds a reply to the specified post of the specified forum. The step begins from the forum's page or from the forum's course page.
*
* @Given /^I reply "(?P<post_subject_string>(?:[^"]|\\")*)" post from "(?P<forum_name_string>(?:[^"]|\\")*)" forum with:$/
* @param string $postname The subject of the post
* @param string $forumname The forum name
* @param TableNode $table
*/
public function i_reply_post_from_forum_with($postsubject, $forumname, TableNode $table) {
// Navigate to forum.
$this->goto_main_post_reply($postsubject);
// Fill form and post.
$this->execute('behat_forms::i_set_the_following_fields_to_these_values', $table);
$this->execute('behat_forms::press_button', get_string('posttoforum', 'forum'));
$this->execute('behat_general::i_wait_to_be_redirected');
}
/**
* Inpage Reply - adds a reply to the specified post of the specified forum. The step begins from the forum's page or from the forum's course page.
*
* @Given /^I reply "(?P<post_subject_string>(?:[^"]|\\")*)" post from "(?P<forum_name_string>(?:[^"]|\\")*)" forum using an inpage reply with:$/
* @param string $postsubject The subject of the post
* @param string $forumname The forum name
* @param TableNode $table
*/
public function i_reply_post_from_forum_using_an_inpage_reply_with($postsubject, $forumname, TableNode $table) {
// Navigate to forum.
$this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($forumname), 'forum activity']);
$this->execute('behat_general::click_link', $this->escape($postsubject));
$this->execute('behat_general::click_link', get_string('reply', 'forum'));
// Fill form and post.
$this->execute('behat_forms::i_set_the_following_fields_to_these_values', $table);
$this->execute('behat_forms::press_button', get_string('posttoforum', 'mod_forum'));
}
/**
* Navigates to a particular discussion page
*
* @Given /^I navigate to post "(?P<post_subject_string>(?:[^"]|\\")*)" in "(?P<forum_name_string>(?:[^"]|\\")*)" forum$/
* @param string $postsubject The subject of the post
* @param string $forumname The forum name
*/
public function i_navigate_to_post_in_forum($postsubject, $forumname) {
// Navigate to forum discussion.
$this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($forumname), 'forum activity']);
$this->execute('behat_general::click_link', $this->escape($postsubject));
}
/**
* Opens up the action menu for the discussion
*
* @Given /^I click on "(?P<post_subject_string>(?:[^"]|\\")*)" action menu$/
* @param string $discussion The subject of the discussion
*/
public function i_click_on_action_menu($discussion) {
$this->execute('behat_general::i_click_on_in_the', [
"[data-container='discussion-tools'] [data-toggle='dropdown']", "css_element",
"//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ') and contains(.,'$discussion')]",
"xpath_element"
]);
}
/**
* Creates new discussions within forums of a given course.
*
* @Given the following forum discussions exist in course :coursename:
* @param string $coursename The full name of the course where the forums exist.
* @param TableNode $discussionsdata The discussion posts to be created.
*/
public function the_following_forum_discussions_exist(string $coursename, TableNode $discussionsdata) {
global $DB;
$courseid = $this->get_course_id($coursename);
$forumgenerator = behat_util::get_data_generator()->get_plugin_generator('mod_forum');
// Add the discussions to the relevant forum.
foreach ($discussionsdata->getHash() as $discussioninfo) {
$discussioninfo['course'] = $courseid;
$discussioninfo['forum'] = $this->get_forum_id($courseid, $discussioninfo['forum']);
$discussioninfo['userid'] = $this->get_user_id($discussioninfo['user']);
// Prepare data for any attachments.
if (!empty($discussioninfo['attachments']) || !empty($discussioninfo['inlineattachments'])) {
$discussioninfo['attachment'] = 1;
$cm = get_coursemodule_from_instance('forum', $discussioninfo['forum']);
}
// Prepare data for groups if needed.
if (!empty($discussioninfo['group'])) {
$discussioninfo['groupid'] = $this->get_group_id($courseid, $discussioninfo['group']);
unset($discussioninfo['group']);
}
// Create the discussion post.
$discussion = $forumgenerator->create_discussion($discussioninfo);
$postid = $DB->get_field('forum_posts', 'id', ['discussion' => $discussion->id]);
// Override the creation and modified timestamps as required.
if (!empty($discussioninfo['created']) || !empty($discussioninfo['modified'])) {
$discussiondata = [
'id' => $discussion->id,
'timemodified' => empty($discussioninfo['modified']) ? $discussioninfo['created'] : $discussioninfo['modified'],
];
$DB->update_record('forum_discussions', $discussiondata);
$postdata = [
'id' => $postid,
'modified' => empty($discussioninfo['modified']) ? $discussioninfo['created'] : $discussioninfo['modified'],
];
if (!empty($discussioninfo['created'])) {
$postdata['created'] = $discussioninfo['created'];
}
$DB->update_record('forum_posts', $postdata);
}
// Create attachments to the discussion post if required.
if (!empty($discussioninfo['attachments'])) {
$attachments = array_map('trim', explode(',', $discussioninfo['attachments']));
$this->create_post_attachments($postid, $discussioninfo['userid'], $attachments, $cm, 'attachment');
}
// Create inline attachments to the discussion post if required.
if (!empty($discussioninfo['inlineattachments'])) {
$inlineattachments = array_map('trim', explode(',', $discussioninfo['inlineattachments']));
$this->create_post_attachments($postid, $discussioninfo['userid'], $inlineattachments, $cm, 'post');
}
}
}
/**
* Creates replies to discussions within forums of a given course.
*
* @Given the following forum replies exist in course :coursename:
* @param string $coursename The full name of the course where the forums exist.
* @param TableNode $repliesdata The reply posts to be created.
*/
public function the_following_forum_replies_exist(string $coursename, TableNode $repliesdata) {
global $DB;
$courseid = $this->get_course_id($coursename);
$forumgenerator = behat_util::get_data_generator()->get_plugin_generator('mod_forum');
// Add the replies to the relevant discussions.
foreach ($repliesdata->getHash() as $replyinfo) {
$replyinfo['course'] = $courseid;
$replyinfo['forum'] = $this->get_forum_id($courseid, $replyinfo['forum']);
$replyinfo['userid'] = $this->get_user_id($replyinfo['user']);
[
'discussionid' => $replyinfo['discussion'],
'parentid' => $replyinfo['parent'],
] = $this->get_base_discussion($replyinfo['forum'], $replyinfo['discussion']);
// Prepare data for any attachments.
if (!empty($replyinfo['attachments']) || !empty($replyinfo['inlineattachments'])) {
$replyinfo['attachment'] = 1;
$cm = get_coursemodule_from_instance('forum', $replyinfo['forum']);
}
// Get the user id of the user to whom the reply is private.
if (!empty($replyinfo['privatereplyto'])) {
$replyinfo['privatereplyto'] = $this->get_user_id($replyinfo['privatereplyto']);
}
// Create the reply post.
$reply = $forumgenerator->create_post($replyinfo);
// Create attachments to the post if required.
if (!empty($replyinfo['attachments'])) {
$attachments = array_map('trim', explode(',', $replyinfo['attachments']));
$this->create_post_attachments($reply->id, $replyinfo['userid'], $attachments, $cm, 'attachment');
}
// Create inline attachments to the post if required.
if (!empty($replyinfo['inlineattachments'])) {
$inlineattachments = array_map('trim', explode(',', $replyinfo['inlineattachments']));
$this->create_post_attachments($reply->id, $replyinfo['userid'], $inlineattachments, $cm, 'post');
}
}
}
/**
* Checks if the user can subscribe to the forum.
*
* @Given /^I can subscribe to this forum$/
*/
public function i_can_subscribe_to_this_forum() {
$this->execute('behat_general::assert_page_contains_text', [get_string('subscribe', 'mod_forum')]);
}
/**
* Checks if the user can unsubscribe from the forum.
*
* @Given /^I can unsubscribe from this forum$/
*/
public function i_can_unsubscribe_from_this_forum() {
$this->execute('behat_general::assert_page_contains_text', [get_string('unsubscribe', 'mod_forum')]);
}
/**
* Subscribes to the forum.
*
* @Given /^I subscribe to this forum$/
*/
public function i_subscribe_to_this_forum() {
$this->execute('behat_general::click_link', [get_string('subscribe', 'mod_forum')]);
}
/**
* Unsubscribes from the forum.
*
* @Given /^I unsubscribe from this forum$/
*/
public function i_unsubscribe_from_this_forum() {
$this->execute('behat_general::click_link', [get_string('unsubscribe', 'mod_forum')]);
}
/**
* Fetch user ID from its username.
*
* @param string $username The username.
* @return int The user ID.
* @throws Exception
*/
protected function get_user_id($username) {
global $DB;
if (!$userid = $DB->get_field('user', 'id', ['username' => $username])) {
throw new Exception("A user with username '{$username}' does not exist");
}
return $userid;
}
/**
* Fetch course ID using course name.
*
* @param string $coursename The name of the course.
* @return int The course ID.
* @throws Exception
*/
protected function get_course_id(string $coursename): int {
global $DB;
if (!$courseid = $DB->get_field('course', 'id', ['fullname' => $coursename])) {
throw new Exception("A course with name '{$coursename}' does not exist");
}
return $courseid;
}
/**
* Fetch forum ID using forum name.
*
* @param int $courseid The course ID the forum exists within.
* @param string $forumname The name of the forum.
* @return int The forum ID.
* @throws Exception
*/
protected function get_forum_id(int $courseid, string $forumname): int {
global $DB;
$conditions = [
'course' => $courseid,
'name' => $forumname,
];
if (!$forumid = $DB->get_field('forum', 'id', $conditions)) {
throw new Exception("A forum with name '{$forumname}' does not exist in the provided course");
}
return $forumid;
}
/**
* Fetch Group ID using group name.
*
* @param int $courseid The course ID the forum exists within.
* @param string $groupname The short name of the group.
* @return int The group ID.
* @throws Exception
*/
protected function get_group_id(int $courseid, string $groupname): int {
global $DB;
if ($groupname === 'All participants') {
return -1;
}
$conditions = [
'courseid' => $courseid,
'idnumber' => $groupname,
];
if (!$groupid = $DB->get_field('groups', 'id', $conditions)) {
throw new Exception("A group with name '{$groupname}' does not exist in the provided course");
}
return $groupid;
}
/**
* Fetch discussion ID and first post ID by discussion name.
*
* @param int $forumid The forum ID where the discussion resides.
* @param string $name The name of the discussion.
* @return array The discussion ID and first post ID.
* @throws dml_exception If the discussion name is not unique within the forum (or doesn't exist).
*/
protected function get_base_discussion(int $forumid, string $name): array {
global $DB;
$conditions = [
'name' => $name,
'forum' => $forumid,
];
$result = $DB->get_record("forum_discussions", $conditions, 'id, firstpost', MUST_EXIST);
return [
'discussionid' => $result->id,
'parentid' => $result->firstpost,
];
}
/**
* Create one or more attached or inline attachments to a forum post.
*
* @param int $postid The ID of the forum post.
* @param int $userid The user ID creating the attachment.
* @param array $attachmentnames Names of all attachments to be created.
* @param stdClass $cm The context module of the forum.
* @param string $filearea The file area being written to, eg 'attachment' or 'post' (inline).
*/
protected function create_post_attachments(int $postid, int $userid, array $attachmentnames, stdClass $cm, string $filearea): void {
$filestorage = get_file_storage();
foreach ($attachmentnames as $attachmentname) {
$filestorage->create_file_from_string(
[
'contextid' => context_module::instance($cm->id)->id,
'component' => 'mod_forum',
'filearea' => $filearea,
'itemid' => $postid,
'filepath' => '/',
'filename' => $attachmentname,
'userid' => $userid,
],
"File content {$attachmentname}"
);
}
}
/**
* Returns the steps list to add a new discussion to a forum.
*
* Abstracts add a new topic and add a new discussion, as depending
* on the forum type the button string changes.
*
* @param string $forumname
* @param TableNode $table
* @param string $buttonstr
*/
protected function add_new_discussion($forumname, TableNode $table, $buttonstr) {
// Navigate to forum.
$this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($forumname), 'forum activity']);
$this->execute('behat_general::click_link', $buttonstr);
$this->execute('behat_forms::press_button', get_string('showadvancededitor'));
$this->fill_new_discussion_form($table);
}
/**
* Returns the steps list to add a new discussion to a forum inline.
*
* Abstracts add a new topic and add a new discussion, as depending
* on the forum type the button string changes.
*
* @param string $forumname
* @param TableNode $table
* @param string $buttonstr
*/
protected function add_new_discussion_inline($forumname, TableNode $table, $buttonstr) {
// Navigate to forum.
$this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($forumname), 'forum activity']);
$this->execute('behat_general::click_link', $buttonstr);
$this->fill_new_discussion_form($table);
}
/**
* Fill in the forum's post form and submit. It assumes you've already navigated and enabled the form for view.
*
* @param TableNode $table
* @throws coding_exception
*/
protected function fill_new_discussion_form(TableNode $table) {
// Fill form and post.
$this->execute('behat_forms::i_set_the_following_fields_to_these_values', $table);
$this->execute('behat_forms::press_button', get_string('posttoforum', 'forum'));
$this->execute('behat_general::i_wait_to_be_redirected');
}
/**
* Go to the default reply to post page.
* This is used instead of navigating through 4-5 different steps and to solve issues where JS would be required to click
* on the advanced button
*
* @param $postsubject
* @throws coding_exception
* @throws dml_exception
* @throws moodle_exception
*/
protected function goto_main_post_reply($postsubject) {
global $DB;
$post = $DB->get_record("forum_posts", array("subject" => $postsubject), 'id', MUST_EXIST);
$url = new moodle_url('/mod/forum/post.php', ['reply' => $post->id]);
$this->execute('behat_general::i_visit', [$url]);
}
}
@@ -0,0 +1,83 @@
@mod @mod_forum @javascript
Feature: Students can choose from 4 discussion display options and their choice is remembered
In order to read forum posts in a suitable view
As a user
I need to select which display method I want to use
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum name |
| idnumber | forum |
And the following "mod_forum > discussions" exist:
| forum | name | subject | message |
| forum | Discussion 1 | Discussion 1 | Discussion contents 1, first message |
| forum | Discussion 2 | Discussion 2 | Discussion contents 2, first message |
And the following "mod_forum > posts" exist:
| parentsubject | subject | message |
| Discussion 1 | Reply 1 to discussion 1 | Discussion contents 1, second message |
| Discussion 2 | Reply 1 to discussion 2 | Discussion contents 2, second message |
Scenario: Display replies flat, with oldest first
Given I am on the "Course 1" course page logged in as student1
And I reply "Discussion 1" post from "Test forum name" forum with:
| Subject | Reply 2 to discussion 1 |
| Message | Discussion contents 1, third message |
When I select "Display replies flat, with oldest first" from the "mode" singleselect
Then I should see "Discussion contents 1, first message" in the "div.firstpost.starter" "css_element"
And I should see "Discussion contents 1, second message" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ') and not(contains(@class, 'starter'))]" "xpath_element"
And I reply "Discussion 2" post from "Test forum name" forum with:
| Subject | Reply 2 to discussion 2 |
| Message | Discussion contents 2, third message |
And the field "Display mode" matches value "Display replies flat, with oldest first"
And I should see "Discussion contents 2, first message" in the "div.firstpost.starter" "css_element"
And I should see "Discussion contents 2, second message" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ') and not(contains(@class, 'starter'))]" "xpath_element"
Scenario: Display replies flat, with newest first
Given I am on the "Course 1" course page logged in as student1
And I reply "Discussion 1" post from "Test forum name" forum with:
| Subject | Reply 2 to discussion 1 |
| Message | Discussion contents 1, third message |
When I select "Display replies flat, with newest first" from the "mode" singleselect
Then I should see "Discussion contents 1, first message" in the "div.firstpost.starter" "css_element"
And I should see "Discussion contents 1, third message" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ') and not(contains(@class, 'starter'))]" "xpath_element"
And I reply "Discussion 2" post from "Test forum name" forum with:
| Subject | Reply 2 to discussion 2 |
| Message | Discussion contents 2, third message |
And the field "Display mode" matches value "Display replies flat, with newest first"
And I should see "Discussion contents 2, first message" in the "div.firstpost.starter" "css_element"
And I should see "Discussion contents 2, third message" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ') and not(contains(@class, 'starter'))]" "xpath_element"
Scenario: Display replies in threaded form
Given I am on the "Test forum name" "forum activity" page logged in as student1
And I follow "Discussion 1"
When I select "Display replies in threaded form" from the "mode" singleselect
Then I should see "Discussion contents 1, first message"
And I should see "Reply 1 to discussion 1" in the "div.forumthread" "css_element"
And I click on "Test forum name" "link" in the "page-header" "region"
And I follow "Discussion 2"
And the field "Display mode" matches value "Display replies in threaded form"
And I should see "Discussion contents 2, first message"
And I should see "Reply 1 to discussion 2" in the "div.forumthread" "css_element"
Scenario: Display replies in nested form
Given I am on the "Test forum name" "forum activity" page logged in as student1
And I follow "Discussion 1"
When I select "Display replies in nested form" from the "mode" singleselect
Then I should see "Discussion contents 1, first message" in the "div.firstpost.starter" "css_element"
And I should see "Discussion contents 1, second message" in the "div.indent div.forumpost" "css_element"
And I click on "Test forum name" "link" in the "page-header" "region"
And I follow "Discussion 2"
And the field "Display mode" matches value "Display replies in nested form"
And I should see "Discussion contents 2, first message" in the "div.firstpost.starter" "css_element"
And I should see "Discussion contents 2, second message" in the "div.indent div.forumpost" "css_element"
@@ -0,0 +1,70 @@
@mod @mod_forum @javascript
Feature: As a teacher, you can manually lock individual discussions when viewing the discussion
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum name |
| idnumber | forum1 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Discussion 1 | Discussion contents 1, first message |
| admin | forum1 | Discussion 2 | Discussion contents 2, first message |
And the following "mod_forum > posts" exist:
| user | parentsubject | subject | message |
| admin | Discussion 1 | Reply 1 to discussion 1 | Discussion contents 1, second message |
| admin | Discussion 2 | Reply 1 to discussion 2 | Discussion contents 2, second message |
Scenario: Lock a discussion and view
Given I am on the "Course 1" course page logged in as admin
And I navigate to post "Discussion 1" in "Test forum name" forum
And I press "Settings"
Then "Lock this discussion" "link" should be visible
And I follow "Lock this discussion"
Then I should see "This discussion has been locked so you can no longer reply to it."
And I press "Settings"
Then "Lock this discussion" "link" should not be visible
Then "Unlock this discussion" "link" should be visible
And I press "Settings"
And I follow "Discussion 2"
Then I should not see "This discussion has been locked so you can no longer reply to it."
And I am on the "Course 1" course page logged in as student1
And I navigate to post "Discussion 1" in "Test forum name" forum
Then I should see "This discussion has been locked so you can no longer reply to it."
And "Reply" "link" should not be visible
@accessibility
Scenario: A locked discussion must be accessible
Given I am on the "Course 1" course page logged in as admin
And I navigate to post "Discussion 1" in "Test forum name" forum
When I reply "Discussion 1" post from "Test forum name" forum with:
| Subject | Discussion 1: Hello world! |
| Message | Discussion contents 1, hello world! |
# Check discussion view accessibility with success notification shown on post.
Then the page should meet accessibility standards with "wcag143" extra tests
And I press "Settings"
When I follow "Lock this discussion"
# Check discussion view accessibility with info notification shown when discussion is locked.
And the page should meet accessibility standards with "wcag143" extra tests
And I am on the "Test forum name" "forum activity" page logged in as student1
# Check discussion list accessibility with danger pill shown when discussion is locked.
And the page should meet accessibility standards with "wcag143" extra tests
And I follow "Preferences" in the user menu
And I click on "Forum preferences" "link"
And I set the following fields to these values:
| Use experimental nested discussion view | Yes |
And I press "Save changes"
And I am on "Course 1" course homepage
And I navigate to post "Discussion 1" in "Test forum name" forum
# Check experimental discussion view accessibility with danger pill shown when discussion is locked.
And the page should meet accessibility standards with "wcag143" extra tests
@@ -0,0 +1,127 @@
@mod @mod_forum
Feature: A user can navigate to previous and next discussions
In order to get go the previous discussion
As a user
I need to click on the previous discussion link
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| teacher1 | G2 |
| student1 | G1 |
| student2 | G2 |
Scenario: A user can navigate between discussions
Given the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Test forum name | C1 | forum | 0 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher1 | forum | Discussion 1 | Test post message |
| teacher1 | forum | Discussion 2 | Test post message |
| teacher1 | forum | Discussion 3 | Test post message |
And I am on the "Test forum name" "forum activity" page logged in as teacher1
When I follow "Discussion 3"
Then I should not see "Discussion 1"
And I should see "Discussion 2"
And I follow "Discussion 2"
And I should see "Discussion 1"
And I should see "Discussion 3"
And I follow "Discussion 1"
And I should see "Discussion 2"
And I should not see "Discussion 3"
And I follow "Reply"
And I set the following fields to these values:
| Message | Answer to discussion |
# We need to wait a bit to guarantee that the post is created after the previous ones.
# because there is a bug in the forum_get_discussion_neighbours() when all the discussion
# last modified times are the same. See MDL-79247 for more details.
And I wait "1" seconds
And I press "Post to forum"
And I should not see "Discussion 2"
And I should see "Discussion 3"
And I follow "Discussion 3"
And I should see "Discussion 1"
And I should see "Discussion 2"
And I follow "Discussion 2"
And I should not see "Discussion 1"
And I should see "Discussion 3"
Scenario: A user can navigate between discussions with visible groups
Given the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Test forum name | C1 | forum | 2 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| student1 | forum | Discussion 1 Group 0 | Test post message |
| student1 | forum | Discussion 2 Group 0 | Test post message |
And the following "mod_forum > discussions" exist:
| user | forum | name | message | group |
| student1 | forum | Discussion 1 Group 1 | Test post message | G1 |
| student1 | forum | Discussion 2 Group 1 | Test post message | G1 |
| student1 | forum | Discussion 1 Group 2 | Test post message | G2 |
| student1 | forum | Discussion 2 Group 2 | Test post message | G2 |
When I am on the "Test forum name" "forum activity" page logged in as student1
And I select "All participants" from the "Visible groups" singleselect
And I follow "Discussion 1 Group 0"
Then I should see "Discussion 2 Group 0"
And I should not see "Group 1"
And I should not see "Group 2"
And I follow "Discussion 2 Group 0"
And I should see "Discussion 1 Group 0"
And I should see "Discussion 1 Group 1"
And I follow "Discussion 1 Group 1"
And I should see "Discussion 2 Group 0"
And I should see "Discussion 2 Group 1"
And I follow "Test forum name"
And I follow "Discussion 1 Group 2"
And I should see "Discussion 2 Group 1"
And I should see "Discussion 2 Group 2"
And I follow "Test forum name"
And I select "Group 1" from the "Visible groups" singleselect
And I follow "Discussion 1 Group 1"
And I should see "Discussion 2 Group 0"
And I should see "Discussion 2 Group 1"
And I follow "Discussion 2 Group 1"
And I should see "Discussion 1 Group 1"
And I should not see "Group 2"
Scenario: A user can navigate between discussions with separate groups
Given the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Test forum name | C1 | forum | 1 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| student1 | forum | Discussion 1 Group 0 | Test post message |
| student1 | forum | Discussion 2 Group 0 | Test post message |
And the following "mod_forum > discussions" exist:
| user | forum | name | message | group |
| student1 | forum | Discussion 1 Group 1 | Test post message | G1 |
| student1 | forum | Discussion 2 Group 1 | Test post message | G1 |
| student1 | forum | Discussion 1 Group 2 | Test post message | G2 |
| student1 | forum | Discussion 2 Group 2 | Test post message | G2 |
When I am on the "Test forum name" "forum activity" page logged in as student1
And I follow "Discussion 1 Group 1"
Then I should see "Discussion 2 Group 0"
And I should see "Discussion 2 Group 1"
And I follow "Discussion 2 Group 1"
And I should see "Discussion 1 Group 1"
And I should not see "Group 2"
@@ -0,0 +1,99 @@
@mod @mod_forum
Feature: An admin or teacher sets the post threshold for blocking and warning
A student should not be able to post more than blocking value
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 1 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | name | course | blockperiod | blockafter | warnafter |
| forum | Test forum name | C1 | 172800 | 3 | 2 |
@javascript
Scenario: A student should not be able to post new discussion or reply to the existing discussion once the threshold block count is reached
Given I am on the "Test forum name" "forum activity" page logged in as student1
When I add a new discussion to "Test forum name" forum with:
| Subject | Test post subject one |
| Message | Test post message one |
And I add a new discussion to "Test forum name" forum with:
| Subject | Test post subject two |
| Message | Test post message two |
And I should see "Add discussion topic"
# Verify that when navigated to one of the topics and then click reply the warning notification is shown.
And I click on "Test post subject two" "link"
And I click on "Reply" "link"
And I should see "You are approaching the posting threshold. You have posted 2 times in the last 2 days and the limit is 3 posts."
And I click on "Test forum name" "link"
And I add a new discussion to "Test forum name" forum with:
| Subject | Test post subject three |
| Message | Test post message three |
Then I should not see "Add discussion topic"
# Verify that no reply link available in the posts.
And I click on "Test post subject three" "link"
And I should not see "Reply"
And I am on the "Test forum name" "forum activity" page
And I click on "Test post subject two" "link"
And I should not see "Reply"
# Verify that student2 is not affected by the posts made by student1
And I am on the "Test forum name" "forum activity" page logged in as student2
And I should see "Add discussion topic"
@javascript
Scenario: A student should see warning when the post is about to reach threshold when experimental nested discussion view is set
Given I am on the "Test forum name" "forum activity" page logged in as student1
When I add a new discussion to "Test forum name" forum with:
| Subject | Test post subject one |
| Message | Test post message one |
And I add a new discussion to "Test forum name" forum with:
| Subject | Test post subject two |
| Message | Test post message two |
And I should see "Add discussion topic"
#Now verify that when "Use experimental nested discussion view" is set, the user should see same warning in the Reply.
And I follow "Preferences" in the user menu
And I click on "Forum preferences" "link"
And I set the field "Use experimental nested discussion view" to "Yes"
And I press "Save changes"
And I am on the "Test forum name" "forum activity" page
And I click on "Test post subject two" "link"
When I press "Reply"
Then I should see "You are approaching the posting threshold. You have posted 2 times in the last 2 days and the limit is 3 posts."
@javascript
Scenario: A student with 'mod/forum:postwithoutthrottling' capability should be able to post unlimited number of times
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| mod/forum:postwithoutthrottling | Allow | student | Course | C1 |
And I am on the "Test forum name" "forum activity" page logged in as student1
And I should see "This forum has a limit to the number of forum postings you can make in a given time period - this is currently set at 3 posting(s) in 2 days"
When I add a new discussion to "Test forum name" forum with:
| Subject | Test post subject one |
| Message | Test post message one |
And I add a new discussion to "Test forum name" forum with:
| Subject | Test post subject two |
| Message | Test post message two |
And I should see "Add discussion topic"
# Verify that when navigated to one of the topics and then click reply the warning notification is shown.
And I click on "Test post subject two" "link"
And I click on "Reply" "link"
# With 'mod/forum:postwithoutthrottling' assigned capability the message below should not be displayed.
And I should not see "You are approaching the posting threshold. You have posted 2 times in the last 2 days and the limit is 3 posts."
And I click on "Test forum name" "link"
And I add a new discussion to "Test forum name" forum with:
| Subject | Test post subject three |
| Message | Test post message three |
Then I should see "Add discussion topic"
# Verify that reply link is available in the posts.
And I click on "Test post subject three" "link"
And I should see "Reply"
And I am on the "Test forum name" "forum activity" page
And I click on "Test post subject two" "link"
And I should see "Reply"
@@ -0,0 +1,279 @@
@mod @mod_forum
Feature: A user can control their own subscription preferences for a discussion
In order to receive notifications for things I am interested in
As a user
I need to choose my discussion subscriptions
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | One | student.one@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
@javascript
Scenario: An optional forum can have discussions subscribed to
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | forum1 |
| name | Test forum name |
| type | general |
| forcesubscribe | 0 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Test post subject one | Test post message one |
| admin | forum1 | Test post subject two | Test post message two |
When I am on the "Test forum name" "forum activity" page logged in as student1
Then I can subscribe to this forum
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject one" "table_row"
And I can subscribe to this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject one" "table_row"
And I can subscribe to this forum
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject one" "table_row"
And I can subscribe to this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I follow "Subscribe to forum"
And I should see "You will be notified of new posts in the forum 'Test forum name'"
And I can unsubscribe from this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I unsubscribe from this forum
And I should see "Student One will NOT be notified of new posts in 'Test forum name'"
And I can subscribe to this forum
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
@javascript
Scenario: An automatic subscription forum can have discussions unsubscribed from
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | forum1 |
| name | Test forum name |
| type | general |
| forcesubscribe | 2 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Test post subject one | Test post message one |
| admin | forum1 | Test post subject two | Test post message two |
When I am on the "Test forum name" "forum activity" page logged in as student1
Then I can unsubscribe from this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject one" "table_row"
And I can unsubscribe from this forum
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject one" "table_row"
And I can unsubscribe from this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject one" "table_row"
And I can unsubscribe from this forum
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I unsubscribe from this forum
And I should see "Student One will NOT be notified of new posts in 'Test forum name'"
And I can subscribe to this forum
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I subscribe to this forum
And I should see "You will be notified of new posts in the forum 'Test forum name'"
And I can unsubscribe from this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
@javascript
Scenario: A user does not lose their preferences when a forum is switch from optional to automatic
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | forum1 |
| name | Test forum name |
| type | general |
| forcesubscribe | 0 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Test post subject one | Test post message one |
| admin | forum1 | Test post subject two | Test post message two |
And I am on the "Test forum name" "forum activity" page logged in as student1
And I can subscribe to this forum
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject one" "table_row"
And I can subscribe to this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I am on the "Test forum name" "forum activity editing" page logged in as admin
And I set the following fields to these values:
| Subscription mode | Auto subscription |
And I press "Save and return to course"
And I am on the "Test forum name" "forum activity" page logged in as student1
And I can unsubscribe from this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
When I unsubscribe from this forum
Then I should see "Student One will NOT be notified of new posts in 'Test forum name'"
And I can subscribe to this forum
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
Scenario: An optional forum prompts a user to subscribe to a discussion when posting unless they have already chosen not to subscribe
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | forum1 |
| name | Test forum name |
| type | general |
| forcesubscribe | 0 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Test post subject one | Test post message one |
| admin | forum1 | Test post subject two | Test post message two |
When I am on the "Test forum name" "forum activity" page logged in as student1
And I should see "Subscribe to forum"
And I reply "Test post subject one" post from "Test forum name" forum with:
| Subject | Reply 1 to discussion 1 |
| Message | Discussion contents 1, second message |
| Discussion subscription | 1 |
And I reply "Test post subject two" post from "Test forum name" forum with:
| Subject | Reply 1 to discussion 1 |
| Message | Discussion contents 1, second message |
| Discussion subscription | 0 |
And I am on the "Test forum name" "forum activity" page
Then "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I follow "Test post subject one"
And I follow "Reply"
And the field "Discussion subscription" matches value "Send me notifications of new posts in this discussion"
And I follow "Test forum name"
And I follow "Test post subject two"
And I follow "Reply"
And the field "Discussion subscription" matches value "I don't want to be notified of new posts in this discussion"
Scenario: An automatic forum prompts a user to subscribe to a discussion when posting unless they have already chosen not to subscribe
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | forum1 |
| name | Test forum name |
| type | general |
| forcesubscribe | 2 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Test post subject one | Test post message one |
| admin | forum1 | Test post subject two | Test post message two |
# added for this scenario
And the following "users" exist:
| username | firstname | lastname | email |
| student2 | Student | Two | student.two@example.com |
And the following "course enrolments" exist:
| user | course | role |
| student2 | C1 | student |
When I am on the "Test forum name" "forum activity" page logged in as student2
And I should see "Unsubscribe from forum"
And I reply "Test post subject one" post from "Test forum name" forum with:
| Subject | Reply 1 to discussion 1 |
| Message | Discussion contents 1, second message |
| Discussion subscription | 1 |
And I reply "Test post subject two" post from "Test forum name" forum with:
| Subject | Reply 1 to discussion 1 |
| Message | Discussion contents 1, second message |
| Discussion subscription | 0 |
And I am on the "Test forum name" "forum activity" page
Then "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And "Subscribe to this discussion" "checkbox" should exist in the "Test post subject two" "table_row"
And I follow "Test post subject one"
And I follow "Reply"
And the field "Discussion subscription" matches value "Send me notifications of new posts in this discussion"
And I am on the "Test forum name" "forum activity" page
And I follow "Test post subject two"
And I follow "Reply"
And the field "Discussion subscription" matches value "I don't want to be notified of new posts in this discussion"
Scenario: A guest should not be able to subscribe to a discussion
Given the following "activities" exist:
| activity | name | course | section | idnumber | type |
| forum | Test forum name | Acceptance test site | 1 | forum1 | general |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Test post subject one | Test post message one |
When I am on the "Test forum name" "forum activity" page logged in as guest
Then "Subscribe to this discussion" "checkbox" should not exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should not exist in the "Test post subject one" "table_row"
And I follow "Test post subject one"
And "Subscribe to this discussion" "checkbox" should not exist
And "Unsubscribe from this discussion" "checkbox" should not exist
Scenario: A user who is not logged in should not be able to subscribe to a discussion
Given the following "activities" exist:
| activity | name | course | section | idnumber | type |
| forum | Test forum name | Acceptance test site | 1 | forum1 | general |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Test post subject one | Test post message one |
When I am on site homepage
And I follow "Test forum name"
Then "Subscribe to this discussion" "checkbox" should not exist in the "Test post subject one" "table_row"
And "Unsubscribe from this discussion" "checkbox" should not exist in the "Test post subject one" "table_row"
And I follow "Test post subject one"
And "Subscribe to this discussion" "checkbox" should not exist
And "Unsubscribe from this discussion" "checkbox" should not exist
Scenario: A user can toggle their subscription preferences when viewing a discussion
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | forum1 |
| name | Test forum name |
| type | general |
| forcesubscribe | 0 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Test post subject one | Test post message one |
When I am on the "Test forum name" "forum activity" page logged in as student1
Then "Subscribe to forum" "link" should exist
And I follow "Test post subject one"
And "You are not subscribed to this discussion. Click to subscribe" "link" should exist
And I follow "Test forum name"
And I follow "Subscribe to forum"
And I should see "You will be notified of new posts in the forum 'Test forum name'"
And "Unsubscribe from forum" "link" should exist
And I follow "Test post subject one"
And "You are subscribed to this discussion. Click to unsubscribe" "link" should exist
And I follow "You are subscribed to this discussion. Click to unsubscribe"
And I should see "Student One will NOT be notified of new posts in 'Test post subject one' of 'Test forum name'"
And I follow "Test post subject one"
And "You are not subscribed to this discussion. Click to subscribe" "link" should exist
And I follow "Test forum name"
And I follow "Unsubscribe from forum"
And I should see "Student One will NOT be notified of new posts in 'Test forum name'"
And "Subscribe to forum" "link" should exist
And I follow "Test post subject one"
And "You are not subscribed to this discussion. Click to subscribe" "link" should exist
And I follow "You are not subscribed to this discussion. Click to subscribe"
And I should see "You will be notified of new posts in 'Test post subject one' in the forum 'Test forum name'."
And "Unsubscribe from this discussion" "checkbox" should exist in the "Test post subject one" "table_row"
And I follow "Subscribe to forum"
And I should see "You will be notified of new posts in the forum 'Test forum name'"
And "Unsubscribe from forum" "link" should exist
And I follow "Test post subject one"
And "You are subscribed to this discussion. Click to unsubscribe" "link" should exist
And I follow "Test forum name"
And I follow "Unsubscribe from forum"
And I should see "Student One will NOT be notified of new posts in 'Test forum name'"
And "Subscribe to forum" "link" should exist
And I follow "Test post subject one"
And "You are not subscribed to this discussion. Click to subscribe" "link" should exist
@@ -0,0 +1,62 @@
@mod @mod_forum
Feature: Students can edit or delete their forum posts within a set time limit
In order to refine forum posts
As a user
I need to edit or delete my forum posts within a certain period of time after posting
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| forum | Test forum name | C1 | forum |
Scenario: Edit forum post
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| student1 | forum | Forum post subject | This is the body |
And I am on the "Test forum name" "forum activity" page logged in as "student1"
And I follow "Forum post subject"
And I follow "Edit"
When I set the following fields to these values:
| Subject | Edited post subject |
| Message | Edited post body |
And I press "Save changes"
And I wait to be redirected
Then I should see "Edited post subject"
And I should see "Edited post body"
Scenario: Delete forum post
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| student1 | forum | Forum post subject | This is the body |
And I am on the "Test forum name" "forum activity" page logged in as "student1"
And I follow "Forum post subject"
When I follow "Delete"
And I press "Continue"
Then I should not see "Forum post subject"
@block_recent_activity
Scenario: Time limit expires
Given the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| recent_activity | Course | C1 | course-view-* | side-pre |
And the following config values are set as admin:
| maxeditingtime | 1 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message | timemodified |
| student1 | forum | Forum post subject | This is the body | ##now +1 second## |
And I am on the "Course 1" course page logged in as student1
And I should see "New forum posts:" in the "Recent activity" "block"
And I should see "Forum post subject" in the "Recent activity" "block"
When I wait "2" seconds
And I follow "Forum post subject"
Then I should not see "Edit" in the "region-main" "region"
And I should not see "Delete" in the "region-main" "region"
@@ -0,0 +1,52 @@
@mod @mod_forum
Feature: Teachers can edit or delete any forum post
In order to refine the forum contents
As a teacher
I need to edit or delete any user's forum posts
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| forum | Test forum name | C1 | forum1 |
And the following "mod_forum > discussions" exist:
| forum | course | user | name | message |
| forum1 | C1 | teacher1 | Teacher post subject | Teacher post message |
And the following "mod_forum > posts" exist:
| user | parentsubject | subject | message |
| student1 | Teacher post subject | Student post subject | Student post message |
Scenario: A teacher can delete another user's posts
When I am on the "Test forum name" "forum activity" page logged in as teacher1
And I follow "Teacher post subject"
And I click on "Delete" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][contains(., 'Student post subject')]" "xpath_element"
And I press "Continue"
Then I should not see "Student post subject"
And I should not see "Student post message"
Scenario: A teacher can edit another user's posts
When I am on the "Test forum name" "forum activity" page logged in as teacher1
And I follow "Teacher post subject"
And I click on "Edit" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][contains(., 'Student post subject')]" "xpath_element"
And I set the following fields to these values:
| Subject | Edited student subject |
And I press "Save changes"
And I wait to be redirected
Then I should see "Edited student subject"
And I should see "Edited by Teacher 1 - original submission"
Scenario: A student can't edit or delete another user's posts
When I am on the "Test forum name" "forum activity" page logged in as student1
And I follow "Teacher post subject"
Then I should not see "Edit" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][contains(., 'Teacher post subject')]" "xpath_element"
And I should not see "Delete" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][contains(., 'Teacher post subject')]" "xpath_element"
+64
View File
@@ -0,0 +1,64 @@
@mod @mod_forum @core_tag
Feature: Edited forum posts handle tags correctly
In order to get forum posts properly labelled
As a user
I need to introduce the tags while editing
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum name |
| idnumber | forum1 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher1 | forum1 | Teacher post subject | Teacher post message |
@javascript
Scenario: Forum post edition of custom tags works as expected
Given I am on the "Course 1" Course page logged in as student1
And I reply "Teacher post subject" post from "Test forum name" forum with:
| Subject | Student post subject |
| Message | Student post message |
| Tags | Tag1 |
Then I should see "Tag1" in the ".tag_list" "css_element"
And I click on "Edit" "link" in the "//div[@aria-label='Student post subject by Student 1']" "xpath_element"
Then I should see "Tag1" in the ".form-autocomplete-selection" "css_element"
@javascript
Scenario: Forum post edition of standard tags works as expected
Given the following "tags" exist:
| name | isstandard |
| OT1 | 1 |
| OT2 | 1 |
| OT3 | 1 |
And I am on the "Test forum name" "forum activity" page logged in as teacher1
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And I expand all fieldsets
And I open the autocomplete suggestions list
And I should see "OT1" in the ".form-autocomplete-suggestions" "css_element"
And I should see "OT2" in the ".form-autocomplete-suggestions" "css_element"
And I should see "OT3" in the ".form-autocomplete-suggestions" "css_element"
And I reply "Teacher post subject" post from "Test forum name" forum with:
| Subject | Student post subject |
| Message | Student post message |
| Tags | OT1, OT3 |
Then I should see "OT1" in the ".tag_list" "css_element"
And I should see "OT3" in the ".tag_list" "css_element"
And I should not see "OT2" in the ".tag_list" "css_element"
And I click on "Edit" "link" in the "//div[@aria-label='Student post subject by Teacher 1']" "xpath_element"
And I should see "OT1" in the ".form-autocomplete-selection" "css_element"
And I should see "OT3" in the ".form-autocomplete-selection" "css_element"
And I should not see "OT2" in the ".form-autocomplete-selection" "css_element"
@@ -0,0 +1,40 @@
@mod @mod_forum @javascript
Feature: A student can favourite a discussion via the forum settings menu
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum name |
| idnumber | forum1 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Discussion 1 | Discussion contents 1, first message |
And the following "mod_forum > posts" exist:
| user | parentsubject | subject | message |
| admin | Discussion 1 | Reply 1 to discussion 1 | Discussion contents 1, second message |
| student1 | Discussion 1 | Reply 2 to discussion 1 | Discussion contents 1, third message |
Scenario: Student can favourite a discussion from within an individual discussion
Given I am on the "Test forum name" "forum activity" page logged in as student1
When I open the action menu in "[data-container='discussion-tools']" "css_element"
And I click on "[title='Star this discussion']" "css_element"
And I wait "3" seconds
And I open the action menu in "[data-container='discussion-tools']" "css_element"
And I click on "[title='Unstar this discussion']" "css_element"
Scenario: Student can favourite a discussion from the discussion list
When I am on the "Test forum name" "forum activity" page logged in as student1
And I click on "Discussion 1" action menu
And I click on "[title='Star this discussion']" "css_element"
And I click on "Discussion 1" action menu
And I click on "[title='Unstar this discussion']" "css_element"
@@ -0,0 +1,55 @@
@mod @mod_forum @javascript
Feature: A teacher or admin can view breadcrumbs in the reply, delete, split, edit and add discussion advanced pages
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher | Teacher | Tom | teacher@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C1 | editingteacher |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum name |
| idnumber | forum1 |
Scenario: A teacher views add discussion topics advanced page
Given I am on the "Test forum name" "forum activity" page logged in as teacher
And I click on "Add discussion topic" "link"
When I click on "Advanced" "button"
And I should see "Add discussion topic"
Then I should see "Add discussion topic" in the ".breadcrumb" "css_element"
And I should see "Test forum name" in the ".breadcrumb" "css_element"
Scenario: A teacher adds posts and then verifies the breadcrumbs in the links
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher | forum1 | Test post subject one | Test post message one |
And the following "mod_forum > posts" exist:
| user | parentsubject | subject | message |
| teacher | Test post subject one | Reply 1 to discussion 1 | Discussion contents 1, second message |
And I am on the "Test forum name" "forum activity" page logged in as teacher
And I follow "Test post subject one"
When I follow "Edit"
Then I should see "Edit discussion topic"
And I should see "Edit discussion topic" in the ".breadcrumb" "css_element"
And I should see "Test post subject one" in the ".breadcrumb" "css_element"
And I click on "Cancel" "button"
And I follow "Delete"
And I should not see "Test forum description"
And I should see "Delete" in the ".breadcrumb" "css_element"
And I click on "Cancel" "button"
And I follow "Reply"
And I click on "Advanced" "button"
And I should not see "Test forum description"
And I should see "Reply to discussion"
And I should see "Add reply" in the ".breadcrumb" "css_element"
And I click on "Cancel" "button"
And I follow "Split"
And I should not see "Test forum description"
And I should see "Split discussion" in the ".breadcrumb" "css_element"
And I click on "Cancel" "button"
@@ -0,0 +1,102 @@
@mod @mod_forum @core_completion
Feature: View activity completion in the forum activity
In order to have visibility of forum completion requirements
As a student
I need to be able to view my forum completion progress
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Vinnie | Student1 | student1@example.com |
| teacher1 | Darrell | Teacher1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | enablecompletion | showcompletionconditions |
| Course 1 | C1 | 1 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| teacher1 | C1 | editingteacher |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | mh1 |
| name | Music history |
And I am on the "Music history" "forum activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the following fields to these values:
| Whole forum grading > Type | Point |
| Add requirements | 1 |
| View the activity | 1 |
| Receive a grade | 1 |
| completiongradeitemnumber | Whole forum |
| Any grade | 1 |
| completionpostsenabled | 1 |
| completionposts | 2 |
| completiondiscussionsenabled | 1 |
| completiondiscussions | 1 |
| completionrepliesenabled | 1 |
| completionreplies | 1 |
And I press "Save and display"
@javascript
Scenario: View automatic completion items as a teacher
When I am on the "Music history" "forum activity" page logged in as teacher1
Then "Music history" should have the "View" completion condition
And "Music history" should have the "Start discussions: 1" completion condition
And "Music history" should have the "Make forum posts: 2" completion condition
And "Music history" should have the "Post replies: 1" completion condition
And "Music history" should have the "Receive a grade" completion condition
@javascript
Scenario: View automatic completion items as a student
Given I am on the "Music history" "forum activity" page logged in as student1
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "todo"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "todo"
And the "Post replies: 1" completion condition of "Music history" is displayed as "todo"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And I add a new discussion to "Music history" forum with:
| Subject | Fun instruments |
| Message | I like drums |
And I am on the "Music history" "forum activity" page
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "todo"
And the "Post replies: 1" completion condition of "Music history" is displayed as "todo"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And I reply "Fun instruments" post from "Music history" forum with:
| Subject | Reply 1 to Fun instruments |
| Message | Guitar is also Fun |
And I am on the "Music history" "forum activity" page
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "done"
And the "Post replies: 1" completion condition of "Music history" is displayed as "done"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
# Grade the student
And I am on the "Music history" "forum activity" page logged in as teacher1
And I press "Grade users"
And I set the field "grade" to "33"
And I press "Save"
And I press "Close grader"
# All conditions should now be completed.
When I am on the "Music history" "forum activity" page logged in as student1
Then the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "done"
And the "Post replies: 1" completion condition of "Music history" is displayed as "done"
And the "Receive a grade" completion condition of "Music history" is displayed as "done"
@javascript
Scenario: Use manual completion
Given I am on the "Music history" "forum activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the field "Students must manually mark the activity as done" to "1"
And I press "Save and display"
# Teacher view.
And the manual completion button for "Music history" should be disabled
# Student view.
When I am on the "Music history" "forum activity" page logged in as student1
Then the manual completion button of "Music history" is displayed as "Mark as done"
And I toggle the manual completion state of "Music history"
And the manual completion button of "Music history" is displayed as "Done"
@@ -0,0 +1,137 @@
@mod @mod_forum @core_completion
Feature: Completion pass grade view activity completion in the forum activity
In order to have visibility of forum completion requirements
As a student
I need to be able to view my forum completion progress
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Vinnie | Student1 | student1@example.com |
| teacher1 | Darrell | Teacher1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category | enablecompletion |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| teacher1 | C1 | editingteacher |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | mh1 |
| name | Music history |
And I am on the "Music history" "forum activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the following fields to these values:
| Whole forum grading > Type | Point |
| Whole forum grading > Grade to pass | 50 |
| Add requirements | 1 |
| View the activity | 1 |
| Receive a grade | 1 |
| Passing grade | 1 |
| completiongradeitemnumber | Whole forum |
| completionpostsenabled | 1 |
| completionposts | 2 |
| completiondiscussionsenabled | 1 |
| completiondiscussions | 1 |
| completionrepliesenabled | 1 |
| completionreplies | 1 |
And I press "Save and display"
Scenario: View automatic completion items as a teacher
Given I am on the "Music history" "forum activity" page logged in as teacher1
Then "Music history" should have the "View" completion condition
And "Music history" should have the "Start discussions: 1" completion condition
And "Music history" should have the "Make forum posts: 2" completion condition
And "Music history" should have the "Post replies: 1" completion condition
And "Music history" should have the "Receive a grade" completion condition
And "Music history" should have the "Receive a passing grade" completion condition
@javascript
Scenario: View automatic completion items as a failing student
Given I am on the "Music history" "forum activity" page logged in as student1
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "todo"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "todo"
And the "Post replies: 1" completion condition of "Music history" is displayed as "todo"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "todo"
And I add a new discussion to "Music history" forum with:
| Subject | Fun instruments |
| Message | I like drums |
And I am on the "Music history" "forum activity" page
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "todo"
And the "Post replies: 1" completion condition of "Music history" is displayed as "todo"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "todo"
And I reply "Fun instruments" post from "Music history" forum with:
| Subject | Reply 1 to Fun instruments |
| Message | Guitar is also Fun |
And I am on the "Music history" "forum activity" page
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "done"
And the "Post replies: 1" completion condition of "Music history" is displayed as "done"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "todo"
# Grade the student
And I am on the "Music history" "forum activity" page logged in as teacher1
And I press "Grade users"
And I set the field "grade" to "33"
And I press "Save"
And I press "Close grader"
# All conditions should now be completed.
When I am on the "Music history" "forum activity" page logged in as student1
Then the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "done"
And the "Post replies: 1" completion condition of "Music history" is displayed as "done"
And the "Receive a grade" completion condition of "Music history" is displayed as "done"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "failed"
@javascript
Scenario: View automatic completion items as a passing student
Given I am on the "Music history" "forum activity" page logged in as student1
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "todo"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "todo"
And the "Post replies: 1" completion condition of "Music history" is displayed as "todo"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "todo"
And I add a new discussion to "Music history" forum with:
| Subject | Fun instruments |
| Message | I like drums |
And I am on the "Music history" "forum activity" page
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "todo"
And the "Post replies: 1" completion condition of "Music history" is displayed as "todo"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "todo"
And I reply "Fun instruments" post from "Music history" forum with:
| Subject | Reply 1 to Fun instruments |
| Message | Guitar is also Fun |
And I am on the "Music history" "forum activity" page
And the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "done"
And the "Post replies: 1" completion condition of "Music history" is displayed as "done"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "todo"
# Grade the student
And I am on the "Music history" "forum activity" page logged in as teacher1
And I press "Grade users"
And I set the field "grade" to "60"
And I press "Save"
And I press "Close grader"
# All conditions should now be completed.
When I am on the "Music history" "forum activity" page logged in as student1
Then the "View" completion condition of "Music history" is displayed as "done"
And the "Start discussions: 1" completion condition of "Music history" is displayed as "done"
And the "Make forum posts: 2" completion condition of "Music history" is displayed as "done"
And the "Post replies: 1" completion condition of "Music history" is displayed as "done"
And the "Receive a grade" completion condition of "Music history" is displayed as "done"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "done"
@@ -0,0 +1,69 @@
@mod @mod_forum
Feature: Forum posts display word count
In order to display forum word count
As a teacher
I need to be able to update forum and set "Display word count" to Yes
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | t1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
Scenario: Forum posts display word count for single simple discussion forum
Given the following "activities" exist:
| activity | course | name | type | displaywordcount | intro |
| forum | C1 | Forum 1 | single | 1 | Single forum post |
When I am on the "Forum 1" "forum activity" page logged in as teacher1
Then I should see "3 words"
And I am on the "Forum 1" "forum activity editing" page
And I expand all fieldsets
And I set the field "Display word count" to "No"
And I press "Save and display"
And I am on the "Forum 1" "forum activity" page
And I should not see "3 words"
Scenario: Forum posts display word count for blog-like forum
Given the following "activities" exist:
| activity | course | name | type | displaywordcount |
| forum | C1 | Forum 1 | blog | 1 |
And the following "mod_forum > discussions" exist:
| forum | name | subject | message |
| Forum 1 | Blog Forum | Blog Forum | This is a blog forum post |
When I am on the "Forum 1" "forum activity" page logged in as teacher1
Then I should see "6 words"
And I am on the "Forum 1" "forum activity editing" page
And I expand all fieldsets
And I set the field "Display word count" to "No"
And I press "Save and display"
And I am on the "Forum 1" "forum activity" page
And I should not see "6 words"
Scenario Outline: Forum posts display word count for other forum types
Given the following "activities" exist:
| activity | course | name | type | displaywordcount |
| forum | C1 | Forum 1 | <type> | 1 |
And the following "mod_forum > discussions" exist:
| forum | name | subject | message |
| Forum 1 | <typeext> | <typeext> | <message> |
When I am on the "Forum 1" "forum activity" page logged in as teacher1
And I follow "<typeext>"
Then I should see "<count> words"
And I am on the "Forum 1" "forum activity editing" page
And I expand all fieldsets
And I set the field "Display word count" to "No"
And I press "Save and display"
And I am on the "Forum 1" "forum activity" page
And I follow "<typeext>"
Then I should not see "<count> words"
Examples:
| type | typeext | message | count |
| general | General Forum | General discussion in forum | 4 |
| eachuser | Each User Forum | This is an each user forum post | 7 |
| qanda | Q and A Forum | This is a Q and A type forum | 8 |
+172
View File
@@ -0,0 +1,172 @@
@mod @mod_forum @javascript
Feature: Export forum
In order to parse forum data for linguistic analysis
As a teacher
I need to export the forum data for select users
Background: Add a forum and a discussion
Given 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 "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | type | course | idnumber |
| forum | Test forum 1 | general | C1 | 123 |
Scenario: Teacher can export forum
Given I am on the "Test forum 1" "forum activity" page logged in as teacher1
And I navigate to "Export" in current page administration
And I open the autocomplete suggestions list
And I should see "Student 1" in the ".form-autocomplete-suggestions" "css_element"
And I should see "Teacher 1" in the ".form-autocomplete-suggestions" "css_element"
And I should not see "Student 2" in the ".form-autocomplete-suggestions" "css_element"
# This will fail if an exception is thrown. This is the best we can do without the ability to use the download. Hence, there is no "Then" step.
And I click on "Export" "button"
Scenario: Students cannot export forum by default
Given I am on the "Test forum 1" "forum activity" page logged in as student1
Then "Export" "link" should not exist in current page administration
Scenario: User with the capability can export
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| mod/forum:exportforum | Allow | student | Course | C1 |
When I am on the "Test forum 1" "forum activity" page logged in as student1
And I navigate to "Export" in current page administration
And I open the autocomplete suggestions list
And I should see "Student 1" in the ".form-autocomplete-suggestions" "css_element"
And I should see "Teacher 1" in the ".form-autocomplete-suggestions" "css_element"
And I should not see "Student 2" in the ".form-autocomplete-suggestions" "css_element"
# This will fail if an exception is thrown. This is the best we can do without the ability to use the download. Hence, there is no "Then" step.
And I click on "Export" "button"
And I log out
Scenario: Group mode is respected when exporting discussions
Given the following "groups" exist:
| name | course | idnumber |
| G1 | C1 | G1 |
| G2 | C1 | G2 |
And the following "users" exist:
| username | firstname | lastname | email |
| teachera | Teacher | A | teacherA@example.com |
| teacherb | Teacher | B | teacherB@example.com |
| teacherc | Teacher | C | teacherC@example.com |
| teacherd | Teacher | D | teacherD@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teachera | C1 | teacher |
| teacherb | C1 | teacher |
| teacherc | C1 | teacher |
| teacherd | C1 | teacher |
And the following "group members" exist:
| user | group |
| teachera | G1 |
| teachera | G2 |
| teacherb | G1 |
| teacherc | G2 |
And the following "activities" exist:
| activity | course | idnumber | name | intro | type | section | groupmode |
| forum | C1 | 00001 | Separate groups forum | Standard forum description | general | 1 | 1 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message | group |
| teachera | Separate groups forum | Discussion 1 Group 1 | Test post message | G1 |
| teacherb | Separate groups forum | Discussion 2 Group 1 | Test post message | G1 |
| teachera | Separate groups forum | Discussion 1 Group 2 | Test post message | G2 |
| teacherc | Separate groups forum | Discussion 2 Group 2 | Test post message | G2 |
And I am on the "Separate groups forum" "forum activity" page logged in as teacher1
And I navigate to "Export" in current page administration
When I expand the "Users" autocomplete
# Editing teacher can see all users and discussions.
Then I should see "Teacher A" in the "Users" "autocomplete"
And I should see "Teacher B" in the "Users" "autocomplete"
And I should see "Teacher C" in the "Users" "autocomplete"
And I should see "Teacher D" in the "Users" "autocomplete"
And I should see "Teacher 1" in the "Users" "autocomplete"
And I should see "Student 1" in the "Users" "autocomplete"
And I press the escape key
And I expand the "Discussions" autocomplete
And I should see "Discussion 1 Group 1" in the "Discussions" "autocomplete"
And I should see "Discussion 2 Group 1" in the "Discussions" "autocomplete"
And I should see "Discussion 1 Group 2" in the "Discussions" "autocomplete"
And I should see "Discussion 2 Group 2" in the "Discussions" "autocomplete"
And I click on "Export" "button"
And I am on the "Separate groups forum" "forum activity" page logged in as teachera
And I navigate to "Export" in current page administration
When I expand the "Users" autocomplete
# Teacher A is is in both groups.
Then I should see "Teacher A" in the "Users" "autocomplete"
And I should see "Teacher B" in the "Users" "autocomplete"
And I should see "Teacher C" in the "Users" "autocomplete"
And I should not see "Teacher D" in the "Users" "autocomplete"
And I should not see "Teacher 1" in the "Users" "autocomplete"
And I should not see "Student 1" in the "Users" "autocomplete"
And I press the escape key
And I expand the "Discussions" autocomplete
And I should see "Discussion 1 Group 1" in the "Discussions" "autocomplete"
And I should see "Discussion 2 Group 1" in the "Discussions" "autocomplete"
And I should see "Discussion 1 Group 2" in the "Discussions" "autocomplete"
And I should see "Discussion 2 Group 2" in the "Discussions" "autocomplete"
And I click on "Export" "button"
And I am on the "Separate groups forum" "forum activity" page logged in as teacherb
And I navigate to "Export" in current page administration
When I expand the "Users" autocomplete
# Teacher B is in group 1.
Then I should see "Teacher A" in the "Users" "autocomplete"
And I should see "Teacher B" in the "Users" "autocomplete"
And I should not see "Teacher C" in the "Users" "autocomplete"
And I should not see "Teacher D" in the "Users" "autocomplete"
And I should not see "Teacher 1" in the "Users" "autocomplete"
And I should not see "Student 1" in the "Users" "autocomplete"
And I press the escape key
And I expand the "Discussions" autocomplete
And I should see "Discussion 1 Group 1" in the "Discussions" "autocomplete"
And I should see "Discussion 2 Group 1" in the "Discussions" "autocomplete"
And I should not see "Discussion 1 Group 2" in the "Discussions" "autocomplete"
And I should not see "Discussion 2 Group 2" in the "Discussions" "autocomplete"
And I click on "Export" "button"
And I am on the "Separate groups forum" "forum activity" page logged in as teacherc
And I navigate to "Export" in current page administration
When I expand the "Users" autocomplete
# Teacher C is in group 2.
Then I should see "Teacher A" in the "Users" "autocomplete"
And I should not see "Teacher B" in the "Users" "autocomplete"
And I should see "Teacher C" in the "Users" "autocomplete"
And I should not see "Teacher D" in the "Users" "autocomplete"
And I should not see "Teacher 1" in the "Users" "autocomplete"
And I should not see "Student 1" in the "Users" "autocomplete"
And I press the escape key
And I expand the "Discussions" autocomplete
And I should not see "Discussion 1 Group 1" in the "Discussions" "autocomplete"
And I should not see "Discussion 2 Group 1" in the "Discussions" "autocomplete"
And I should see "Discussion 1 Group 2" in the "Discussions" "autocomplete"
And I should see "Discussion 2 Group 2" in the "Discussions" "autocomplete"
And I click on "Export" "button"
And I am on the "Separate groups forum" "forum activity" page logged in as teacherd
And I navigate to "Export" in current page administration
When I expand the "Users" autocomplete
# Teacher D is in no group.
Then I should not see "Teacher A" in the "Users" "autocomplete"
And I should not see "Teacher B" in the "Users" "autocomplete"
And I should not see "Teacher C" in the "Users" "autocomplete"
And I should not see "Teacher D" in the "Users" "autocomplete"
And I should not see "Teacher 1" in the "Users" "autocomplete"
And I should not see "Student 1" in the "Users" "autocomplete"
And I press the escape key
And I expand the "Discussions" autocomplete
And I should not see "Discussion 1 Group 1" in the "Discussions" "autocomplete"
And I should not see "Discussion 2 Group 1" in the "Discussions" "autocomplete"
And I should not see "Discussion 1 Group 2" in the "Discussions" "autocomplete"
And I should not see "Discussion 2 Group 2" in the "Discussions" "autocomplete"
And I click on "Export" "button"
@@ -0,0 +1,57 @@
@mod @mod_forum
Feature: Each person posts one discussion forum type
In order to limit the number of discussions in a forum
As a teacher
I want to set up a forum that allows each person to post one discussion topic
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 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
And the following "activities" exist:
| activity | name | course | type | idnumber |
| forum | Forum 1 | C1 | eachuser | forum1 |
Scenario: Student can only post once on a 'Each person posts one discussion' forum
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| student1 | forum1 | student1 discussion | posted by student1 |
| student2 | forum1 | student2 discussion | posted by student2 |
When I am on the "Forum 1" "forum activity" page logged in as student3
# Only 1 discussion can be added to forum.
And I should see "This forum allows each person to start one discussion topic."
And I click on "Add discussion topic" "link"
And I set the following fields to these values:
| Subject | student3 discussion |
| Message | posted by student3 |
And I press "Post to forum"
# Confirm 2nd discussion topic cannot be added.
Then "Add discussion topic" "link" should not exist
# Confirm user can reply to other posts.
And I click on "student1 discussion" "link"
And I click on "Reply" "link"
And I set the following fields to these values:
| Message | Reply to student1 discussion |
And I press "Post to forum"
And I am on the "Forum 1" "forum activity" page
And I click on "student2 discussion" "link"
And I click on "Reply" "link"
And I set the following fields to these values:
| Message | Reply to student2 discussion |
And I press "Post to forum"
And I am on the "Forum 1" "forum activity" page
And I click on "student3 discussion" "link"
And I click on "Reply" "link"
And I set the following fields to these values:
| Message | Reply to student3 discussion |
And I press "Post to forum"
@@ -0,0 +1,55 @@
@mod @mod_forum @javascript
Feature: A teacher or admin can view subscriptions tab
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher | Teacher | Tom | teacher@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C1 | editingteacher |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum name |
Scenario: A teacher views view subscribers by default and views the Subscribers heading
Given I am on the "Test forum name" "forum activity" page logged in as teacher
When I navigate to "Subscriptions" in current page administration
Then I should see "View subscribers" in the "//div[@class='urlselect']//option[@selected]" "xpath_element"
And I should see "Subscribers"
And I should see "There are no subscribers yet for this forum"
Scenario: A teacher selects forced subscription and subscribers selector is not visible
Given I am on the "Test forum name" "forum activity" page logged in as teacher
And I navigate to "Subscriptions" in current page administration
When I select "Forced subscription" from the "Subscription mode" singleselect
And I should see "Everyone is now subscribed to this forum"
Then I should not see "View subscribers"
And I should not see "Manage subscribers"
And I should not see "Manage subscribers"
# Now select Optional subscription
And I select "Optional subscription" from the "Subscription mode" singleselect
And I should see "Everyone can now choose to be subscribed"
And I should see "View subscribers"
Scenario: A teacher selects forced subscription and subscribers selector is not visible
Given I am on the "Test forum name" "forum activity" page logged in as teacher
And I navigate to "Subscriptions" in current page administration
When I select "Manage subscribers" from the "jump" singleselect
And I should see "Manage subscribers"
Then "Subscription mode" "select" should not exist
And I should not see "Optional subscription"
And I should not see "Forced subscription"
And I should not see "Auto subscription"
And I should not see "Subscription disabled"
Scenario: A teacher selects reports tab and verify the heading
Given I am on the "Test forum name" "forum activity" page logged in as teacher
And I should see "There are no discussion topics yet in this forum" in the "//div[contains(@class, 'alert-info')]" "xpath_element"
And I navigate to "Reports" in current page administration
And I should see "Forum summary report"
And I should see "Nothing to display" in the "//div[contains(@class, 'alert-info')]" "xpath_element"
@@ -0,0 +1,26 @@
@mod @mod_forum @javascript
Feature: A teacher or admin when changes the subscription mode should land in the subscriptions tab
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher | Teacher | Tom | teacher@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C1 | editingteacher |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum name |
Scenario: A teacher views subscriptions tab and changes the subscriptions mode to forced mode and lands in subscription tab
Given I am on the "Test forum name" "forum activity" page logged in as teacher
And I navigate to "Subscriptions" in current page administration
When I select "Forced subscription" from the "Subscription mode" singleselect
And I should see "Everyone is now subscribed to this forum"
Then I should see "Forced subscription"
And I should not see "View subscribers"
And I should not see "Manage subscribers"
@@ -0,0 +1,67 @@
@mod @mod_forum
Feature: A user can control their own subscription preferences for a forum
In order to receive notifications for things I am interested in
As a user
I need to choose my forum subscriptions
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | One | student.one@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following "activities" exist:
| activity | course | idnumber | name | type |
| forum | C1 | forum1 | Test forum name | general |
And the following "mod_forum > discussions" exist:
| forum | course | user | name | message |
| forum1 | C1 | admin | Test post subject | Test post message |
And I am on the "Test forum name" "forum activity editing" page logged in as admin
Scenario: A disallowed subscription forum cannot be subscribed to
Given I set the following fields to these values:
| Subscription mode | Subscription disabled |
And I press "Save and return to course"
When I am on the "Test forum name" "forum activity" page logged in as student1
Then I should not see "Subscribe to this forum"
And I should not see "Unsubscribe from this forum"
And "You are subscribed to this discussion. Click to unsubscribe." "link" should not exist in the "Test post subject" "table_row"
And "You are not subscribed to this discussion. Click to subscribe." "link" should not exist in the "Test post subject" "table_row"
Scenario: A forced subscription forum cannot be subscribed to
Given I set the following fields to these values:
| Subscription mode | Forced subscription |
And I press "Save and return to course"
When I am on the "Test forum name" "forum activity" page logged in as student1
Then I should not see "Subscribe to this forum"
And I should not see "Unsubscribe from this forum"
And "You are subscribed to this discussion. Click to unsubscribe." "link" should not exist in the "Test post subject" "table_row"
And "You are not subscribed to this discussion. Click to subscribe." "link" should not exist in the "Test post subject" "table_row"
Scenario: An optional forum can be subscribed to
Given I set the following fields to these values:
| Subscription mode | Optional subscription |
And I press "Save and return to course"
When I am on the "Test forum name" "forum activity" page logged in as student1
Then I should see "Subscribe to forum"
And I should not see "Unsubscribe from forum"
And I follow "Subscribe to forum"
And I should see "You will be notified of new posts in the forum 'Test forum name'"
And I should see "Unsubscribe from forum"
And I should not see "Subscribe to forum"
Scenario: An Automatic forum can be unsubscribed from
Given I set the following fields to these values:
| Subscription mode | Auto subscription |
And I press "Save and return to course"
When I am on the "Test forum name" "forum activity" page logged in as student1
Then I should see "Unsubscribe from forum"
And I should not see "Subscribe to forum"
And I follow "Unsubscribe from forum"
And I should see "Student One will NOT be notified of new posts in 'Test forum name'"
And I should see "Subscribe to forum"
And I should not see "Unsubscribe from forum"
@@ -0,0 +1,109 @@
@mod @mod_forum
Feature: As a teacher I need to see an accurate list of subscribed users
In order to see who is subscribed to a forum
As a teacher
I need to view the list of subscribed users
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher | Teacher | Teacher | teacher@example.com |
| student1 | Student | 1 | student.1@example.com |
| student2 | Student | 2 | student.2@example.com |
| student3 | Student | 3 | student.3@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G2 |
And the following "groupings" exist:
| name | course | idnumber |
| Grouping 1 | C1 | GG1 |
And the following "grouping groups" exist:
| grouping | group |
| GG1 | G1 |
And the following "activities" exist:
| activity | course | idnumber | type | name | forcesubscribe |
| forum | C1 | 1 | general | Forced Forum 1 | 1 |
| forum | C1 | 0001 | general | Forced Forum 2 | |
| forum | C1 | 0002 | general | Forced Forum 3 | 2 |
And I log in as "teacher"
And I am on "Course 1" course homepage with editing mode on
@javascript
Scenario: A forced forum lists all subscribers
When I am on the "Forced Forum 1" "forum activity" page
And I navigate to "Subscriptions" in current page administration
Then I should see "Student 1"
And I should see "Teacher Teacher"
And I should see "Student 2"
And I should see "Student 3"
And I am on the "Forced Forum 1" "forum activity editing" page
And I expand all fieldsets
And I click on "Add restriction..." "button"
And I click on "Grouping" "button" in the "Add restriction..." "dialogue"
And I set the field with xpath "//select[@name='id']" to "Grouping 1"
And I press "Save and display"
And I navigate to "Subscriptions" in current page administration
And I should see "Student 1"
And I should see "Teacher Teacher"
And I should not see "Student 2"
And I should not see "Student 3"
Scenario: A forced forum does not allow to edit the subscribers
Given I am on the "Forced Forum 2" "forum activity editing" page
And I set the following fields to these values:
| Subscription mode | Forced subscription |
| Availability | Show on course page |
And I press "Save and return to course"
And I am on the "Forced Forum 2" "forum activity" page
And I navigate to "Subscriptions" in current page administration
Then I should see "Teacher Teacher"
And I should see "Student 1"
And I should see "Student 2"
And I should see "Student 3"
Scenario: A forced and hidden forum lists only teachers
Given I am on the "Forced Forum 2" "forum activity editing" page
And I set the following fields to these values:
| Subscription mode | Forced subscription |
| Availability | Hide on course page |
And I press "Save and return to course"
And I am on the "Forced Forum 2" "forum activity" page
And I navigate to "Subscriptions" in current page administration
Then I should see "Teacher Teacher"
And I should not see "Student 1"
And I should not see "Student 2"
And I should not see "Student 3"
@javascript
Scenario: An automatic forum lists all subscribers
When I am on the "Forced Forum 3" "forum activity" page
And I navigate to "Subscriptions" in current page administration
Then I should see "Student 1"
And I should see "Teacher Teacher"
And I should see "Student 2"
And I should see "Student 3"
And I am on the "Forced Forum 3" "forum activity editing" page
And I expand all fieldsets
And I click on "Add restriction..." "button"
And I click on "Grouping" "button" in the "Add restriction..." "dialogue"
And I set the field with xpath "//select[@name='id']" to "Grouping 1"
And I press "Save and display"
And I navigate to "Subscriptions" in current page administration
And I should see "Student 1"
And I should see "Teacher Teacher"
And I should not see "Student 2"
And I should not see "Student 3"
@@ -0,0 +1,101 @@
@mod @mod_forum
Feature: A user can control their default discussion subscription settings
In order to automatically subscribe to discussions
As a user
I can choose my default subscription preference
Background:
Given the following "users" exist:
| username | firstname | lastname | email | autosubscribe |
| student1 | Student | One | student.one@example.com | 1 |
| student2 | Student | Two | student.one@example.com | 0 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Test forum name | C1 | forump1 | general |
And I am on the "Test forum name" "forum activity editing" page logged in as admin
And I set the following fields to these values:
| Subscription mode | Optional subscription |
And I press "Save and return to course"
Scenario: Creating a new discussion in an optional forum follows user preferences
Given I am on the "Test forum name" "forum activity" page logged in as student1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then "input[name=discussionsubscribe]:checked" "css_element" should exist
And I am on the "Test forum name" "forum activity" page logged in as student2
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And "input[name=discussionsubscribe]:not(:checked)" "css_element" should exist
Scenario: Replying to an existing discussion in an optional forum follows user preferences
Given the following forum discussions exist in course "Course 1":
| user | forum | name | message |
| admin | Test forum name | Test post subject | Test post message |
And I am on the "Test forum name" "forum activity" page logged in as student1
And I follow "Test post subject"
When I follow "Reply"
Then "input[name=discussionsubscribe]:checked" "css_element" should exist
And I am on the "Test forum name" "forum activity" page logged in as student2
And I follow "Test post subject"
And I follow "Reply"
And "input[name=discussionsubscribe]:not(:checked)" "css_element" should exist
Scenario: Creating a new discussion in an automatic forum follows forum subscription
Given I am on the "Test forum name" "forum activity editing" page logged in as admin
And I set the following fields to these values:
| Subscription mode | Auto subscription |
And I press "Save and return to course"
And I am on the "Test forum name" "forum activity" page logged in as student1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then "input[name=discussionsubscribe]:checked" "css_element" should exist
And I am on the "Test forum name" "forum activity" page logged in as student2
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And "input[name=discussionsubscribe]:checked" "css_element" should exist
Scenario: Replying to an existing discussion in an automatic forum follows forum subscription
Given the following forum discussions exist in course "Course 1":
| user | forum | name | message |
| admin | Test forum name | Test post subject | Test post message |
And I am on the "Test forum name" "forum activity editing" page logged in as admin
And I set the following fields to these values:
| Subscription mode | Optional subscription |
And I press "Save and return to course"
And I am on the "Test forum name" "forum activity" page logged in as student1
And I follow "Test post subject"
When I follow "Reply"
Then "input[name=discussionsubscribe]:checked" "css_element" should exist
And I am on the "Test forum name" "forum activity" page logged in as student2
And I follow "Test post subject"
And I follow "Reply"
And "input[name=discussionsubscribe]:not(:checked)" "css_element" should exist
@javascript
Scenario: Replying to an existing discussion in an automatic forum which has been unsubscribed from follows user preferences
Given the following forum discussions exist in course "Course 1":
| user | forum | name | message |
| admin | Test forum name | Test post subject | Test post message |
And I am on the "Test forum name" "forum activity editing" page logged in as admin
And I set the following fields to these values:
| Subscription mode | Auto subscription |
And I press "Save and return to course"
And I am on the "Test forum name" "forum activity" page logged in as student1
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject" "table_row"
And I follow "Test post subject"
When I follow "Reply"
And I click on "Advanced" "button"
And "input[name=discussionsubscribe][checked]" "css_element" should exist
And I am on the "Test forum name" "forum activity" page logged in as student2
And I click on "input[id^=subscription-toggle]" "css_element" in the "Test post subject" "table_row"
And I follow "Test post subject"
And I follow "Reply"
And I click on "Advanced" "button"
And "input[name=discussionsubscribe]:not([checked])" "css_element" should exist
@@ -0,0 +1,54 @@
@mod @mod_forum
Feature: A teacher can control the subscription to a forum
In order to change individual user's subscriptions
As a course administrator
I can change subscription setting for my users
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher | Teacher | Tom | teacher@example.com |
| student1 | Student | 1 | student.1@example.com |
| student2 | Student | 2 | student.2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
Scenario: A teacher can change toggle subscription editing on and off
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | f01 |
| name | Test forum name |
When I am on the "Test forum name" "forum activity" page logged in as teacher
And I navigate to "Subscriptions" in current page administration
Then I select "Manage subscribers" from the "Subscribers" singleselect
And "Add" "button" should exist
And "Remove" "button" should exist
And I select "View subscribers" from the "Subscribers" singleselect
And the "Subscribers" select box should contain "View subscribers"
@javascript
Scenario Outline: Toggle forum subscription mode via settings navigation
Given the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Test forum |
| idnumber | F1 |
| type | general |
| forcesubscribe | <initialmode> |
When I am on the "Test forum" "forum activity" page logged in as "teacher"
And I navigate to "Subscriptions" in current page administration
And I select "<updatedmode>" from the "Subscription mode" singleselect
Then I should see "<updatedmodeconfirmed>"
Examples:
| initialmode | updatedmode | updatedmodeconfirmed |
| 1 | Optional subscription | Everyone can now choose to be subscribed |
| 0 | Forced subscription | Everyone is now subscribed to this forum |
| 0 | Auto subscription | Everyone is now subscribed to this forum |
| 0 | Subscription disabled | Subscriptions are now disallowed |
@@ -0,0 +1,36 @@
@mod @mod_forum
Feature: Changes to the subscription mode of a forum can change subcribers of a forum
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher | Teacher | Tom | teacher@example.com |
| student1 | Student | 1 | student.1@example.com |
| student2 | Student | 2 | student.2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activity" exists:
| activity | forum |
| idnumber | f01 |
| course | C1 |
| name | Test forum name |
| type | general |
| forcesubscribe | 1 |
Scenario: A change from Forced subscription to Auto subcription causes all participants to be subscribed
Given I am on the "Test forum name" "forum activity" page logged in as teacher
And I navigate to "Subscriptions" in current page administration
When I select "Auto subscription" from the "Subscription mode" singleselect
Then I should not see "There are no subscribers yet for this forum"
And I navigate to "Subscriptions" in current page administration
And the following should exist in the "subscribers-table" table:
| Full name | Email address |
| Student 1 | student.1@example.com |
| Student 2 | student.2@example.com |
| Teacher Tom | teacher@example.com |
+178
View File
@@ -0,0 +1,178 @@
@mod @mod_forum @core_grades @javascript
Feature: I can grade a students interaction across a forum
In order to assess a student's contributions
As a teacher
I can assign grades to a student based on their contributions
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | format | numsections |
| Course 1 | C1 | weeks | 5 |
And the following "grade categories" exist:
| fullname | course |
| Tutor | C1 |
| Peers | C1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "scales" exist:
| name | scale |
| Test Scale 1 | Disappointing, Good, Very good, Excellent |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | 0001 |
| name | Test Forum 1 |
And I log in as "teacher1"
And I change window size to "large"
And I am on "Course 1" course homepage with editing mode on
Scenario: Ensure that forum grade settings do not leak to Ratings
Given I am on the "Test Forum 1" "forum activity editing" page
And I expand all fieldsets
# Fields should be hidden when grading is not set.
When I set the field "Whole forum grading > Type" to "None"
Then "Whole forum grading > Grade to pass" "field" should not be visible
And "Whole forum grading > Grade category" "field" should not be visible
And "Whole forum grading > Maximum grade" "field" should not be visible
And "Ratings > Grade to pass" "field" should not be visible
And "Ratings > Grade category" "field" should not be visible
And "Ratings > Maximum grade" "field" should not be visible
# Only Whole forum grading fields should be visible.
When I set the field "Whole forum grading > Type" to "Point"
Then "Whole forum grading > Grade to pass" "field" should be visible
And "Whole forum grading > Grade category" "field" should be visible
And "Whole forum grading > Maximum grade" "field" should be visible
But "Ratings > Grade to pass" "field" should not be visible
And "Ratings > Grade category" "field" should not be visible
And "Ratings > Maximum grade" "field" should not be visible
# Save some values.
Given I set the field "Whole forum grading > Maximum grade" to "10"
And I set the field "Whole forum grading > Grade category" to "Tutor"
And I set the field "Whole forum grading > Grade to pass" to "4"
When I press "Save and return to course"
And I navigate to "View > Grader report" in the course gradebook
# There shouldn't be any Ratings grade item.
Then I should see "Test Forum 1 whole forum"
But I should not see "Test Forum 1 rating"
# The values saved should be reflected here.
And I click on grade item menu "Test Forum 1 whole forum" of type "gradeitem" on "grader" page
And I choose "Edit grade item" in the open action menu
When I click on "Show more..." "link" in the ".modal-dialog" "css_element"
Then the field "Maximum grade" matches value "10"
Then the field "Grade to pass" matches value "4"
And I should see "Tutor" in the "Parent category" "fieldset"
Scenario: Ensure that Ratings settings do not leak to Forum grading
Given I am on the "Test Forum 1" "forum activity editing" page
And I expand all fieldsets
# Fields should be hidden when grading is not set.
When I set the field "Ratings > Aggregate type" to "No ratings"
Then "Ratings > Type" "field" should not be visible
And "Ratings > Grade to pass" "field" should not be visible
And "Ratings > Grade category" "field" should not be visible
And "Ratings > Maximum grade" "field" should not be visible
And "Whole forum grading > Grade to pass" "field" should not be visible
And "Whole forum grading > Grade category" "field" should not be visible
And "Whole forum grading > Maximum grade" "field" should not be visible
# Set to "Count of ratings"
When I set the field "Ratings > Aggregate type" to "Count of ratings"
Then "Ratings > Type" "field" should be visible
When I set the field "Ratings > Type" to "None"
Then "Ratings > Grade to pass" "field" should not be visible
And "Ratings > Grade category" "field" should not be visible
And "Ratings > Maximum grade" "field" should not be visible
And "Whole forum grading > Grade to pass" "field" should not be visible
And "Whole forum grading > Grade category" "field" should not be visible
And "Whole forum grading > Maximum grade" "field" should not be visible
# Use point grading
When I set the field "Ratings > Type" to "Point"
Then "Ratings > Grade to pass" "field" should be visible
And "Ratings > Grade category" "field" should be visible
And "Ratings > Maximum grade" "field" should be visible
And "Whole forum grading > Grade to pass" "field" should not be visible
And "Whole forum grading > Grade category" "field" should not be visible
And "Whole forum grading > Maximum grade" "field" should not be visible
# Save some values.
Given I set the field "Ratings > Maximum grade" to "10"
And I set the field "Ratings > Grade category" to "Tutor"
And I set the field "Ratings > Grade to pass" to "4"
When I press "Save and return to course"
And I navigate to "View > Grader report" in the course gradebook
# There shouldn't be any Whole forum grade gradeitem.
Then I should see "Test Forum 1 rating"
But I should not see "Test Forum 1 whole forum"
# The values saved should be reflected here.
And I click on grade item menu "Test Forum 1 rating" of type "gradeitem" on "grader" page
And I choose "Edit grade item" in the open action menu
When I click on "Show more..." "link" in the ".modal-dialog" "css_element"
Then the field "Maximum grade" matches value "10"
Then the field "Grade to pass" matches value "4"
And I should see "Tutor" in the "Parent category" "fieldset"
Scenario: Setting both a rating and a whole forum grade does not bleed
Given I am on the "Test Forum 1" "forum activity editing" page
And I expand all fieldsets
And I set the field "Ratings > Aggregate type" to "Count of ratings"
And I set the field "Ratings > Type" to "Point"
And I set the field "Ratings > Maximum grade" to "100"
And I set the field "Ratings > Grade category" to "Peers"
And I set the field "Ratings > Grade to pass" to "40"
And I set the field "Whole forum grading > Type" to "Point"
And I set the field "Whole forum grading > Maximum grade" to "10"
And I set the field "Whole forum grading > Grade category" to "Tutor"
And I set the field "Whole forum grading > Grade to pass" to "4"
And I press "Save and return to course"
And I navigate to "View > Grader report" in the course gradebook
# There shouldn't be any Whole forum grade gradeitem.
Then I should see "Test Forum 1 rating"
And I should see "Test Forum 1 whole forum"
# The values saved should be reflected here.
And I click on grade item menu "Test Forum 1 rating" of type "gradeitem" on "grader" page
And I choose "Edit grade item" in the open action menu
When I click on "Show more..." "link" in the ".modal-dialog" "css_element"
Then the field "Maximum grade" matches value "100"
Then the field "Grade to pass" matches value "40"
And I should see "Peers" in the "Parent category" "fieldset"
And I press "Cancel"
And I click on grade item menu "Test Forum 1 whole forum" of type "gradeitem" on "grader" page
And I choose "Edit grade item" in the open action menu
When I click on "Show more..." "link" in the ".modal-dialog" "css_element"
Then the field "Maximum grade" matches value "10"
Then the field "Grade to pass" matches value "4"
And I should see "Tutor" in the "Parent category" "fieldset"
Scenario: Ensure that only gradable users are available in forum grading interface
Given I am on the "Test Forum 1" "forum activity editing" page logged in as "admin"
And I expand all fieldsets
When I set the field "Whole forum grading > Type" to "Point"
And I set the field "Whole forum grading > Maximum grade" to "10"
And I set the field "Whole forum grading > Grade to pass" to "4"
And I press "Save and display"
And I press "Grade users"
Then I should see "1 out of 1"
And I should see "Student 1"
And I should not see "Teacher 1"
And I press "Save changes and proceed to the next user"
And I should see "Student 1"
And I should not see "Teacher 1"
@@ -0,0 +1,55 @@
@mod @mod_forum @javascript
Feature: View discussion while grading in a forum
In order to grade efficiently
As a teacher
I want to be able to see the full discussion the student was taking part in.
Background:
# Student 1 needs to be created first or they will not be the first user on the grading screen.
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student.1@example.com |
| student2 | Student | 2 | student.2@example.com |
| teacher | Teacher | Tom | teacher@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| teacher | C1 | editingteacher |
And the following "activity" exists:
| activity | forum |
| name | Gradable forum |
| course | C1 |
| idnumber | forum1 |
| grade_forum | 100 |
| scale | 100 |
# If there is more than one pots for Student 1 the test will not be able to select the
# correct View discussion link, as there is no selector for thier container.
And the following forum discussions exist in course "Course 1":
| forum | user | name | message |
| Gradable forum | student1 | My topic | This is the thing I posted about |
And the following forum replies exist in course "Course 1":
| forum | user | discussion | message |
| Gradable forum | student2 | My topic | I disagree |
Scenario: Viewing a discussion
Given I am on the "Gradable forum" "forum activity" page logged in as teacher
And I press "Grade users"
When I press "View discussion"
Then I should see "I disagree" in the "My topic" "dialogue"
And I click on "Cancel" "button" in the "My topic" "dialogue"
And I should not see "I disagree"
Scenario: Viewing a discussion while grading is fullscreen
Given I am on the "Gradable forum" "forum activity" page logged in as teacher
And I press "Grade users"
# Uses the aria-label for the menu in in the grading interface.
And I press "Actions for the grader interface"
And I press "Toggle full screen"
When I press "View discussion"
Then I should see "I disagree" in the "My topic" "dialogue"
And I click on "Cancel" "button" in the "My topic" "dialogue"
And I should not see "I disagree"
@@ -0,0 +1,58 @@
@mod @mod_forum
Feature: Forums in 'No groups' mode allow posting to All participants for all users
In order to post to a forum in 'No groups' mode, which is in course which has groups
As any user
I need to post
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group A | C1 | G1 |
| Group B | C1 | G2 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| teacher1 | G2 |
| student1 | G1 |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Standard forum name | C1 | nogroups | 0 |
Scenario: Teacher can post
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
And I should not see "Group A"
And I should not see "Group B"
When I click on "Add discussion topic" "link"
Then I should not see "Post a copy to all groups"
And I should not see "Group" in the "form" "css_element"
And I set the following fields to these values:
| Subject | Teacher 1 -> Forum |
| Message | Teacher 1 -> Forum |
And I press "Post to forum"
And I wait to be redirected
And I should see "Teacher 1 -> Forum"
Scenario: Student can post
Given I am on the "Standard forum name" "forum activity" page logged in as student1
And I should not see "Group A"
And I should not see "Group B"
When I click on "Add discussion topic" "link"
Then I should not see "Post a copy to all groups"
And I should not see "Group" in the "form" "css_element"
And I set the following fields to these values:
| Subject | Student 1 -> Forum |
| Message | Student 1 -> Forum |
And I press "Post to forum"
And I wait to be redirected
And I should see "Student 1 -> Forum"
+143
View File
@@ -0,0 +1,143 @@
@mod @mod_forum @javascript
Feature: Guest and not logged users could see the option to add new post or reply
In order to guide users to create an account
As a guest or not logged user
I want to see the option to add new post or reply
Background:
Given the following config values are set as admin:
| enrol_guest | Yes |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher | Teacher | 1 | teacher@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C1 | editingteacher |
And I am on the "Course 1" "enrolment methods" page logged in as teacher
And I click on "Enable" "link" in the "Guest access" "table_row"
Scenario Outline: As a not enrolled guest I don't see the option to add a new discussion
Given the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Forum | C1 | forum | <type> |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher | forum | Forum discussion 1 | How awesome is this forum discussion? |
And I log out
And I am on "Course 1" course homepage
When I press "Access as a guest"
And I am on the "Forum" "forum activity" page
Then I should not see "Add discussion topic"
And I should see "Forum discussion 1"
And I click on "Forum discussion 1" "link"
And I should not see "Reply"
Examples:
| type |
| general |
| eachuser |
| qanda |
Scenario: As a not enrolled guest I don't see the option to add a new discussion in a single forum
Given the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Forum (single discussion) | C1 | forum | single |
And I log out
And I am on "Course 1" course homepage
When I press "Access as a guest"
And I am on the "Forum (single discussion)" "forum activity" page
Then I should not see "Add discussion topic"
And I should see "Forum (single discussion)"
And I should not see "Reply"
Scenario: As a not enrolled guest I don't see the option to add a new discussion in a blog type forum
Given the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Forum | C1 | forum | blog |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher | forum | Forum discussion 1 | How awesome is this forum discussion? |
And I log out
And I am on "Course 1" course homepage
When I press "Access as a guest"
And I am on the "Forum" "forum activity" page
Then I should not see "Add discussion topic"
And I should see "Forum discussion 1"
And I should not see "Reply"
Scenario Outline: As an enrolled guest I see the option to add a new discussion
Given I am on the "Course 1" "enrolment methods" page logged in as teacher
And I click on "Enable" "link" in the "Self enrolment" "table_row"
And the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Forum | C1 | forum | <type> |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher | forum | Forum discussion 1 | How awesome is this forum discussion? |
And I log out
And I am on "Course 1" course homepage
When I press "Access as a guest"
And I am on the "Forum" "forum activity" page
Then I should see "Add discussion topic"
And I click on "Add discussion topic" "link"
And I should see "Only logged in users can post to this forum"
And I click on "Cancel" "button"
And I should see "Forum discussion 1"
And I click on "Forum discussion 1" "link"
And I should see "Reply"
And I click on "Reply" "link"
And I should see "Only logged in users can post to this forum"
And I click on "Log in" "button"
And I should see "Log in"
Examples:
| type |
| general |
| eachuser |
| qanda |
Scenario: As an enrolled guest I see the option to reply in a single forum
Given I am on the "Course 1" "enrolment methods" page logged in as teacher
And I click on "Enable" "link" in the "Self enrolment" "table_row"
And the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Forum (single discussion) | C1 | forum | single |
And I log out
And I am on "Course 1" course homepage
When I press "Access as a guest"
And I am on the "Forum (single discussion)" "forum activity" page
And I should see "Forum (single discussion)"
Then I should see "Reply"
And I click on "Reply" "link"
And I should see "Only logged in users can post to this forum"
And I click on "Cancel" "button"
And I should see "Reply"
And I click on "Reply" "link"
And I click on "Log in" "button"
And I should see "Log in"
Scenario: As an enrolled guest I see the option to reply in a blog type forum
Given I am on the "Course 1" "enrolment methods" page logged in as teacher
And I click on "Enable" "link" in the "Self enrolment" "table_row"
And the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Forum | C1 | forum | blog |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher | forum | Forum discussion 1 | How awesome is this forum discussion? |
And I log out
And I am on "Course 1" course homepage
When I press "Access as a guest"
And I am on the "Forum" "forum activity" page
Then I should see "Add discussion topic"
And I click on "Add discussion topic" "link"
And I should see "Only logged in users can post to this forum"
And I click on "Cancel" "button"
And I should see "Forum discussion 1"
And I click on "Add discussion topic" "link"
And I should see "Only logged in users can post to this forum"
And I click on "Log in" "button"
And I should see "Log in"
@@ -0,0 +1,122 @@
@mod @mod_forum @filter @filter_displayh5p @core_h5p @_file_upload @_switch_iframe
Feature: Inline editing H5P content in mod_forum
In order to edit an existing H5P content
As a user
I need to see the button and access to the H5P editor
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| teacher2 | Teacher | 2 | teacher2@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | teacher |
| student1 | C1 | student |
And the following "contentbank content" exist:
| contextlevel | reference | contenttype | user | contentname | filepath |
| Course | C1 | contenttype_h5p | admin | Greeting card | /h5p/tests/fixtures/greeting-card.h5p |
And the following "activities" exist:
| activity | name | introformat | course | content | contentformat | idnumber |
| forum | ForumName1 | 1 | C1 | H5Ptest | 1 | 1 |
And the "displayh5p" filter is "on"
# Override this capability to let teachers and students to Turn editing on.
And the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/course:update | Allow | teacher | System | |
| moodle/course:update | Allow | student | System | |
@javascript @repository_contentbank
Scenario: Edit H5P content from a forum intro using copy to content bank file
Given I am on the "ForumName1" "forum activity editing" page logged in as admin
# Add H5P content to the forum description.
And I click on "Configure H5P content" "button" in the "#fitem_id_introeditor" "css_element"
And I click on "Browse repositories..." "button" in the "Insert H5P content" "dialogue"
And I select "Content bank" repository in file picker
And I click on "Greeting card" "file" in repository content area
And I click on "Make a copy of the file" "radio"
And I click on "Select this file" "button"
And I click on "Insert H5P" "button" in the "Insert H5P content" "dialogue"
And I click on "Save and display" "button"
And I switch to "h5p-iframe" class iframe
And I switch to "h5p-iframe" class iframe
And I should see "Hello world!"
And I switch to the main frame
# The Edit button is only displayed when editing mode is on.
And I should not see "Edit H5P content"
When I am on "Course 1" course homepage with editing mode on
And I am on the "ForumName1" "forum activity" page
Then I should see "Edit H5P content"
And I log out
# Check teacher1 can see the Edit button too.
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I am on the "ForumName1" "forum activity" page
And I should see "Edit H5P content"
And I log out
# Check teacher2 (non-editing teacher) can't see the Edit button, because she can't edit the forum activity.
And I log in as "teacher2"
And I am on "Course 1" course homepage with editing mode on
And I am on the "ForumName1" "forum activity" page
And I should not see "Edit H5P content"
And I log out
# Check student1 can't see the Edit button.
And I log in as "student1"
And I am on "Course 1" course homepage with editing mode on
And I am on the "ForumName1" "forum activity" page
And I should not see "Edit H5P content"
@javascript @repository_contentbank
Scenario: Edit H5P content from a forum post
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | 1 | Forum post by admin | Forum post by admin |
Given I am on the "ForumName1" "forum activity" page logged in as admin
And I follow "Forum post by admin"
And I click on "Edit" "link"
# Add H5P content to a forum post as admin.
And I set the following fields to these values:
| Subject | Forum post by admin |
And I click on "Configure H5P content" "button" in the "#fitem_id_message" "css_element"
And I click on "Browse repositories..." "button" in the "Insert H5P content" "dialogue"
And I select "Content bank" repository in file picker
And I click on "Greeting card" "file" in repository content area
And I click on "Select this file" "button"
And I click on "Insert H5P" "button" in the "Insert H5P content" "dialogue"
And I press "Save changes"
And I switch to "h5p-iframe" class iframe
And I switch to "h5p-iframe" class iframe
And I should see "Hello world!"
And I switch to the main frame
# The Edit button is only displayed when editing mode is on.
And I should not see "Edit H5P content"
When I am on "Course 1" course homepage with editing mode on
And I am on the "ForumName1" "forum activity" page
And I follow "Forum post by admin"
Then I should see "Edit H5P content"
And I log out
# Check teacher1 can see the Edit button because she can edit the post too.
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I am on the "ForumName1" "forum activity" page
And I follow "Forum post by admin"
And I should see "Edit H5P content"
And I log out
# Check teacher2 (non-editing teacher) can see the Edit button because she can edit the post too.
And I log in as "teacher2"
And I am on "Course 1" course homepage with editing mode on
And I am on the "ForumName1" "forum activity" page
And I follow "Forum post by admin"
And I should see "Edit H5P content"
And I log out
# Check student1 can't see the Edit button.
And I log in as "student1"
And I am on "Course 1" course homepage with editing mode on
And I am on the "ForumName1" "forum activity" page
And I follow "Forum post by admin"
And I should not see "Edit H5P content"
@@ -0,0 +1,42 @@
@mod @mod_forum @javascript
Feature: Students can reply to a discussion in page.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| teacher1 | C1 | editingteacher |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | 0001 |
| name | Test forum name |
And the following "mod_forum > discussions" exist:
| forum | course | user | name | message |
| 0001 | C1 | teacher1 | Discussion 1 | Discussion contents 1, first message |
| 0001 | C1 | teacher1 | Discussion 2 | Discussion contents 2, first message |
Scenario: Confirm inpage replies work
Given I am on the "Course 1" course page logged in as student1
When I reply "Discussion 2" post from "Test forum name" forum using an inpage reply with:
| post | Discussion contents 1, third message |
Then I should see "Discussion contents 1, third message"
And I reload the page
And I should see "Discussion contents 1, third message"
Scenario: Confirm inpage replies work - private reply
Given I am on the "Course 1" course page logged in as teacher1
When I reply "Discussion 2" post from "Test forum name" forum using an inpage reply with:
| post | Discussion contents 1, third message |
| privatereply | 1 |
Then I should see "Discussion contents 1, third message"
And I should see "This is a private reply. (Teachers and other users with the capability to view private replies can also see it.)"
And I reload the page
And I should see "Discussion contents 1, third message"
@@ -0,0 +1,34 @@
@mod @mod_forum
Feature: A teacher can move discussions between forums
In order to move a discussion
As a teacher
I need to use the move discussion selector
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
Scenario: A teacher can move discussions
Given the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Test forum 1 | C1 | forum1 | 0 |
| forum | Test forum 2 | C1 | forum2 | 0 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| student1 | forum1 | Discussion 1 | Test post message |
And I am on the "Test forum 1" "forum activity" page logged in as teacher1
And I follow "Discussion 1"
When I set the field "jump" to "Test forum 2"
And I press "Move"
Then I should see "This discussion has been moved to 'Test forum 2'."
And I press "Move"
And I should see "Discussion 1"
@@ -0,0 +1,34 @@
@mod @mod_forum
Feature: A user can view their posts and discussions
In order to ensure a user can view their posts and discussions
As a student
I need to view my post and discussions
Scenario: View the student's posts and discussions
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Test forum name | C1 | forum | 0 |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| student1 | forum | Forum discussion 1 | How awesome is this forum discussion? |
And the following "mod_forum > posts" exist:
| user | parentsubject | subject | message |
| student1 | Forum discussion 1 | Actually, I've seen better. | Actually, I've seen better. |
And I log in as "student1"
When I follow "Profile" in the user menu
And I follow "Forum posts"
Then I should see "How awesome is this forum discussion?"
And I should see "Actually, I've seen better."
And I follow "Profile" in the user menu
And I follow "Forum discussions"
And I should see "How awesome is this forum discussion?"
And I should not see "Actually, I've seen better."
@@ -0,0 +1,74 @@
@mod @mod_forum
Feature: Posting to forums in a course with no groups behaves correctly
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Standard forum | C1 | nogroups | 0 |
| forum | Visible forum | C1 | visgroups | 2 |
| forum | Separate forum | C1 | sepgroups | 1 |
Scenario: Teachers can post in standard forum
Given I am on the "Standard forum" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
Then I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Teacher -> All participants |
| Message | Teacher -> All participants |
And I press "Post to forum"
And I wait to be redirected
And I should see "Teacher -> All participants"
Scenario: Teachers can post in forum with separate groups
Given I am on the "Separate forum" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
Then I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Teacher -> All participants |
| Message | Teacher -> All participants |
And I press "Post to forum"
And I wait to be redirected
And I should see "Teacher -> All participants"
Scenario: Teachers can post in forum with visible groups
Given I am on the "Visible forum" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
Then I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Teacher -> All participants |
| Message | Teacher -> All participants |
And I press "Post to forum"
And I wait to be redirected
And I should see "Teacher -> All participants"
Scenario: Students can post in standard forum
Given I am on the "Standard forum" "forum activity" page logged in as student1
When I click on "Add discussion topic" "link"
Then I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Student -> All participants |
| Message | Student -> All participants |
And I press "Post to forum"
And I wait to be redirected
And I should see "Student -> All participants"
Scenario: Students cannot post in forum with separate groups
When I am on the "Separate forum" "forum activity" page logged in as student1
Then I should see "You are not able to create a discussion because you are not a member of any group."
And I should not see "Add discussion topic"
Scenario: Students cannot post in forum with visible groups
When I am on the "Visible forum" "forum activity" page logged in as student1
Then I should see "You are not able to create a discussion because you are not a member of any group."
And I should not see "Add discussion topic"
@@ -0,0 +1,38 @@
@mod @mod_forum
Feature: Ensure only users with appropriate permissions can export forum discussions.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
Scenario: A teacher can export discussions to a portfolio.
Given the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Test forum 1 | C1 | forum | 0 |
And I log in as "admin"
And the following config values are set as admin:
| enableportfolios | 1 |
And I navigate to "Plugins > Portfolios > Manage portfolios" in site administration
And I set portfolio instance "File download" to "Enabled and visible"
And I click on "Save" "button"
When I am on the "Test forum 1" "forum activity" page logged in as student1
And I add a new discussion to "Test forum 1" forum with:
| Subject | Discussion 1 |
| Message | Test post message |
And I reload the page
And I follow "Discussion 1"
Then "Export whole discussion to portfolio" "button" should not exist
And I am on the "Test forum 1" "forum activity" page logged in as teacher1
And I follow "Discussion 1"
And "Export whole discussion to portfolio" "button" should exist
And I press "Export whole discussion to portfolio"
And I should see "Exporting to portfolio"
@@ -0,0 +1,108 @@
@mod @mod_forum
Feature: A user with access to multiple groups should be able to post a copy of a message to all the groups they have access to
In order to post to all groups a user has access to
As a user
I need to have the option to post a copy of a message to all groups
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
| Course 2 | C2 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher1 | C2 | editingteacher |
| student1 | C1 | student |
| student1 | C2 | student |
| student2 | C1 | student |
| student2 | C2 | student |
| student3 | C1 | student |
| student3 | C2 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group A | C1 | C1G1 |
| Group B | C1 | C1G2 |
| Group C | C1 | C1G3 |
| Group A | C2 | C2G1 |
| Group B | C2 | C2G2 |
| Group C | C2 | C2G3 |
And the following "groupings" exist:
| name | course | idnumber |
| G1 | C2 | G1 |
And the following "group members" exist:
| user | group |
| teacher1 | C1G1 |
| teacher1 | C1G2 |
| teacher1 | C1G3 |
| teacher1 | C2G1 |
| teacher1 | C2G1 |
| student1 | C1G1 |
| student1 | C2G1 |
| student1 | C2G2 |
| student2 | C1G1 |
| student2 | C1G2 |
| student3 | C1G1 |
| student3 | C1G2 |
| student3 | C1G3 |
And the following "grouping groups" exist:
| grouping | group |
| G1 | C2G1 |
| G1 | C2G2 |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode | grouping |
| forum | No group forum | C1 | forum | 0 | |
| forum | Separate group forum | C1 | forum | 1 | |
| forum | Visible group forum | C1 | forum | 2 | |
| forum | Groupings forum | C2 | forum | 1 | G1 |
And the following forum discussions exist in course "Course 1":
| user | forum | name | message | posttomygroups |
| teacher1 | Separate group forum | Discussion 1 | test | 1 |
| teacher1 | Visible group forum | Discussion 1 | test | 1 |
Scenario: Teacher is able to post a copy of a message to all groups in a separate group forum
When I am on the "Separate group forum" "forum activity" page logged in as student1
Then I should see "Discussion 1"
And I am on the "Separate group forum" "forum activity" page logged in as student2
And I should see "Discussion 1"
And I am on the "Separate group forum" "forum activity" page logged in as student3
And I should see "Discussion 1"
Scenario: Teacher is able to post a copy of a message to all groups in a visible group forum
When I am on the "Visible group forum" "forum activity" page logged in as student1
Then I should see "Discussion 1"
And I am on the "Visible group forum" "forum activity" page logged in as student2
And I should see "Discussion 1"
And I am on the "Visible group forum" "forum activity" page logged in as student3
And I should see "Discussion 1"
Scenario: Teacher is unable to post a copy of a message to all groups in a no group forum
Given I am on the "No group forum" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
Then I should not see "Post a copy to all groups"
Scenario: Posts to all groups that have groupings should only display within the grouping and not to other groups
Given I log in as "teacher1"
And I am on "Course 2" course homepage
And I add a new discussion to "Groupings forum" forum with:
| Subject | Discussion 1 |
| Message | test |
| Post a copy to all groups | 1 |
When I am on the "Groupings forum" "forum activity" page logged in as student1
Then I should see "Discussion 1"
And I am on the "Groupings forum" "forum activity" page logged in as student2
And I should not see "Discussion 1"
@javascript @mod_forum_quick_discussion_post_to_all_groups
Scenario: The 'Post a copy to all groups' option can be available in the quick discussion creation form
Given I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Separate group forum"
When I click on "Add discussion topic" "link"
Then "Post a copy to all groups" "field" should exist
@@ -0,0 +1,73 @@
@mod @mod_forum
Feature: Blog posts are always displayed in reverse chronological order
In order to use forum as a blog
As a user
I need to see most recent blog posts first
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Course blog forum |
| type | blog |
#
# We need javascript/wait to prevent creation of the posts in the same second. The threads
# would then ignore each other in the prev/next navigation as the Forum is unable to compute
# the correct order.
#
@javascript
Scenario: Replying to a blog post or editing it does not affect its display order
Given I am on the "Course blog forum" "forum activity" page logged in as student1
#
# Add three posts into the blog.
#
When I add a new topic to "Course blog forum" forum with:
| Subject | Blog post 1 |
| Message | This is the first post |
And I add a new topic to "Course blog forum" forum with:
| Subject | Blog post 2 |
| Message | This is the second post |
And I add a new topic to "Course blog forum" forum with:
| Subject | Blog post 3 |
| Message | This is the third post |
#
# Edit one of the blog posts.
#
And I click on "Edit" "link" in the "//div[@aria-label='Blog post 2 by Student 1']" "xpath_element"
And I set the following fields to these values:
| Subject | Edited blog post 2 |
And I press "Save changes"
And I wait to be redirected
And I log out
#
# Reply to another blog post.
#
And I am on the "Course blog forum" "forum activity" page logged in as teacher1
And I click on "Discuss this topic" "link" in the "//div[@aria-label='Blog post 1 by Student 1']" "xpath_element"
And I reply "Blog post 1" post from "Course blog forum" forum with:
| Message | Reply to the first post |
And I am on the "Course blog forum" "forum activity" page
#
# Make sure the order of the blog posts is still reverse chronological.
#
Then I should see "This is the third post" in the "//article[position()=1]" "xpath_element"
And I should see "This is the second post" in the "//article[position()=2]" "xpath_element"
And I should see "This is the first post" in the "//article[position()=3]" "xpath_element"
#
# Make sure the next/prev navigation uses the same order of the posts.
#
And I click on "Discuss this topic" "link" in the "//div[@aria-label='Edited blog post 2 by Student 1']" "xpath_element"
And "//a[@aria-label='Next discussion: Blog post 3']" "xpath_element" should exist
And "//a[@aria-label='Previous discussion: Blog post 1']" "xpath_element" should exist
@@ -0,0 +1,69 @@
@mod @mod_forum
Feature: New discussions and discussions with recently added replies are displayed first
In order to use forum as a discussion tool
As a user
I need to see currently active discussions first
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | forum |
| name | Course general forum |
| idnumber | forum1 |
#
# Add three posts into the blog.
#
And the following "mod_forum > discussions" exist:
| user | forum | name | message | timemodified |
| student1 | forum1 | Forum post 1 | This is the first post | ##now +1 second## |
| student1 | forum1 | Forum post 2 | This is the second post | ##now +2 second## |
| student1 | forum1 | Forum post 3 | This is the third post | ##now +3 second## |
#
# We need javascript/wait to prevent creation of the posts in the same second. The threads
# would then ignore each other in the prev/next navigation as the Forum is unable to compute
# the correct order.
#
@javascript
Scenario: Replying to a forum post or editing it puts the discussion to the front
Given I am on the "Course general forum" "forum activity" page logged in as student1
#
# Edit one of the forum posts.
#
And I follow "Forum post 2"
And I click on "Edit" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][contains(., 'Forum post 2')]" "xpath_element"
And I set the following fields to these values:
| Subject | Edited forum post 2 |
And I press "Save changes"
And I wait to be redirected
#
# Reply to another forum post.
#
And I am on the "Course general forum" "forum activity" page logged in as teacher1
And I follow "Forum post 1"
When I reply "Forum post 1" post from "Course general forum" forum with:
| Message | Reply to the first post |
And I am on the "Course general forum" "forum activity" page
#
# Make sure the order of the forum posts is as expected, with most recent new participation first (ie excluding edits).
#
Then I should see "Forum post 1" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=1]" "xpath_element"
And I should see "Forum post 3" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=2]" "xpath_element"
And I should see "Edited forum post 2" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=3]" "xpath_element"
#
# Make sure the next/prev navigation uses the same order of the posts.
#
And I follow "Forum post 3"
And "//a[@aria-label='Next discussion: Forum post 1']" "xpath_element" should exist
And "//a[@aria-label='Previous discussion: Edited forum post 2']" "xpath_element" should exist
@@ -0,0 +1,51 @@
@mod @mod_forum
Feature: Forum posts can be replied to in private
In order to post feedback to my students
As a Teacher
I need to be able to reply privately to students
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| teacher2 | Teacher | 2 | teacher2@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Science 101 | C1 | 0 |
And the following "activities" exist:
| activity | name | course | idnumber |
| forum | Study discussions | C1 | forum |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| student1 | forum | Answers to the homework | Here are the answers to last night's homework. |
And the following forum replies exist in course "Science 101":
| user | forum | discussion | message | privatereplyto |
| teacher1 | Study discussions | Answers to the homework |How about you and I have a meeting after class about plagiarism?| student1 |
Scenario: As a teacher I can see my own response
Given I am on the "Study discussions" "forum activity" page logged in as teacher1
When I follow "Answers to the homework"
Then I should see "How about you and I have a meeting after class about plagiarism?"
Scenario: As a fellow teacher I can see the other teacher's response
Given I am on the "Study discussions" "forum activity" page logged in as teacher2
When I follow "Answers to the homework"
Then I should see "How about you and I have a meeting after class about plagiarism?"
Scenario: As the intended recipient I can see my own response
Given I am on the "Study discussions" "forum activity" page logged in as student1
When I follow "Answers to the homework"
Then I should see "How about you and I have a meeting after class about plagiarism?"
Scenario: As a non-privileged user I cannot see my own response
Given I am on the "Study discussions" "forum activity" page logged in as student2
When I follow "Answers to the homework"
Then I should not see "How about you and I have a meeting after class about plagiarism?"
@@ -0,0 +1,114 @@
@mod @mod_forum @block_recent_activity
Feature: Users can see the relevant recent forum posts from the recent activity block
In order to quickly see the updates from forums in my course
As a user
I need to be able to see the recent forum posts in the recent activity block
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| teacher1 | C1 | editingteacher |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G2 |
| teacher1 | G1 |
| teacher1 | G2 |
And the following "activities" exist:
| activity | name | course | idnumber | type | groupmode | visible |
| forum | Separate groups forum | C1 | forum1 | general | 1 | 1 |
| forum | Visible groups forum | C1 | forum2 | general | 2 | 1 |
| forum | Standard forum | C1 | forum3 | general | 0 | 1 |
| forum | Hidden forum | C1 | forum4 | general | 0 | 0 |
| forum | Q&A forum | C1 | forum5 | qanda | 0 | 1 |
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| recent_activity | Course | C1 | course-view-* | side-pre |
And I log in as "teacher1"
Scenario: Recent forum activity with separate group discussion
Given the following "mod_forum > discussions" exist:
| user | forum | name | message | group |
| teacher1 | forum1 | Group 1 separate discussion | Group 1 members only | G1 |
When I am on the "Course 1" course page logged in as student1
Then I should see "Group 1 separate discussion" in the "Recent activity" "block"
And I am on the "Course 1" course page logged in as student2
And I should not see "Group 1 separate discussion" in the "Recent activity" "block"
Scenario: Recent forum activity with visible groups discussion
Given the following "mod_forum > discussions" exist:
| user | forum | name | message | group |
| teacher1 | forum2 | Group 1 visible discussion | Not just for group 1 members | G1 |
When I am on the "Course 1" course page logged in as student1
Then I should see "Group 1 visible discussion" in the "Recent activity" "block"
And I am on the "Course 1" course page logged in as student2
And I should see "Group 1 visible discussion" in the "Recent activity" "block"
Scenario: Recent forum activity with recent post as a private reply
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher1 | forum3 | Standard forum discussion | Discuss anything under the sun here! |
And the following "mod_forum > posts" exist:
| user | parentsubject | subject | message | privatereplyto |
| teacher1 | Standard forum discussion | Teacher's private reply | This is a private reply | 1 |
And I am on "Course 1" course homepage
And I should see "Standard forum discussion" in the "Recent activity" "block"
And I should see "Teacher's private reply" in the "Recent activity" "block"
When I am on the "Course 1" course page logged in as student1
Then I should see "Standard forum discussion" in the "Recent activity" "block"
But I should not see "Teacher's private reply" in the "Recent activity" "block"
Scenario: Recent forum activity with recent post in a hidden forum
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher1 | forum4 | Hidden discussion | Should be hidden! |
And I am on "Course 1" course homepage
And I should see "Hidden discussion" in the "Recent activity" "block"
When I am on the "Course 1" course page logged in as student1
Then I should not see "Hidden discussion" in the "Recent activity" "block"
Scenario: Recent forum activity with question and answer forum
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| teacher1 | forum5 | The egg vs the chicken | Which came first? The egg or the chicken? |
And the following "mod_forum > posts" exist:
| user | parentsubject | subject | message |
| student1 | The egg vs the chicken | Student 1's answer | The egg! |
And I am on the "Course 1" course page logged in as student1
And I should see "The egg vs the chicken" in the "Recent activity" "block"
And I should see "Student 1's answer" in the "Recent activity" "block"
And the following config values are set as admin:
| maxeditingtime | 1 |
When I am on the "Course 1" course page logged in as student2
Then I should see "The egg vs the chicken" in the "Recent activity" "block"
But I should not see "Student 1's answer" in the "Recent activity" "block"
And I reply "The egg vs the chicken" post from "Q&A forum" forum with:
| Subject | Student 2's answer |
| Message | The chicken, duh! |
And I wait "2" seconds
And I am on "Course 1" course homepage
And I should see "Student 1's answer" in the "Recent activity" "block"
And I should see "Student 2's answer" in the "Recent activity" "block"
Scenario: Recent forum activity with timed discussion
Given the following "mod_forum > discussions" exist:
| user | forum | name | message | timeend |
| teacher1 | forum3 | Timed discussion | Discuss anything under the sun here... no more!!! | ##1 Jan 2020 08:00## |
And I am on "Course 1" course homepage
And I should see "Timed discussion" in the "Recent activity" "block"
When I am on the "Course 1" course page logged in as student1
Then I should not see "Timed discussion" in the "Recent activity" "block"
@@ -0,0 +1,270 @@
@mod @mod_forum
Feature: Posting to all groups in a separate group discussion is restricted to users with access to all groups
In order to post to all groups in a forum with separate groups
As a teacher
I need to have the accessallgroups capability
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| noneditor1 | Non-editing teacher | 1 | noneditor1@example.com |
| noneditor2 | Non-editing teacher | 2 | noneditor2@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 "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| noneditor1 | C1 | teacher |
| noneditor2 | C1 | teacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | participation |
| Group A | C1 | G1 | 1 |
| Group B | C1 | G2 | 1 |
| Group C | C1 | G3 | 1 |
| Group D | C1 | G4 | 0 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| teacher1 | G2 |
| noneditor1 | G1 |
| noneditor1 | G2 |
| noneditor1 | G3 |
| noneditor2 | G1 |
| noneditor2 | G2 |
| student1 | G1 |
| student2 | G1 |
| student2 | G2 |
| student3 | G4 |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Standard forum name | C1 | sepgroups | 1 |
And the following "mod_forum > discussions" exist:
| forum | name | subject | message | group |
| sepgroups | Initial Disc ALL | Initial Disc ALL | Disc ALL content | All participants |
| sepgroups | Initial Disc G1 | Initial Disc G1 | Disc G1 content | G1 |
| sepgroups | Initial Disc G2 | Initial Disc G2 | Disc G2 content | G2 |
| sepgroups | Initial Disc G3 | Initial Disc G3 | Disc G3 content | G3 |
Scenario: Teacher with accessallgroups can view all groups
When I am on the "Standard forum name" "forum activity" page logged in as teacher1
Then the "Separate groups" select box should contain "All participants"
And the "Separate groups" select box should contain "Group A"
And the "Separate groups" select box should contain "Group B"
And the "Separate groups" select box should contain "Group C"
And the "Separate groups" select box should not contain "Group D"
And I select "All participants" from the "Separate groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G1"
And I should see "Initial Disc G2"
And I should see "Initial Disc G2"
And I select "Group A" from the "Separate groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G1"
But I should not see "Initial Disc G2"
And I should not see "Initial Disc G3"
And I select "Group B" from the "Separate groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G2"
But I should not see "Initial Disc G1"
And I should not see "Initial Disc G3"
Scenario: Teacher with accessallgroups can select any group when posting
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then the "Group" select box should contain "All participants"
And the "Group" select box should contain "Group A"
And the "Group" select box should contain "Group B"
And the "Group" select box should contain "Group C"
And the "Group" select box should not contain "Group D"
And I should see "Post a copy to all groups"
Scenario: Teacher with accessallgroups can post in groups they are a member of
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
And I select "Group A" from the "Separate groups" singleselect
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then I should see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Teacher 1 -> Group B |
| Message | Teacher 1 -> Group B |
# Change the group in the post form.
| Group | Group B |
And I press "Post to forum"
And I wait to be redirected
# We should be redirected to the group that we selected when posting.
And the field "Separate groups" matches value "Group B"
And I should see "Group B" in the "Teacher 1 -> Group B" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Group B" "table_row"
And I should not see "Group C" in the "Teacher 1 -> Group B" "table_row"
# It should also be displayed under All participants
And I select "All participants" from the "Separate groups" singleselect
And I should see "Group B" in the "Teacher 1 -> Group B" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Group B" "table_row"
And I should not see "Group C" in the "Teacher 1 -> Group B" "table_row"
# It should not be displayed in Groups A, or C.
And I select "Group A" from the "Separate groups" singleselect
And I should not see "Teacher 1 -> Group B"
And I select "Group C" from the "Separate groups" singleselect
And I should not see "Teacher 1 -> Group B"
Scenario: Teacher with accessallgroups can post in groups they are not a member of
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
And I select "Group A" from the "Separate groups" singleselect
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then I should see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Teacher 1 -> Group C |
| Message | Teacher 1 -> Group C |
| Group | Group C |
And I press "Post to forum"
And I wait to be redirected
# We should be redirected to the group that we selected when posting.
And the field "Separate groups" matches value "Group C"
# We redirect to the group posted in automatically.
And I should see "Group C" in the "Teacher 1 -> Group C" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Group C" "table_row"
And I should not see "Group B" in the "Teacher 1 -> Group C" "table_row"
# It should also be displayed under All participants
And I select "All participants" from the "Separate groups" singleselect
And I should see "Group C" in the "Teacher 1 -> Group C" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Group C" "table_row"
And I should not see "Group B" in the "Teacher 1 -> Group C" "table_row"
# It should not be displayed in Groups A, or B.
And I select "Group A" from the "Separate groups" singleselect
And I should not see "Teacher 1 -> Group C"
And I select "Group B" from the "Separate groups" singleselect
And I should not see "Teacher 1 -> Group C"
Scenario: Teacher with accessallgroups can post to all groups
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And I set the following fields to these values:
| Subject | Teacher 1 -> Post to all |
| Message | Teacher 1 -> Post to all |
| Post a copy to all groups | 1 |
And I press "Post to forum"
And I wait to be redirected
# Posting to all groups means that we should be redirected to the page we started from.
And the field "Separate groups" matches value "All participants"
And I select "Group A" from the "Separate groups" singleselect
Then I should see "Group A" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group B" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group C" in the "Teacher 1 -> Post to all" "table_row"
And I select "Group B" from the "Separate groups" singleselect
And I should see "Group B" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group C" in the "Teacher 1 -> Post to all" "table_row"
And I select "Group C" from the "Separate groups" singleselect
And I should see "Group C" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group B" in the "Teacher 1 -> Post to all" "table_row"
# No point testing the "All participants".
Scenario: Students in one group can only post in their group
When I am on the "Standard forum name" "forum activity" page logged in as student1
Then I should see "Group A"
And I click on "Add discussion topic" "link"
And I should see "Group A"
And I should not see "Group B"
And I should not see "Group C"
And I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Student -> B |
| Message | Student -> B |
And I press "Post to forum"
And I wait to be redirected
And I should see "Group A" in the "Student -> B" "table_row"
And I should not see "Group B" in the "Student -> B" "table_row"
Scenario: Students in no group can see all group discussions, but not post.
When I am on the "Standard forum name" "forum activity" page logged in as student4
Then I should see "All participants"
And I should see "Initial Disc ALL"
And I should see "You are not able to create a discussion"
And I should not see "Add discussion topic"
Scenario: Students in non-participation groups can see all group discussions, but not post.
When I am on the "Standard forum name" "forum activity" page logged in as student3
Then I should see "All participants"
And I should see "Initial Disc ALL"
And I should see "You are not able to create a discussion"
And I should not see "Add discussion topic"
Scenario: Students in multiple group can post in all of their group individually
When I am on the "Standard forum name" "forum activity" page logged in as student2
And I select "Group A" from the "Separate groups" singleselect
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And the "Group" select box should not contain "All participants"
And the "Group" select box should contain "Group A"
And the "Group" select box should contain "Group B"
And the "Group" select box should not contain "Group C"
And I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Student -> B |
| Message | Student -> B |
| Group | Group B |
And I press "Post to forum"
And I wait to be redirected
# We should be redirected to the group that we selected when posting.
And the field "Separate groups" matches value "Group B"
And I should see "Group B" in the "Student -> B" "table_row"
And I should not see "Group A" in the "Student -> B" "table_row"
And I select "Group A" from the "Separate groups" singleselect
And I should not see "Student -> B"
# Now try posting in Group A (starting at Group B)
And I select "Group B" from the "Separate groups" singleselect
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And the "Group" select box should not contain "All participants"
And the "Group" select box should contain "Group A"
And the "Group" select box should contain "Group B"
And the "Group" select box should not contain "Group C"
And I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Student -> A |
| Message | Student -> A |
| Group | Group A |
And I press "Post to forum"
And I wait to be redirected
# We should be redirected to the group that we selected when posting.
And the field "Separate groups" matches value "Group A"
And I should see "Group A" in the "Student -> A" "table_row"
And I should not see "Group B" in the "Student -> A" "table_row"
And I select "Group B" from the "Separate groups" singleselect
And I should not see "Student -> A"
Scenario: Teacher in all groups but without accessallgroups can only post in their groups
Given I am on the "Standard forum name" "forum activity" page logged in as noneditor1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then the "Group" select box should not contain "All participants"
And the "Group" select box should contain "Group A"
And the "Group" select box should contain "Group B"
And I should see "Post a copy to all groups"
Scenario: Teacher in some groups and without accessallgroups can only post in their groups
Given I am on the "Standard forum name" "forum activity" page logged in as noneditor1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then the "Group" select box should not contain "All participants"
And the "Group" select box should contain "Group A"
And the "Group" select box should contain "Group B"
And I should see "Post a copy to all groups"
Scenario: Students can view all participants discussions in separate groups mode
When I am on the "Standard forum name" "forum activity" page logged in as student1
Then I should see "Initial Disc ALL"
@@ -0,0 +1,108 @@
@mod @mod_forum @core_grades
Feature: I can grade a students by group with separate groups
In order to assess a student's contributions
As a teacher
I can assign grades to a student based on their separate groups
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group A | C1 | G1 |
| Group B | C1 | G2 |
| Group C | C1 | G3 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G2 |
| student1 | G3 |
| student2 | G3 |
| student3 | G3 |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| name | Test Forum 1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I am on the "Test Forum 1" "forum activity editing" page
And I expand all fieldsets
And I set the following fields to these values:
| Forum name | Test Forum 1 |
| Description | Test |
And I set the field "Whole forum grading > Type" to "Point"
And I set the field "Common module settings > Group mode" to "Separate groups"
And I press "Save and display"
@javascript
Scenario: Grade users by group A
When I select "Group A" from the "Separate groups" singleselect
And I click on "Grade users" "button"
Then I should see "1 out of 1"
And I should not see "1 out of 2"
And I should not see "1 out of 3"
And I should see "Student 1"
And I should not see "Student 2"
@javascript
Scenario: Grade users by group B
And I select "Group B" from the "Separate groups" singleselect
And I click on "Grade users" "button"
Then I should see "1 out of 1"
And I should not see "1 out of 2"
And I should not see "1 out of 3"
And I should not see "Student 1"
And I should see "Student 2"
@javascript
Scenario: Grade users by group C
And I select "Group C" from the "Separate groups" singleselect
And I click on "Grade users" "button"
Then I should not see "1 out of 1"
And I should not see "1 out of 2"
And I should see "1 out of 3"
@javascript
Scenario: Teacher can see only the discussions which are joined to
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher2 | Teacher | 2 | teacher2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher2 | C1 | teacher |
And the following "group members" exist:
| user | group |
| teacher2 | G2 |
| teacher2 | G3 |
When I am on the "Test Forum 1" "forum activity" page logged in as student1
And I select "Group A" from the "Separate groups" singleselect
And I click on "Add discussion topic" "link"
And I set the following fields to these values:
| Subject | Discussion subject A |
| Message | Discussion message A |
And I press "Post to forum"
And I select "Group C" from the "Separate groups" singleselect
And I click on "Add discussion topic" "link"
And I set the following fields to these values:
| Subject | Discussion subject C |
| Message | Discussion message C |
And I press "Post to forum"
Then I am on the "Test Forum 1" "forum activity" page logged in as teacher2
And I select "Group C" from the "Separate groups" singleselect
And I click on "Grade users" "button"
And I should see "Student 1"
And I should see "Discussion subject C"
And I should not see "Discussion subject A"
@@ -0,0 +1,73 @@
@mod @mod_forum
Feature: Posting to groups in a separate group discussion when restricted to groupings
In order to post to groups in a forum with separate groups and groupings
As a teacher
I need to have groups configured to post to a group
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | teacher1 | teacher1 | teacher1@example.com |
| teacher2 | teacher2 | teacher2 | teacher2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | teacher |
And the following "groups" exist:
| name | course | idnumber |
| G1G1 | C1 | G1G1 |
| G1G2 | C1 | G1G2 |
| G2G1 | C1 | G2G1 |
And the following "groupings" exist:
| name | course | idnumber |
| G1 | C1 | G1 |
| G2 | C1 | G2 |
And the following "group members" exist:
| user | group |
| teacher1 | G1G1 |
| teacher1 | G1G2 |
| teacher1 | G2G1 |
| teacher2 | G1G1 |
| teacher2 | G1G2 |
| teacher2 | G2G1 |
And the following "grouping groups" exist:
| grouping | group |
| G1 | G1G1 |
| G1 | G1G2 |
| G2 | G2G1 |
And the following "activities" exist:
| activity | course | idnumber | name | intro | type | section | groupmode | grouping |
| forum | C1 | 00001 | Multiple groups forum | Standard forum description | general | 1 | 1 | G1 |
| forum | C1 | 00001 | Single groups forum | Standard forum description | general | 1 | 1 | G2 |
Scenario: Teacher with accessallgroups can post in all groups
Given I am on the "Multiple groups forum" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then the "Group" select box should contain "All participants"
And the "Group" select box should contain "G1G1"
And the "Group" select box should contain "G1G2"
And I am on "Course 1" course homepage
And I follow "Single groups forum"
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And the "Group" select box should contain "All participants"
And the "Group" select box should contain "G2G1"
And I should not see "Post a copy to all groups"
Scenario: Teacher in all groups but without accessallgroups can post in either group but not to All Participants
Given I am on the "Multiple groups forum" "forum activity" page logged in as teacher2
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then the "Group" select box should not contain "All participants"
And the "Group" select box should contain "G1G1"
And the "Group" select box should contain "G1G2"
And I am on "Course 1" course homepage
And I follow "Single groups forum"
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And I should see "G2G1"
And "Group" "select" should not exist
@@ -0,0 +1,35 @@
@mod @mod_forum
Feature: Single simple forum discussion type
In order to restrict the discussion topic to one
As a teacher
I need to create a forum with a single simple discussion
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | intro | type | course | idnumber |
| forum | Single discussion forum name | Single discussion forum description | single | C1 | forum |
Scenario: Teacher can start the single simple discussion
When I am on the "Single discussion forum name" "forum activity" page logged in as teacher1
Then I should see "Single discussion forum description" in the "div.firstpost.starter" "css_element"
And I should not see "Add a new discussion topic"
Scenario: Student can not add more discussions
Given I am on the "Course 1" course page logged in as student1
When I reply "Single discussion forum name" post from "Single discussion forum name" forum with:
| Subject | Reply to single discussion subject |
| Message | Reply to single discussion message |
Then I should not see "Add a new discussion topic"
And I should see "Reply" in the "div.firstpost.starter" "css_element"
And I should see "Reply to single discussion message"
@@ -0,0 +1,45 @@
@mod @mod_forum
Feature: Forum discussions can be split
In order to manage forum discussions in my course
As a Teacher
I need to be able to split threads to keep them on topic.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Science 101 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Study discussions | C1 | forump1 | general |
And the following "mod_forum > discussions" exist:
| forum | course | user | name | message |
| forump1 | C1 | teacher1 | Photosynthesis discussion | Lets discuss our learning about Photosynthesis this week in this thread. |
And the following "mod_forum > posts" exist:
| user | parentsubject | subject | message |
| student1 | Photosynthesis discussion | Mass number | Can anyone tell me which number is the mass number in the periodic table? |
| student2 | Mass number | | I would also like to know this |
Scenario: Split a forum discussion
Given I am on the "Study discussions" "forum activity" page logged in as teacher1
And I follow "Photosynthesis discussion"
When I follow "Split"
And I set the following fields to these values:
| Discussion name | Mass number in periodic table |
And I press "Split"
Then I should see "Mass number in periodic table"
And I follow "Study discussions"
And I should see "Teacher 1" in the "Photosynthesis" "table_row"
# Confirm that the last post author has been updated.
And I should not see "Student 2" in the "Photosynthesis" "table_row"
# Confirm that the current author has been shown for the new split discussion.
And I should see "Student 1" in the "Mass number in periodic table" "table_row"
@@ -0,0 +1,35 @@
@mod @mod_forum
Feature: Users can choose to set start and end time for display of their discussions
In order to temporarly hide discussions to students
As a teacher
I need to set a discussion time start and time end
Scenario: Student should not see the tooltip or the discussion
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | type |
| forum | Test forum name | C1 | forump1 | general |
And the following "mod_forum > discussions" exist:
| user | forum | name | message | timeend | timestart |
| admin | forump1 | Discussion 1 | Discussion contents 1, first message | | |
| admin | forump1 | Discussion 2 timed not visible | Discussion contents 2, first message | ##1 Jan 2014 08:00## | |
| admin | forump1 | Discussion 3 timed visible now | Discussion contents 3, first message | | 1 |
And the following config values are set as admin:
| forum_enabletimedposts | 1 |
And I am on the "Test forum name" "forum activity" page logged in as admin
And I should see "Discussion 2 timed"
And I should see "Discussion 3 timed"
And "[data-region=timed-label]" "css_element" should exist
When I am on the "Test forum name" "forum activity" page logged in as student1
Then I should see "Discussion 1"
And I should not see "Discussion 2 timed"
And "[data-region=timed-label]" "css_element" should not exist
And I should see "Discussion 3 timed"
@@ -0,0 +1,187 @@
@mod @mod_forum
Feature: A teacher can set one of 3 possible options for tracking read forum posts
In order to ease the forum posts follow up
As a user
I need to distinct the unread posts from the read ones
Background:
Given the following "users" exist:
| username | firstname | lastname | email | trackforums |
| student1 | Student | 1 | student1@example.com | 1 |
| student2 | Student | 2 | student2@example.com | 0 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
Scenario: Tracking forum posts off
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | forum1 |
| type | general |
| name | Test forum name |
| trackingtype | 0 |
And the following "mod_forum > discussion" exists:
| forum | forum1 |
| course | C1 |
| user | admin |
| name | Test post subject |
| message | Test post message |
When I am on the "Course 1" course page logged in as student1
Then I should not see "1 unread post"
And I follow "Test forum name"
And I should not see "Track unread posts"
Scenario: Tracking forum posts optional with user tracking on
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | forum1 |
| name | Test forum name |
| type | general |
| trackingtype | 1 |
And the following "mod_forum > discussion" exists:
| forum | forum1 |
| course | C1 |
| user | admin |
| name | Test post subject |
| message | Test post message |
When I am on the "Course 1" course page logged in as student1
Then I should see "1 unread post"
And I follow "Test forum name"
And I follow "Don't track unread posts"
And I wait to be redirected
And I am on "Course 1" course homepage
And I should not see "1 unread post"
And I follow "Test forum name"
And I follow "Track unread posts"
And I wait to be redirected
And I click on "1" "link" in the "Admin User" "table_row"
And I am on "Course 1" course homepage
And I should not see "1 unread post"
Scenario: Tracking forum posts optional with user tracking off
Given the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | 00001 |
| name | Test forum name |
| type | general |
| trackingtype | 1 |
And the following "mod_forum > discussion" exists:
| forum | 00001 |
| course | C1 |
| user | admin |
| name | Test post subject |
| message | Test post message |
When I am on the "Course 1" course page logged in as student2
Then I should not see "1 unread post"
And I follow "Test forum name"
And I should not see "Track unread posts"
Scenario: Tracking forum posts forced with user tracking on
Given the following config values are set as admin:
| forum_allowforcedreadtracking | 1 |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | 00001 |
| name | Test forum name |
| type | general |
| trackingtype | 2 |
And the following "mod_forum > discussion" exists:
| forum | 00001 |
| course | C1 |
| user | admin |
| name | Test post subject |
| message | Test post message |
When I am on the "Course 1" course page logged in as student1
Then I should see "1 unread post"
And I am on the "Test forum name" "forum activity" page
And I should not see "Don't track unread posts"
And I follow "Test post subject"
And I am on "Course 1" course homepage
And I should not see "1 unread post"
Scenario: Tracking forum posts forced with user tracking off
Given the following config values are set as admin:
| forum_allowforcedreadtracking | 1 |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | 00001 |
| name | Test forum name |
| type | general |
| trackingtype | 2 |
And the following "mod_forum > discussion" exists:
| forum | 00001 |
| course | C1 |
| user | admin |
| name | Test post subject |
| message | Test post message |
When I am on the "Course 1" course page logged in as student2
Then I should see "1 unread post"
And I am on the "Test forum name" "forum activity" page
And I should not see "Don't track unread posts"
And I follow "Test post subject"
And I am on "Course 1" course homepage
And I should not see "1 unread post"
Scenario: Tracking forum posts forced (with force disabled) with user tracking on
Given the following config values are set as admin:
| forum_allowforcedreadtracking | 1 |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | 00001 |
| name | Test forum name |
| type | general |
| trackingtype | 2 |
And the following "mod_forum > discussion" exists:
| forum | 00001 |
| course | C1 |
| user | admin |
| name | Test post subject |
| message | Test post message |
And the following config values are set as admin:
| forum_allowforcedreadtracking | 0 |
When I am on the "Course 1" course page logged in as student1
Then I should see "1 unread post"
And I follow "Test forum name"
And I follow "Don't track unread posts"
And I wait to be redirected
And I am on "Course 1" course homepage
And I should not see "1 unread post"
And I follow "Test forum name"
And I follow "Track unread posts"
And I wait to be redirected
And I click on "1" "link" in the "Admin User" "table_row"
And I am on "Course 1" course homepage
And I should not see "1 unread post"
Scenario: Tracking forum posts forced (with force disabled) with user tracking off
Given the following config values are set as admin:
| forum_allowforcedreadtracking | 1 |
And the following "activity" exists:
| activity | forum |
| course | C1 |
| idnumber | 00001 |
| name | Test forum name |
| type | general |
| trackingtype | 2 |
And the following "mod_forum > discussion" exists:
| forum | 00001 |
| course | C1 |
| user | admin |
| name | Test post subject |
| message | Test post message |
And the following config values are set as admin:
| forum_allowforcedreadtracking | 0 |
When I am on the "Course 1" course page logged in as student2
Then I should not see "1 unread post"
And I follow "Test forum name"
And I should not see "Track unread posts"
@@ -0,0 +1,275 @@
@mod @mod_forum
Feature: Posting to all groups in a visible group discussion is restricted to users with access to all groups
In order to post to all groups in a forum with visible groups
As a teacher
I need to have the accessallgroups capability
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@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 "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | participation |
| Group A | C1 | G1 | 1 |
| Group B | C1 | G2 | 1 |
| Group C | C1 | G3 | 1 |
| Group D | C1 | G4 | 0 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| teacher1 | G2 |
| student1 | G1 |
| student2 | G1 |
| student2 | G2 |
| student3 | G4 |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| forum | Standard forum name | C1 | groups | 2 |
And the following "mod_forum > discussions" exist:
| forum | name | subject | message | group |
| groups | Initial Disc ALL | Initial Disc ALL | Disc ALL content | All participants |
| groups | Initial Disc G1 | Initial Disc G1 | Disc G1 content | G1 |
| groups | Initial Disc G2 | Initial Disc G2 | Disc G2 content | G2 |
| groups | Initial Disc G3 | Initial Disc G3 | Disc G3 content | G3 |
Scenario: Teacher with accessallgroups can view all groups
When I am on the "Standard forum name" "forum activity" page logged in as teacher1
Then the "Visible groups" select box should contain "All participants"
And the "Visible groups" select box should contain "Group A"
And the "Visible groups" select box should contain "Group B"
And the "Visible groups" select box should contain "Group C"
And the "Visible groups" select box should not contain "Group D"
And I select "All participants" from the "Visible groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G1"
And I should see "Initial Disc G2"
And I should see "Initial Disc G2"
And I select "Group A" from the "Visible groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G1"
But I should not see "Initial Disc G2"
And I should not see "Initial Disc G3"
And I select "Group B" from the "Visible groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G2"
But I should not see "Initial Disc G1"
And I should not see "Initial Disc G3"
Scenario: Teacher with accessallgroups can select any group when posting
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then the "Group" select box should contain "All participants"
And the "Group" select box should contain "Group A"
And the "Group" select box should contain "Group B"
And the "Group" select box should contain "Group C"
And the "Group" select box should not contain "Group D"
And I should see "Post a copy to all groups"
Scenario: Teacher with accessallgroups can post in groups they are a member of
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
And I select "Group A" from the "Visible groups" singleselect
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then I should see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Teacher 1 -> Group B |
| Message | Teacher 1 -> Group B |
# Change the group in the post form.
| Group | Group B |
And I press "Post to forum"
And I wait to be redirected
# We should be redirected to the group that we selected when posting.
And the field "Visible groups" matches value "Group B"
And I should see "Group B" in the "Teacher 1 -> Group B" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Group B" "table_row"
And I should not see "Group C" in the "Teacher 1 -> Group B" "table_row"
# It should also be displayed under All participants
And I select "All participants" from the "Visible groups" singleselect
And I should see "Group B" in the "Teacher 1 -> Group B" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Group B" "table_row"
And I should not see "Group C" in the "Teacher 1 -> Group B" "table_row"
# It should not be displayed in Groups A, or C.
And I select "Group A" from the "Visible groups" singleselect
And I should not see "Teacher 1 -> Group B"
And I select "Group C" from the "Visible groups" singleselect
And I should not see "Teacher 1 -> Group B"
Scenario: Teacher with accessallgroups can post in groups they are not a member of
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
And I select "Group A" from the "Visible groups" singleselect
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
Then I should see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Teacher 1 -> Group C |
| Message | Teacher 1 -> Group C |
| Group | Group C |
And I press "Post to forum"
And I wait to be redirected
# We should be redirected to the group that we selected when posting.
And the field "Visible groups" matches value "Group C"
# We redirect to the group posted in automatically.
And I should see "Group C" in the "Teacher 1 -> Group C" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Group C" "table_row"
And I should not see "Group B" in the "Teacher 1 -> Group C" "table_row"
# It should also be displayed under All participants
And I select "All participants" from the "Visible groups" singleselect
And I should see "Group C" in the "Teacher 1 -> Group C" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Group C" "table_row"
And I should not see "Group B" in the "Teacher 1 -> Group C" "table_row"
# It should not be displayed in Groups A, or B.
And I select "Group A" from the "Visible groups" singleselect
And I should not see "Teacher 1 -> Group C"
And I select "Group B" from the "Visible groups" singleselect
And I should not see "Teacher 1 -> Group C"
Scenario: Teacher with accessallgroups can post to all groups
Given I am on the "Standard forum name" "forum activity" page logged in as teacher1
When I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And I set the following fields to these values:
| Subject | Teacher 1 -> Post to all |
| Message | Teacher 1 -> Post to all |
| Post a copy to all groups | 1 |
And I press "Post to forum"
And I wait to be redirected
# Posting to all groups means that we should be redirected to the page we started from.
And the field "Visible groups" matches value "All participants"
And I select "Group A" from the "Visible groups" singleselect
Then I should see "Group A" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group B" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group C" in the "Teacher 1 -> Post to all" "table_row"
And I select "Group B" from the "Visible groups" singleselect
And I should see "Group B" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group C" in the "Teacher 1 -> Post to all" "table_row"
And I select "Group C" from the "Visible groups" singleselect
And I should see "Group C" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group A" in the "Teacher 1 -> Post to all" "table_row"
And I should not see "Group B" in the "Teacher 1 -> Post to all" "table_row"
# No point testing the "All participants".
Scenario: Students can view all groups
When I am on the "Standard forum name" "forum activity" page logged in as student1
Then the "Visible groups" select box should contain "All participants"
And the "Visible groups" select box should contain "Group A"
And the "Visible groups" select box should contain "Group B"
And the "Visible groups" select box should contain "Group C"
And the "Visible groups" select box should not contain "Group D"
And I select "All participants" from the "Visible groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G1"
And I should see "Initial Disc G2"
And I should see "Initial Disc G2"
And I select "Group A" from the "Visible groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G1"
But I should not see "Initial Disc G2"
And I should not see "Initial Disc G3"
And I select "Group B" from the "Visible groups" singleselect
And I should see "Initial Disc ALL"
And I should see "Initial Disc G2"
But I should not see "Initial Disc G1"
And I should not see "Initial Disc G3"
Scenario: Students in one group can only post in their group
When I am on the "Standard forum name" "forum activity" page logged in as student1
Then I should see "Group A"
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And I should see "Group A"
And I should not see "Group B"
And I should not see "Group C"
And I should not see "Group D"
And I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Student -> B |
| Message | Student -> B |
And I press "Post to forum"
And I wait to be redirected
And I should see "Group A" in the "Student -> B" "table_row"
And I should not see "Group B" in the "Student -> B" "table_row"
Scenario: Students in multiple group can post in all of their group individually
When I am on the "Standard forum name" "forum activity" page logged in as student2
And I select "Group A" from the "Visible groups" singleselect
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And the "Group" select box should not contain "All participants"
And the "Group" select box should contain "Group A"
And the "Group" select box should contain "Group B"
And the "Group" select box should not contain "Group C"
And the "Group" select box should not contain "Group D"
And I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Student -> B |
| Message | Student -> B |
| Group | Group B |
And I press "Post to forum"
And I wait to be redirected
# We should be redirected to the group that we selected when posting.
And the field "Visible groups" matches value "Group B"
And I should see "Group B" in the "Student -> B" "table_row"
And I should not see "Group A" in the "Student -> B" "table_row"
And I select "Group A" from the "Visible groups" singleselect
And I should not see "Student -> B"
# Now try posting in Group A (starting at Group B)
And I select "Group B" from the "Visible groups" singleselect
And I click on "Add discussion topic" "link"
And I click on "Advanced" "button"
And the "Group" select box should not contain "All participants"
And the "Group" select box should contain "Group A"
And the "Group" select box should contain "Group B"
And the "Group" select box should not contain "Group C"
And I should not see "Post a copy to all groups"
And I set the following fields to these values:
| Subject | Student -> A |
| Message | Student -> A |
| Group | Group A |
And I press "Post to forum"
And I wait to be redirected
# We should be redirected to the group that we selected when posting.
And the field "Visible groups" matches value "Group A"
And I should see "Group A" in the "Student -> A" "table_row"
And I should not see "Group B" in the "Student -> A" "table_row"
And I select "Group B" from the "Visible groups" singleselect
And I should not see "Student -> A"
Scenario: Students in no group can see all discussions, but not post.
Given I log in as "student4"
And I am on "Course 1" course homepage
When I follow "Standard forum name"
And I select "All participants" from the "Visible groups" singleselect
Then I should see "Initial Disc ALL"
And I should see "Initial Disc G1"
And I should see "Initial Disc G2"
And I should see "Initial Disc G2"
And I should see "You are not able to create a discussion"
And I should not see "Add discussion topic"
Scenario: Students in non-participation groups can see all discussions, but not post.
Given I log in as "student3"
And I am on "Course 1" course homepage
When I follow "Standard forum name"
And I select "All participants" from the "Visible groups" singleselect
Then I should see "Initial Disc ALL"
And I should see "Initial Disc G1"
And I should see "Initial Disc G2"
And I should see "Initial Disc G2"
And I should see "You are not able to create a discussion"
And I should not see "Add discussion topic"
@@ -0,0 +1,521 @@
<?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 mod_forum;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The exported_posts builder tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class builders_exported_posts_test extends \advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/** @var \mod_forum\local\builders\exported_posts */
private $builder;
/**
* Set up function for tests.
*/
public function setUp(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
$builderfactory = \mod_forum\local\container::get_builder_factory();
$this->builder = $builderfactory->get_exported_posts_builder();
}
/**
* Tear down function for tests.
*/
public function tearDown(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
}
/**
* Convert the stdClass values into their proper entity classes.
*
* @param stdClass[] $forums List of forums
* @param stdClass[] $discussions List of discussions
* @param stdClass[] $posts List of posts
* @return array
*/
private function convert_to_entities(array $forums, array $discussions, array $posts) {
global $DB;
$entityfactory = \mod_forum\local\container::get_entity_factory();
return [
// Forums.
array_map(function($forum) use ($entityfactory, $DB) {
$course = $DB->get_record('course', ['id' => $forum->course]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
return $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
}, $forums),
// Discussions.
array_map(function($discussion) use ($entityfactory) {
return $entityfactory->get_discussion_from_stdClass($discussion);
}, $discussions),
// Posts.
array_map(function($post) use ($entityfactory) {
return $entityfactory->get_post_from_stdClass($post);
}, $posts)
];
}
/**
* Test the build function throws exception if not given all of the forums for
* the list of posts.
*/
public function test_build_throws_exception_on_missing_forums(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user);
[$discussion2, $post2] = $this->helper_post_to_forum($forum2, $user);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2],
[$post1, $post2]
);
$this->expectException('moodle_exception');
$this->builder->build($user, [$forums[0]], $discussions, $posts);
}
/**
* Test the build function throws exception if not given all of the discussions for
* the list of posts.
*/
public function test_build_throws_exception_on_missing_discussions(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user);
[$discussion2, $post2] = $this->helper_post_to_forum($forum2, $user);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2],
[$post1, $post2]
);
$this->expectException('moodle_exception');
$this->builder->build($user, $forums, [$discussions[0]], $posts);
}
/**
* Test the build function returns the exported posts in the order that the posts are
* given.
*/
public function test_build_returns_posts_in_order(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$user1 = $datagenerator->create_and_enrol($course);
$user2 = $datagenerator->create_and_enrol($course);
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post2, $user1);
$post8 = $this->helper_reply_to_post($post2, $user2);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Randomly order the posts.
shuffle($posts);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expectedpostids = array_map(function($post) {
return $post->get_id();
}, $posts);
$actualpostids = array_map(function($exportedpost) {
return (int) $exportedpost->id;
}, $exportedposts);
$this->assertEquals($expectedpostids, $actualpostids);
}
/**
* Test the build function loads authors.
*/
public function test_build_loads_authors(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$user3 = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
// These 2 replies from user 3 won't be inlcuded in the export.
$post7 = $this->helper_reply_to_post($post2, $user3);
$post8 = $this->helper_reply_to_post($post2, $user3);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6]
);
$datagenerator->enrol_user($user1->id, $course->id);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
// We didn't include any posts from user 3 so we shouldn't see the authors
// that match that user.
$expectedids = [$user1->id, $user2->id];
$actualids = array_unique(array_map(function($exportedpost) {
return (int) $exportedpost->author->id;
}, $exportedposts));
sort($expectedids);
sort($actualids);
$this->assertEquals($expectedids, $actualids);
}
/**
* Test the build function loads attachments.
*/
public function test_build_loads_attachments(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$user3 = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post5, $user3);
$post8 = $this->helper_reply_to_post($post5, $user3);
$filestorage = get_file_storage();
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Add an attachment to a post in forum 1.
$attachment1 = $filestorage->create_file_from_string(
[
'contextid' => $forums[0]->get_context()->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post1->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
// Add an attachment to a post in forum 2.
$attachment2 = $filestorage->create_file_from_string(
[
'contextid' => $forums[1]->get_context()->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post7->id,
'filepath' => '/',
'filename' => 'example2.jpg',
],
'image contents'
);
// Enrol the user so that they can see the posts.
$datagenerator->enrol_user($user1->id, $course->id);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expected = ['example1.jpg', 'example2.jpg'];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
if (!empty($exportedpost->attachments)) {
foreach ($exportedpost->attachments as $attachment) {
$carry[] = $attachment->filename;
}
}
return $carry;
}, []);
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual);
}
/**
* Test the build function loads author groups.
*/
public function test_build_loads_author_groups(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$user3 = $datagenerator->create_user();
$course1 = $datagenerator->create_course();
$course2 = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course1->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course1->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post5, $user3);
$post8 = $this->helper_reply_to_post($post5, $user3);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Enrol the user so that they can see the posts.
$datagenerator->enrol_user($user1->id, $course1->id);
$datagenerator->enrol_user($user1->id, $course2->id);
$datagenerator->enrol_user($user2->id, $course1->id);
$datagenerator->enrol_user($user2->id, $course2->id);
$datagenerator->enrol_user($user3->id, $course1->id);
$datagenerator->enrol_user($user3->id, $course2->id);
$group1 = $datagenerator->create_group(['courseid' => $course1->id]);
$group2 = $datagenerator->create_group(['courseid' => $course1->id]);
// This group shouldn't be included in the results since it's in a different course.
$group3 = $datagenerator->create_group(['courseid' => $course2->id]);
$datagenerator->create_group_member(['userid' => $user1->id, 'groupid' => $group1->id]);
$datagenerator->create_group_member(['userid' => $user2->id, 'groupid' => $group1->id]);
$datagenerator->create_group_member(['userid' => $user1->id, 'groupid' => $group2->id]);
$datagenerator->create_group_member(['userid' => $user1->id, 'groupid' => $group3->id]);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expected = [
$user1->id => [$group1->id, $group2->id],
$user2->id => [$group1->id],
$user3->id => []
];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
$author = $exportedpost->author;
$authorid = $author->id;
if (!isset($carry[$authorid])) {
$carry[$authorid] = array_map(function($group) {
return $group['id'];
}, $author->groups);
}
return $carry;
}, []);
$this->assertEquals($expected, $actual);
}
/**
* Test the build function loads tags.
*/
public function test_build_loads_tags(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$user3 = $datagenerator->create_user();
$course1 = $datagenerator->create_course();
$course2 = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course1->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course1->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post5, $user3);
$post8 = $this->helper_reply_to_post($post5, $user3);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Enrol the user so that they can see the posts.
$datagenerator->enrol_user($user1->id, $course1->id);
$datagenerator->enrol_user($user1->id, $course2->id);
$datagenerator->enrol_user($user2->id, $course1->id);
$datagenerator->enrol_user($user2->id, $course2->id);
$datagenerator->enrol_user($user3->id, $course1->id);
$datagenerator->enrol_user($user3->id, $course2->id);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post1->id, $forums[0]->get_context(), ['foo', 'bar']);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post4->id, $forums[0]->get_context(), ['foo', 'baz']);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post7->id, $forums[1]->get_context(), ['bip']);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expected = [
$post1->id => ['foo', 'bar'],
$post4->id => ['foo', 'baz'],
$post7->id => ['bip']
];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
if (!empty($exportedpost->tags)) {
$carry[$exportedpost->id] = array_map(function($tag) {
return $tag['displayname'];
}, $exportedpost->tags);
}
return $carry;
}, []);
$this->assertEquals($expected, $actual);
}
/**
* Test the build function loads read_receipts.
*/
public function test_build_loads_read_receipts(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user(['trackforums' => 1]);
$user2 = $datagenerator->create_user(['trackforums' => 0]);
$course1 = $datagenerator->create_course();
$course2 = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course1->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course1->id, 'trackingtype' => FORUM_TRACKING_OFF]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post5, $user1);
$post8 = $this->helper_reply_to_post($post5, $user1);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Enrol the user so that they can see the posts.
$datagenerator->enrol_user($user1->id, $course1->id);
$datagenerator->enrol_user($user1->id, $course2->id);
$datagenerator->enrol_user($user2->id, $course1->id);
$datagenerator->enrol_user($user2->id, $course2->id);
forum_tp_add_read_record($user1->id, $post1->id);
forum_tp_add_read_record($user1->id, $post4->id);
forum_tp_add_read_record($user1->id, $post7->id);
forum_tp_add_read_record($user2->id, $post1->id);
forum_tp_add_read_record($user2->id, $post4->id);
forum_tp_add_read_record($user2->id, $post7->id);
// User 1 has tracking enabled.
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expected = [
// Tracking set for forum 1 for user 1.
$post1->id => false,
$post2->id => true,
$post3->id => true,
$post4->id => false,
// Tracking is off for forum 2 so everything should be null.
$post5->id => null,
$post6->id => null,
$post7->id => null,
$post8->id => null
];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
$carry[$exportedpost->id] = $exportedpost->unread;
return $carry;
}, []);
$this->assertEquals($expected, $actual);
// User 2 has tracking disabled.
$exportedposts = $this->builder->build($user2, $forums, $discussions, $posts);
// Tracking is off for user 2 so everything should be null.
$expected = [
$post1->id => null,
$post2->id => null,
$post3->id => null,
$post4->id => null,
$post5->id => null,
$post6->id => null,
$post7->id => null,
$post8->id => null
];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
$carry[$exportedpost->id] = $exportedpost->unread;
return $carry;
}, []);
$this->assertEquals($expected, $actual);
}
}
+129
View File
@@ -0,0 +1,129 @@
<?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/>.
/**
* The forum module cron trait.
*
* @package mod_forum
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
trait mod_forum_tests_cron_trait {
/**
* Run the main cron task to queue all tasks, and ensure that posts
* were sent to the correct users.
*
* @param \stdClass[] $expectations The list of users, along with their expected count of messages and digests.
*/
protected function queue_tasks_and_assert($expectations = []) {
global $DB;
// Note, we cannot use expectOutputRegex because it only allows for a single RegExp.
ob_start();
\core\cron::setup_user();
$cron = new \mod_forum\task\cron_task();
$cron->execute();
$output = ob_get_contents();
ob_end_clean();
$uniqueusers = 0;
foreach ($expectations as $expect) {
$expect->digests = isset($expect->digests) ? $expect->digests : 0;
$expect->messages = isset($expect->messages) ? $expect->messages : 0;
$expect->mentioned = isset($expect->mentioned) ? $expect->mentioned : false;
if ($expect->digests || $expect->messages) {
$expect->mentioned = true;
}
if (!$expect->mentioned) {
$this->assertDoesNotMatchRegularExpression("/Queued 0 for {$expect->userid}/", $output);
} else {
$uniqueusers++;
$this->assertMatchesRegularExpression(
"/Queued {$expect->digests} digests and {$expect->messages} messages for {$expect->userid}/",
$output
);
}
}
if (empty($expectations)) {
$this->assertMatchesRegularExpression("/No posts found./", $output);
} else {
$this->assertMatchesRegularExpression("/Unique users: {$uniqueusers}/", $output);
}
// Update the forum queue for digests.
$DB->execute("UPDATE {forum_queue} SET timemodified = timemodified - 1");
}
/**
* Run any send_user_notifications tasks for the specified user, and
* ensure that the posts specified were sent.
*
* @param \stdClass $user
* @param \stdClass[] $posts
* @param bool $ignoreemptyposts
*/
protected function send_notifications_and_assert($user, $posts = [], $ignoreemptyposts = false) {
ob_start();
$this->runAdhocTasks(\mod_forum\task\send_user_notifications::class, $user->id);
$output = ob_get_contents();
ob_end_clean();
if (empty($posts) && !$ignoreemptyposts) {
$this->assertEquals('', $output);
} else {
$this->assertMatchesRegularExpression("/Sending messages to {$user->username}/", $output);
foreach ($posts as $post) {
$this->assertMatchesRegularExpression("/Post {$post->id} sent/", $output);
}
$count = count($posts);
$this->assertMatchesRegularExpression("/Sent {$count} messages with 0 failures/", $output);
}
}
/**
* Run any send_user_digests tasks for the specified user, and
* ensure that the posts specified were sent.
*
* @param \stdClass $user
* @param \stdClass[] $fullposts
* @param \stdClass[] $shortposts
*/
protected function send_digests_and_assert($user, $fullposts = [], $shortposts = []) {
ob_start();
$this->runAdhocTasks(\mod_forum\task\send_user_digests::class, $user->id);
$output = ob_get_contents();
ob_end_clean();
if (empty($shortposts) && empty($fullposts)) {
$this->assertEquals('', $output);
$this->assertMatchesRegularExpression("/Digest sent with 0 messages./", $output);
} else {
$this->assertMatchesRegularExpression("/Sending forum digests for {$user->username}/", $output);
foreach ($fullposts as $post) {
$this->assertMatchesRegularExpression("/Adding post {$post->id} in format 1/", $output);
}
foreach ($shortposts as $post) {
$this->assertMatchesRegularExpression("/Adding post {$post->id} in format 2/", $output);
}
$count = count($fullposts) + count($shortposts);
$this->assertMatchesRegularExpression("/Digest sent with {$count} messages./", $output);
}
}
}
+273
View File
@@ -0,0 +1,273 @@
<?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/>.
/**
* Contains unit tests for core_completion/activity_custom_completion.
*
* @package mod_forum
* @copyright Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_forum;
use advanced_testcase;
use cm_info;
use coding_exception;
use mod_forum\completion\custom_completion;
use moodle_exception;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/completionlib.php');
require_once($CFG->dirroot . '/mod/forum/tests/generator/lib.php');
require_once($CFG->dirroot . '/mod/forum/tests/generator_trait.php');
/**
* Class for unit testing mod_forum/activity_custom_completion.
*
* @package mod_forum
* @copyright Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_completion_test extends advanced_testcase {
use \mod_forum_tests_generator_trait;
/**
* Data provider for get_state().
*
* @return array[]
*/
public function get_state_provider(): array {
return [
'Undefined rule' => [
'somenonexistentrule', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, coding_exception::class
],
'Completion discussions rule not available' => [
'completiondiscussions', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class
],
'Completion discussions rule available, user has not created discussion' => [
'completiondiscussions', 0, COMPLETION_TRACKING_AUTOMATIC, 5, 0, 0, COMPLETION_INCOMPLETE, null
],
'Rule available, user has created discussions' => [
'completiondiscussions', 5, COMPLETION_TRACKING_AUTOMATIC, 5, 0, 0, COMPLETION_COMPLETE, null
],
'Completion replies rule not available' => [
'completionreplies', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class
],
'Rule available, user has not replied' => [
'completionreplies', 0, COMPLETION_TRACKING_AUTOMATIC, 0, 5, 0, COMPLETION_INCOMPLETE, null
],
'Rule available, user has created replied' => [
'completionreplies', 5, COMPLETION_TRACKING_AUTOMATIC, 0, 5, 0, COMPLETION_COMPLETE, null
],
'Completion posts rule not available' => [
'completionposts', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class
],
'Rule available, user has not posted' => [
'completionposts', 0, COMPLETION_TRACKING_AUTOMATIC, 0, 0, 5, COMPLETION_INCOMPLETE, null
],
'Rule available, user has posted' => [
'completionposts', 5, COMPLETION_TRACKING_AUTOMATIC, 0, 0, 5, COMPLETION_COMPLETE, null
],
];
}
/**
* Test for get_state().
*
* @dataProvider get_state_provider
* @param string $rule The custom completion rule.
* @param int $rulecount Quantity of discussions, replies or posts to be created.
* @param int $available Whether this rule is available.
* @param int|null $discussions The number of discussions.
* @param int|null $replies The number of replies.
* @param int|null $posts The number of posts.
* @param int|null $status Expected status.
* @param string|null $exception Expected exception.
*/
public function test_get_state(string $rule, int $rulecount, int $available, ?int $discussions, ?int $replies,
?int $posts, ?int $status, ?string $exception): void {
if (!is_null($exception)) {
$this->expectException($exception);
}
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course(['enablecompletion' => COMPLETION_ENABLED]);
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$params = [
'course' => $course->id,
'completion' => $available,
'completiondiscussions' => $discussions,
'completionreplies' => $replies,
'completionposts' => $posts
];
$forum = $this->getDataGenerator()->create_module('forum', $params);
$cm = get_coursemodule_from_instance('forum', $forum->id);
if ($rulecount > 0) {
if ($rule == 'completiondiscussions') {
// Create x number of discussions.
for ($i = 0; $i < $rulecount; $i++) {
$forumgenerator->create_discussion((object) [
'course' => $forum->course,
'userid' => $student->id,
'forum' => $forum->id,
]);
}
} else if ($rule == 'completionreplies') {
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $student);
for ($i = 0; $i < $rulecount; $i++) {
$this->helper_reply_to_post($post1, $student);
}
} else if ($rule == 'completionposts') {
for ($i = 0; $i < $rulecount; $i++) {
$this->helper_post_to_forum($forum, $student);
}
}
}
// Make sure we're using a cm_info object.
$cm = cm_info::create($cm);
$customcompletion = new custom_completion($cm, (int)$student->id);
$this->assertEquals($status, $customcompletion->get_state($rule));
}
/**
* Test for get_defined_custom_rules().
*/
public function test_get_defined_custom_rules(): void {
$rules = custom_completion::get_defined_custom_rules();
$this->assertCount(3, $rules);
$this->assertEquals('completiondiscussions', reset($rules));
}
/**
* Test for get_defined_custom_rule_descriptions().
*/
public function test_get_custom_rule_descriptions(): void {
// Get defined custom rules.
$rules = custom_completion::get_defined_custom_rules();
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Instantiate a custom_completion object using the mocked cm_info.
$customcompletion = new custom_completion($mockcminfo, 1);
// Get custom rule descriptions.
$ruledescriptions = $customcompletion->get_custom_rule_descriptions();
// Confirm that defined rules and rule descriptions are consistent with each other.
$this->assertEquals(count($rules), count($ruledescriptions));
foreach ($rules as $rule) {
$this->assertArrayHasKey($rule, $ruledescriptions);
}
}
/**
* Test for is_defined().
*/
public function test_is_defined(): void {
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->getMock();
$customcompletion = new custom_completion($mockcminfo, 1);
// Rule is defined.
$this->assertTrue($customcompletion->is_defined('completiondiscussions'));
// Undefined rule.
$this->assertFalse($customcompletion->is_defined('somerandomrule'));
}
/**
* Data provider for test_get_available_custom_rules().
*
* @return array[]
*/
public function get_available_custom_rules_provider(): array {
return [
'Completion discussions available' => [
COMPLETION_ENABLED, ['completiondiscussions']
],
'Completion discussions not available' => [
COMPLETION_DISABLED, []
],
'Completion replies available' => [
COMPLETION_ENABLED, ['completionreplies']
],
'Completion replies not available' => [
COMPLETION_DISABLED, []
],
'Completion posts available' => [
COMPLETION_ENABLED, ['completionposts']
],
'Completion posts not available' => [
COMPLETION_DISABLED, []
],
];
}
/**
* Test for get_available_custom_rules().
*
* @dataProvider get_available_custom_rules_provider
* @param int $status
* @param array $expected
*/
public function test_get_available_custom_rules(int $status, array $expected): void {
$customdataval = [
'customcompletionrules' => []
];
if ($status == COMPLETION_ENABLED) {
$rule = $expected[0];
$customdataval = [
'customcompletionrules' => [$rule => $status]
];
}
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Mock the return of magic getter for the customdata attribute.
$mockcminfo->expects($this->any())
->method('__get')
->with('customdata')
->willReturn($customdataval);
$customcompletion = new custom_completion($mockcminfo, 1);
$this->assertEquals($expected, $customcompletion->get_available_custom_rules());
}
}
+100
View File
@@ -0,0 +1,100 @@
<?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/>.
/**
* Contains unit tests for mod_forum\dates.
*
* @package mod_forum
* @category test
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_forum;
use advanced_testcase;
use cm_info;
use core\activity_dates;
/**
* Class for unit testing mod_forum\dates.
*
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dates_test extends advanced_testcase {
/**
* Data provider for get_dates_for_module().
* @return array[]
*/
public function get_dates_for_module_provider(): array {
$now = time();
$before = $now - DAYSECS;
$after = $now + DAYSECS;
return [
'without any dates' => [
null, []
],
'future due date' => [
$after, [
['label' => 'Due:', 'timestamp' => $after, 'dataid' => 'duedate'],
]
],
'due date is past' => [
$before, [
['label' => 'Due:', 'timestamp' => $before, 'dataid' => 'duedate'],
]
],
];
}
/**
* Test for get_dates_for_module().
*
* @dataProvider get_dates_for_module_provider
* @param int|null $duedate Forum's due date.
* @param array $expected The expected value of calling get_dates_for_module()
*/
public function test_get_dates_for_module(?int $duedate, array $expected): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user->id, $course->id);
$data = ['course' => $course->id];
if ($duedate) {
$data['duedate'] = $duedate;
}
$this->setAdminUser();
$forum = $this->getDataGenerator()->create_module('forum', $data);
$this->setUser($user);
$cm = get_coursemodule_from_instance('forum', $forum->id);
// Make sure we're using a cm_info object.
$cm = cm_info::create($cm);
$dates = activity_dates::get_dates_for_module($cm, (int) $user->id);
$this->assertEquals($expected, $dates);
}
}
+63
View File
@@ -0,0 +1,63 @@
<?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 mod_forum;
use mod_forum\local\entities\author as author_entity;
/**
* The author entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entities_author_test extends \advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity(): void {
$this->resetAfterTest();
$author = new author_entity(
1,
2,
'test',
'person',
'test person',
'test@example.com',
false,
'middle',
'tteeeeest',
'ppppeeerssson',
'maverick',
'image alt'
);
$this->assertEquals(1, $author->get_id());
$this->assertEquals(2, $author->get_picture_item_id());
$this->assertEquals('test', $author->get_first_name());
$this->assertEquals('person', $author->get_last_name());
$this->assertEquals('test person', $author->get_full_name());
$this->assertEquals('test@example.com', $author->get_email());
$this->assertEquals(false, $author->is_deleted());
$this->assertEquals('middle', $author->get_middle_name());
$this->assertEquals('tteeeeest', $author->get_first_name_phonetic());
$this->assertEquals('ppppeeerssson', $author->get_last_name_phonetic());
$this->assertEquals('maverick', $author->get_alternate_name());
$this->assertEquals('image alt', $author->get_image_alt());
}
}
@@ -0,0 +1,100 @@
<?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 mod_forum;
use mod_forum\local\entities\author as author_entity;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\discussion_summary as discussion_summary_entity;
use mod_forum\local\entities\post as post_entity;
/**
* The discussion_summary entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entities_discussion_summary_test extends \advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity(): void {
$this->resetAfterTest();
$firstauthor = new author_entity(
1,
2,
'test',
'person',
'test person',
'test@example.com',
false
);
$lastauthor = new author_entity(
2,
3,
'test 2',
'person 2',
'test 2 person 2',
'test2@example.com',
false
);
$discussion = new discussion_entity(
1,
1,
1,
'test discussion',
1,
1,
0,
false,
time(),
time(),
0,
0,
false,
0
);
$firstpost = new post_entity(
1,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false,
false,
null,
null
);
$discussionsummary = new discussion_summary_entity($discussion, $firstpost, $firstauthor, $lastauthor);
$this->assertEquals($discussion, $discussionsummary->get_discussion());
$this->assertEquals($firstauthor, $discussionsummary->get_first_post_author());
$this->assertEquals($lastauthor, $discussionsummary->get_latest_post_author());
$this->assertEquals($firstpost, $discussionsummary->get_first_post());
}
}
@@ -0,0 +1,174 @@
<?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 mod_forum;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\post as post_entity;
/**
* The discussion entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entities_discussion_test extends \advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity(): void {
$this->resetAfterTest();
// In the past to ensure the time started is true.
$time = time() + 10;
$discussion = new discussion_entity(
1,
2,
3,
'test discussion',
4,
5,
6,
false,
$time,
$time,
0,
0,
false,
0
);
$firstpost = new post_entity(
4,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false,
false,
null,
null
);
$notfirstpost = new post_entity(
1,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false,
false,
null,
null
);
$this->assertEquals(1, $discussion->get_id());
$this->assertEquals(2, $discussion->get_course_id());
$this->assertEquals(3, $discussion->get_forum_id());
$this->assertEquals('test discussion', $discussion->get_name());
$this->assertEquals(4, $discussion->get_first_post_id());
$this->assertEquals(5, $discussion->get_user_id());
$this->assertEquals(6, $discussion->get_group_id());
$this->assertEquals(false, $discussion->is_assessed());
$this->assertEquals($time, $discussion->get_time_modified());
$this->assertEquals($time, $discussion->get_user_modified());
$this->assertEquals(0, $discussion->get_time_start());
$this->assertEquals(0, $discussion->get_time_end());
$this->assertEquals(false, $discussion->is_pinned());
$this->assertEquals(true, $discussion->is_first_post($firstpost));
$this->assertEquals(false, $discussion->is_first_post($notfirstpost));
$this->assertEquals(true, $discussion->has_started());
$this->assertEquals(true, $discussion->has_group());
}
/**
* Test the display period settings for discussions.
* This covers each individual date function as well as the combination of the 2.
*
* @dataProvider diplay_period_options_provider
* @param string $testdescription A basic description of the base assertions.
* @param int $startoffset Start time offset with current time in seconds.
* @param int $endoffset End time offset with current time in seconds.
* @param bool $timestartresult Expected result from the has_started function
* @param bool $timeendresult Expected result from the has_ended function
* @param bool $isvisible Expected result from the is_timed_discussion_visible function
*/
public function test_display_period_settings($testdescription, $startoffset, $endoffset,
$timestartresult, $timeendresult, $isvisible): void {
global $CFG;
$this->resetAfterTest();
$basetime = time();
$starttime = $startoffset != 0 ? $basetime + $startoffset : 0;
$endtime = $endoffset != 0 ? $basetime + $endoffset : 0;
$discussion = new discussion_entity(
1,
2,
3,
'test discussion',
4,
5,
6,
false,
$basetime,
$basetime,
$starttime,
$endtime,
false,
0
);
$CFG->forum_enabletimedposts = true;
$this->assertEquals($timestartresult, $discussion->has_started(), $testdescription);
$this->assertEquals($timeendresult, $discussion->has_ended(), $testdescription);
$this->assertEquals($isvisible, $discussion->is_timed_discussion_visible(), $testdescription);
}
/**
* Data provider for test_display_period_settings().
*
* @return array start/end time offsets and the expected results.
*/
public function diplay_period_options_provider() {
return array(
["No dates set", 0, 0, true, false, true],
["Only started date in the future", 100, 0, false, false, false],
["Only started date in the past", -100, 0, true, false, true],
["Only end date in the future", 0, 100, true, false, true],
["Only end date in the past", 0, -100, true, true, false],
["Start date in the past, end date in the future", -100, 100, true, false, true],
["Both dates in the past", -100, -50, true, true, false],
["Both dates in the future", 100, 150, false, false, false],
);
}
}
+171
View File
@@ -0,0 +1,171 @@
<?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 mod_forum;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\forum as forum_entity;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/rating/lib.php');
/**
* The forum entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entities_forum_test extends \advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity(): void {
$this->resetAfterTest();
$time = time() - 10;
$discussion = new discussion_entity(
1,
2,
3,
'test discussion',
4,
5,
6,
false,
$time,
$time,
0,
0,
false,
0
);
$past = time() - 100;
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$effectivegroupmode = NOGROUPS;
$id = 1;
$courseid = 2;
$type = 'standard';
$name = 'test forum';
$intro = 'this is the intro';
$introformat = FORMAT_MOODLE;
$assessed = RATING_AGGREGATE_NONE;
$assesstimestart = 0;
$assesstimefinish = 0;
$scale = 0;
$gradeforum = 0;
$maxbytes = 200;
$maxattachments = 5;
$forcesubscribe = 0;
$trackingtype = 1;
$rsstype = 0;
$rssarticles = 0;
$timemodified = $past;
$warnafter = 0;
$blockafter = 0;
$blockperiod = 0;
$completiondiscussions = 0;
$completionreplies = 0;
$completionposts = 0;
$displaywordcount = false;
$lockdiscussionafter = 0;
$duedate = 0;
$cutoffdate = 0;
$sendnotification = false;
$forum = new forum_entity(
$context,
$coursemodule,
$course,
$effectivegroupmode,
$id,
$courseid,
$type,
$name,
$intro,
$introformat,
$assessed,
$assesstimestart,
$assesstimefinish,
$scale,
$gradeforum,
$sendnotification,
$maxbytes,
$maxattachments,
$forcesubscribe,
$trackingtype,
$rsstype,
$rssarticles,
$timemodified,
$warnafter,
$blockafter,
$blockperiod,
$completiondiscussions,
$completionreplies,
$completionposts,
$displaywordcount,
$lockdiscussionafter,
$duedate,
$cutoffdate
);
$this->assertEquals($context, $forum->get_context());
$this->assertEquals($coursemodule, $forum->get_course_module_record());
$this->assertEquals($coursemodule, $forum->get_course_module_record());
$this->assertEquals($effectivegroupmode, $forum->get_effective_group_mode());
$this->assertEquals(false, $forum->is_in_group_mode());
$this->assertEquals($course, $forum->get_course_record());
$this->assertEquals($id, $forum->get_id());
$this->assertEquals($courseid, $forum->get_course_id());
$this->assertEquals($name, $forum->get_name());
$this->assertEquals($intro, $forum->get_intro());
$this->assertEquals($introformat, $forum->get_intro_format());
$this->assertEquals($assessed, $forum->get_rating_aggregate());
// Rating aggregate is set to none.
$this->assertEquals(false, $forum->has_rating_aggregate());
$this->assertEquals($assesstimestart, $forum->get_assess_time_start());
$this->assertEquals($assesstimefinish, $forum->get_assess_time_finish());
$this->assertEquals($scale, $forum->get_scale());
$this->assertEquals($gradeforum, $forum->get_grade_for_forum());
$this->assertEquals($maxbytes, $forum->get_max_bytes());
$this->assertEquals($maxattachments, $forum->get_max_attachments());
$this->assertEquals($forcesubscribe, $forum->get_subscription_mode());
$this->assertEquals($trackingtype, $forum->get_tracking_type());
$this->assertEquals($rsstype, $forum->get_rss_type());
$this->assertEquals($rssarticles, $forum->get_rss_articles());
$this->assertEquals($timemodified, $forum->get_time_modified());
$this->assertEquals($warnafter, $forum->get_warn_after());
$this->assertEquals($blockafter, $forum->get_block_after());
$this->assertEquals($blockperiod, $forum->get_block_period());
$this->assertEquals(false, $forum->has_blocking_enabled());
$this->assertEquals($completiondiscussions, $forum->get_completion_discussions());
$this->assertEquals($completionreplies, $forum->get_completion_replies());
$this->assertEquals($completionposts, $forum->get_completion_posts());
$this->assertEquals($displaywordcount, $forum->should_display_word_count());
$this->assertEquals($lockdiscussionafter, $forum->get_lock_discussions_after());
$this->assertEquals(false, $forum->has_lock_discussions_after());
$this->assertEquals(false, $forum->is_discussion_locked($discussion));
$this->assertEquals(false, $forum->has_due_date());
$this->assertEquals(false, $forum->is_due_date_reached());
$this->assertEquals(false, $forum->has_cutoff_date());
$this->assertEquals(false, $forum->is_cutoff_date_reached());
}
}
@@ -0,0 +1,95 @@
<?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 mod_forum;
use mod_forum\local\entities\post_read_receipt_collection as collection_entity;
use mod_forum\local\entities\post as post_entity;
/**
* The post_read_receipt_collection entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entities_post_read_receipt_collection_test extends \advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$missingpost = new post_entity(
4,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false,
false,
null,
null
);
$post = new post_entity(
1,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false,
false,
null,
null
);
$collection = new collection_entity([
(object) [
'postid' => 1,
'userid' => $user->id + 1
],
(object) [
'postid' => 1,
'userid' => $user->id
],
(object) [
'postid' => 4,
'userid' => $user->id + 1
]
]);
$this->assertEquals(true, $collection->has_user_read_post($user, $post));
$this->assertEquals(false, $collection->has_user_read_post($user, $missingpost));
}
}
+79
View File
@@ -0,0 +1,79 @@
<?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 mod_forum;
use mod_forum\local\entities\post as post_entity;
/**
* The post entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entities_post_test extends \advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity(): void {
$this->resetAfterTest();
$owner = $this->getDataGenerator()->create_user();
$notowner = $this->getDataGenerator()->create_user();
$past = time() - 10;
$post = new post_entity(
4,
1,
0,
$owner->id,
$past,
$past,
true,
'post subject',
'post message',
FORMAT_MOODLE,
true,
false,
0,
false,
false,
false,
null,
null
);
$this->assertEquals(4, $post->get_id());
$this->assertEquals(1, $post->get_discussion_id());
$this->assertEquals(0, $post->get_parent_id());
$this->assertEquals(false, $post->has_parent());
$this->assertEquals($owner->id, $post->get_author_id());
$this->assertEquals($past, $post->get_time_created());
$this->assertEquals($past, $post->get_time_modified());
$this->assertEquals(true, $post->has_been_mailed());
$this->assertEquals('post subject', $post->get_subject());
$this->assertEquals('post message', $post->get_message());
$this->assertEquals(FORMAT_MOODLE, $post->get_message_format());
$this->assertEquals(true, $post->is_message_trusted());
$this->assertEquals(false, $post->has_attachments());
$this->assertEquals(0, $post->get_total_score());
$this->assertEquals(false, $post->should_mail_now());
$this->assertEquals(false, $post->is_deleted());
$this->assertTrue($post->get_age() >= 10);
$this->assertEquals(true, $post->is_owned_by_user($owner));
$this->assertEquals(false, $post->is_owned_by_user($notowner));
}
}
+87
View File
@@ -0,0 +1,87 @@
<?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 mod_forum;
use mod_forum\local\entities\sorter as sorter_entity;
/**
* The discussion entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entities_sorter_test extends \advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity_sort_into_children(): void {
$this->resetAfterTest();
$sorter = new sorter_entity(
function($entity) {
return $entity['id'];
},
function($entity) {
return $entity['parent'];
}
);
$a = ['id' => 1, 'parent' => 0];
$b = ['id' => 2, 'parent' => 1];
$c = ['id' => 3, 'parent' => 1];
$d = ['id' => 4, 'parent' => 2];
$e = ['id' => 5, 'parent' => 0];
$expected = [
[$e, []],
[$a, [[$b, [[$d, []]]], [$c, []]]],
];
$actual = $sorter->sort_into_children([$d, $b, $e, $a, $c]);
$this->assertEquals($expected, $actual);
}
/**
* Test the entity returns expected values.
*/
public function test_entity_flatten_children(): void {
$this->resetAfterTest();
$sorter = new sorter_entity(
function($entity) {
return $entity['id'];
},
function($entity) {
return $entity['parent'];
}
);
$a = ['id' => 1, 'parent' => 0];
$b = ['id' => 2, 'parent' => 1];
$c = ['id' => 3, 'parent' => 1];
$d = ['id' => 4, 'parent' => 3];
$sorted = [
[$a, [[$b, [[$d, []]]], [$c, []]]]
];
$expected = [$a, $b, $d, $c];
$actual = $sorter->flatten_children($sorted);
$this->assertEquals($expected, $actual);
}
}
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
<?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 mod_forum;
use mod_forum\local\entities\author as author_entity;
use mod_forum\local\exporters\author as author_exporter;
/**
* The author exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class exporters_author_test extends \advanced_testcase {
/**
* Test the export function returns expected values.
*/
public function test_export_author(): void {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$author = new author_entity(
1,
1,
'test',
'user',
'test user',
'test@example.com',
false
);
$exporter = new author_exporter($author, 1, [], true, [
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'context' => $context,
'forum' => $forum,
]);
$exportedauthor = $exporter->export($renderer);
$this->assertEquals(1, $exportedauthor->id);
$this->assertEquals('test user', $exportedauthor->fullname);
$this->assertEquals([], $exportedauthor->groups);
$this->assertNotEquals(null, $exportedauthor->urls['profile']);
$this->assertNotEquals(null, $exportedauthor->urls['profileimage']);
}
/**
* Test the export function with groups.
*/
public function test_export_author_with_groups(): void {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$author = new author_entity(
1,
1,
'test',
'user',
'test user',
'test@example.com',
false
);
$group = $datagenerator->create_group(['courseid' => $course->id]);
$exporter = new author_exporter($author, 1, [$group], true, [
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'context' => $context,
'forum' => $forum,
]);
$exportedauthor = $exporter->export($renderer);
$this->assertCount(1, $exportedauthor->groups);
$this->assertEquals($group->id, $exportedauthor->groups[0]['id']);
}
/**
* Test the export function with no view capability.
*/
public function test_export_author_no_view_capability(): void {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$author = new author_entity(
1,
1,
'test',
'user',
'test user',
'test@example.com',
false
);
$group = $datagenerator->create_group(['courseid' => $course->id]);
$exporter = new author_exporter($author, 1, [$group], false, [
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'context' => $context,
'forum' => $forum,
]);
$exportedauthor = $exporter->export($renderer);
$this->assertEquals(null, $exportedauthor->id);
$this->assertNotEquals('test user', $exportedauthor->fullname);
$this->assertEquals([], $exportedauthor->groups);
$this->assertEquals(null, $exportedauthor->urls['profile']);
$this->assertEquals(null, $exportedauthor->urls['profileimage']);
}
}
@@ -0,0 +1,112 @@
<?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 mod_forum;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\exporters\discussion as discussion_exporter;
/**
* The discussion exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class exporters_discussion_test extends \advanced_testcase {
/** @var \mod_forum\local\builders\exported_posts */
private $builder;
/**
* Test set up function.
*/
public function setUp(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
$builderfactory = \mod_forum\local\container::get_builder_factory();
$this->builder = $builderfactory->get_exported_posts_builder();
}
/**
* Test tear down function.
*/
public function tearDown(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
}
/**
* Test the export function returns expected values.
*/
public function test_export(): void {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$group = $datagenerator->create_group(['courseid' => $course->id]);
$now = time();
$discussion = new discussion_entity(
1,
$course->id,
$forum->get_id(),
'test discussion',
1,
$user->id,
$group->id,
false,
$now,
$now,
0,
0,
false,
0
);
$exporter = new discussion_exporter($discussion, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'capabilitymanager' => (\mod_forum\local\container::get_manager_factory())->get_capability_manager($forum),
'context' => $context,
'forum' => $forum,
'user' => $user,
'groupsbyid' => [$group->id => $group],
'latestpostid' => 7
]);
$exporteddiscussion = $exporter->export($renderer);
$this->assertEquals(1, $exporteddiscussion->id);
$this->assertEquals($forum->get_id(), $exporteddiscussion->forumid);
$this->assertEquals(false, $exporteddiscussion->pinned);
$this->assertEquals('test discussion', $exporteddiscussion->name);
$this->assertEquals($now, $exporteddiscussion->times['modified']);
$this->assertEquals(0, $exporteddiscussion->times['start']);
$this->assertEquals(0, $exporteddiscussion->times['end']);
$this->assertEquals($group->name, $exporteddiscussion->group['name']);
}
}
+70
View File
@@ -0,0 +1,70 @@
<?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 mod_forum;
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\exporters\forum as forum_exporter;
/**
* The discussion forum tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class exporters_forum_test extends \advanced_testcase {
/**
* Test the export function returns expected values.
*/
public function test_export(): void {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', [
'course' => $course->id,
'groupmode' => VISIBLEGROUPS
]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$exporter = new forum_exporter($forum, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'capabilitymanager' => (\mod_forum\local\container::get_manager_factory())->get_capability_manager($forum),
'user' => $user,
'currentgroup' => null,
'vaultfactory' => \mod_forum\local\container::get_vault_factory()
]);
$exportedforum = $exporter->export($renderer);
$this->assertEquals($forum->get_id(), $exportedforum->id);
$this->assertEquals(VISIBLEGROUPS, $exportedforum->state['groupmode']);
$this->assertEquals(false, $exportedforum->userstate['tracked']);
$this->assertEquals(false, $exportedforum->capabilities['viewdiscussions']);
$this->assertEquals(false, $exportedforum->capabilities['create']);
$this->assertEquals(false, $exportedforum->capabilities['subscribe']);
$this->assertNotEquals(null, $exportedforum->urls['create']);
$this->assertNotEquals(null, $exportedforum->urls['markasread']);
}
}
+596
View File
@@ -0,0 +1,596 @@
<?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/>.
/**
* The post exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\post as post_entity;
use mod_forum\local\exporters\post as post_exporter;
use mod_forum\local\managers\capability as capability_manager;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once(__DIR__ . '/generator_trait.php');
require_once($CFG->dirroot . '/rating/lib.php');
/**
* The post exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class exporters_post_test extends \advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* Test the export function returns expected values.
*
* @dataProvider export_post_provider
* @param bool $istimed True if this is a timed post
* @param int $addtime Seconds to be added to the current time
*/
public function test_export_post($istimed = false, $addtime = 0): void {
global $CFG, $PAGE;
$this->resetAfterTest();
$CFG->enableportfolios = true;
$filestorage = get_file_storage();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$now = time();
$forumgenparams = [
'course' => $forum->course,
'userid' => $user->id,
'forum' => $forum->id,
];
if ($istimed) {
$forumgenparams['timestart'] = $now + $addtime;
}
$discussion = $forumgenerator->create_discussion((object) $forumgenparams);
$post = $forumgenerator->create_post((object) [
'discussion' => $discussion->id,
'parent' => 0,
'userid' => $user->id,
'created' => $now,
'modified' => $now,
'subject' => 'This is the subject',
'message' => 'This is the message',
'messagetrust' => 1,
'attachment' => 0,
'totalscore' => 0,
'mailnow' => 1,
'deleted' => 0
]);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
$tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
$attachment = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$canview = true;
$canedit = true;
$candelete = true;
$cansplit = true;
$canreply = true;
$canexport = true;
$cancontrolreadstatus = true;
$canreplyprivately = true;
$canenrol = true;
$capabilitymanager = new test_capability_manager(
$canview,
$canedit,
$candelete,
$cansplit,
$canreply,
$canexport,
$cancontrolreadstatus,
$canreplyprivately,
$canenrol
);
$managerfactory = \mod_forum\local\container::get_manager_factory();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$discussion = $entityfactory->get_discussion_from_stdClass($discussion);
$post = $entityfactory->get_post_from_stdClass($post);
$author = $entityfactory->get_author_from_stdClass($user);
$authorcontext = \context_user::instance($author->get_id());
$exporter = new post_exporter($post, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'capabilitymanager' => $capabilitymanager,
'readreceiptcollection' => null,
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'forum' => $forum,
'discussion' => $discussion,
'author' => $author,
'authorcontextid' => $authorcontext->id,
'user' => $user,
'context' => $context,
'authorgroups' => [],
'attachments' => [$attachment],
'tags' => $tags,
'rating' => null,
'includehtml' => true
]);
$exportedpost = $exporter->export($renderer);
$this->assertEquals('This is the subject', $exportedpost->subject);
$this->assertEquals('This is the message', $exportedpost->message);
$this->assertEquals($user->id, $exportedpost->author->id);
$this->assertEquals($discussion->get_id(), $exportedpost->discussionid);
$this->assertEquals(false, $exportedpost->hasparent);
$this->assertEquals(null, $exportedpost->parentid);
if ($istimed && ($addtime > 0)) {
$this->assertEquals($now + $addtime, $exportedpost->timecreated);
} else {
$this->assertEquals($now, $exportedpost->timecreated);
}
$this->assertEquals(null, $exportedpost->unread);
$this->assertEquals(false, $exportedpost->isdeleted);
$this->assertEquals($canview, $exportedpost->capabilities['view']);
$this->assertEquals($canedit, $exportedpost->capabilities['edit']);
$this->assertEquals($candelete, $exportedpost->capabilities['delete']);
$this->assertEquals($cansplit, $exportedpost->capabilities['split']);
$this->assertEquals($canreply, $exportedpost->capabilities['reply']);
$this->assertEquals($canexport, $exportedpost->capabilities['export']);
$this->assertEquals($canenrol, $exportedpost->capabilities['selfenrol']);
$this->assertEquals($cancontrolreadstatus, $exportedpost->capabilities['controlreadstatus']);
$this->assertNotEmpty($exportedpost->urls['view']);
$this->assertNotEmpty($exportedpost->urls['viewisolated']);
$this->assertNotEmpty($exportedpost->urls['edit']);
$this->assertNotEmpty($exportedpost->urls['delete']);
$this->assertNotEmpty($exportedpost->urls['split']);
$this->assertNotEmpty($exportedpost->urls['reply']);
$this->assertNotEmpty($exportedpost->urls['markasread']);
$this->assertNotEmpty($exportedpost->urls['markasunread']);
$this->assertCount(1, $exportedpost->attachments);
$this->assertEquals('example1.jpg', $exportedpost->attachments[0]->filename);
$this->assertCount(2, $exportedpost->tags);
$this->assertEquals('foo', $exportedpost->tags[0]['displayname']);
$this->assertEquals('bar', $exportedpost->tags[1]['displayname']);
$this->assertEquals(null, $exportedpost->html['rating']);
$this->assertNotEquals(null, $exportedpost->html['taglist']);
$this->assertNotEmpty($exportedpost->html['authorsubheading']);
}
/**
* Data provider for test_export_post().
*
* @return array
*/
public function export_post_provider(): array {
return [
'Simple export' => [
],
'Test timed post future' => [
true,
1000
],
'Test timed post past' => [
true,
-1000
],
];
}
/**
* Test exporting of a deleted post.
*/
public function test_export_deleted_post(): void {
global $CFG, $PAGE;
$this->resetAfterTest();
$CFG->enableportfolios = true;
$filestorage = get_file_storage();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$discussion = $forumgenerator->create_discussion((object) [
'course' => $forum->course,
'userid' => $user->id,
'forum' => $forum->id
]);
$now = time();
$post = $forumgenerator->create_post((object) [
'discussion' => $discussion->id,
'parent' => 0,
'userid' => $user->id,
'created' => $now,
'modified' => $now,
'subject' => 'This is the subject',
'message' => 'This is the message',
'messagetrust' => 1,
'attachment' => 0,
'totalscore' => 0,
'mailnow' => 1,
'deleted' => 1
]);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
$tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
$attachment = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$canview = true;
$canedit = true;
$candelete = true;
$cansplit = true;
$canreply = true;
$canexport = true;
$cancontrolreadstatus = true;
$capabilitymanager = new test_capability_manager(
$canview,
$canedit,
$candelete,
$cansplit,
$canreply,
$canexport,
$cancontrolreadstatus
);
$managerfactory = \mod_forum\local\container::get_manager_factory();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$discussion = $entityfactory->get_discussion_from_stdClass($discussion);
$post = $entityfactory->get_post_from_stdClass($post);
$author = $entityfactory->get_author_from_stdClass($user);
$authorcontext = \context_user::instance($author->get_id());
$exporter = new post_exporter($post, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'capabilitymanager' => $capabilitymanager,
'readreceiptcollection' => null,
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'forum' => $forum,
'discussion' => $discussion,
'author' => $author,
'authorcontextid' => $authorcontext->id,
'user' => $user,
'context' => $context,
'authorgroups' => [],
'attachments' => [$attachment],
'tags' => $tags,
'rating' => null,
'includehtml' => true
]);
$exportedpost = $exporter->export($renderer);
$this->assertNotEquals('This is the subject', $exportedpost->subject);
$this->assertNotEquals('This is the message', $exportedpost->message);
$this->assertEquals(null, $exportedpost->timecreated);
$this->assertEquals(null, $exportedpost->unread);
$this->assertEquals(true, $exportedpost->isdeleted);
$this->assertEquals([], $exportedpost->attachments);
$this->assertEquals([], $exportedpost->tags);
$this->assertEquals(null, $exportedpost->html['rating']);
$this->assertEquals(null, $exportedpost->html['taglist']);
$this->assertEquals(null, $exportedpost->html['authorsubheading']);
}
/**
* Test exporting of a post the user can't view.
*/
public function test_export_post_no_view_capability(): void {
global $CFG, $PAGE;
$this->resetAfterTest();
$CFG->enableportfolios = true;
$filestorage = get_file_storage();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$discussion = $forumgenerator->create_discussion((object) [
'course' => $forum->course,
'userid' => $user->id,
'forum' => $forum->id
]);
$now = time();
$post = $forumgenerator->create_post((object) [
'discussion' => $discussion->id,
'parent' => 0,
'userid' => $user->id,
'created' => $now,
'modified' => $now,
'subject' => 'This is the subject',
'message' => 'This is the message',
'messagetrust' => 1,
'attachment' => 0,
'totalscore' => 0,
'mailnow' => 1,
'deleted' => 0
]);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
$tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
$attachment = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$canview = false;
$canedit = true;
$candelete = true;
$cansplit = true;
$canreply = true;
$canexport = true;
$cancontrolreadstatus = true;
$capabilitymanager = new test_capability_manager(
$canview,
$canedit,
$candelete,
$cansplit,
$canreply,
$canexport,
$cancontrolreadstatus
);
$managerfactory = \mod_forum\local\container::get_manager_factory();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$discussion = $entityfactory->get_discussion_from_stdClass($discussion);
$post = $entityfactory->get_post_from_stdClass($post);
$author = $entityfactory->get_author_from_stdClass($user);
$authorcontext = \context_user::instance($author->get_id());
$exporter = new post_exporter($post, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'capabilitymanager' => $capabilitymanager,
'readreceiptcollection' => null,
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'forum' => $forum,
'discussion' => $discussion,
'author' => $author,
'authorcontextid' => $authorcontext->id,
'user' => $user,
'context' => $context,
'authorgroups' => [],
'attachments' => [$attachment],
'tags' => $tags,
'rating' => null,
'includehtml' => true
]);
$exportedpost = $exporter->export($renderer);
$this->assertNotEquals('This is the subject', $exportedpost->subject);
$this->assertNotEquals('This is the message', $exportedpost->message);
$this->assertEquals(null, $exportedpost->timecreated);
$this->assertEquals(null, $exportedpost->unread);
$this->assertEquals(false, $exportedpost->isdeleted);
$this->assertEquals([], $exportedpost->attachments);
$this->assertEquals([], $exportedpost->tags);
$this->assertEquals(null, $exportedpost->html['rating']);
$this->assertEquals(null, $exportedpost->html['taglist']);
$this->assertEquals(null, $exportedpost->html['authorsubheading']);
}
}
/**
* Test implementation of the capability manager.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class test_capability_manager extends capability_manager {
/** @var bool $view Value for can_view_post */
private $view;
/** @var bool $edit Value for can_edit_post */
private $edit;
/** @var bool $delete Value for can_delete_post */
private $delete;
/** @var bool $split Value for can_split_post */
private $split;
/** @var bool $reply Value for can_reply_to_post */
private $reply;
/** @var bool $export Value for can_export_post */
private $export;
/** @var bool $controlreadstatus Value for can_manually_control_post_read_status */
private $controlreadstatus;
/** @var bool $controlreadstatus Value for can_reply_privately_to_post */
private $canreplyprivatelytopost;
/** @var bool $canenrol Value for can_self_enrol */
private $canenrol;
/**
* Constructor.
*
* @param bool $view Value for can_view_post
* @param bool $edit Value for can_edit_post
* @param bool $delete Value for can_delete_post
* @param bool $split Value for can_split_post
* @param bool $reply Value for can_reply_to_post
* @param bool $export Value for can_export_post
* @param bool $controlreadstatus Value for can_manually_control_post_read_status
*/
public function __construct(
bool $view = true,
bool $edit = true,
bool $delete = true,
bool $split = true,
bool $reply = true,
bool $export = true,
bool $controlreadstatus = true,
bool $canreplyprivatelytopost = true,
bool $canenrol = true
) {
$this->view = $view;
$this->edit = $edit;
$this->delete = $delete;
$this->split = $split;
$this->reply = $reply;
$this->export = $export;
$this->controlreadstatus = $controlreadstatus;
$this->canreplyprivatelytopost = $canreplyprivatelytopost;
$this->canenrol = $canenrol;
}
/**
* Override can_view_post
*
* @param \stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_view_post(\stdClass $user, discussion_entity $discussion, post_entity $post): bool {
return $this->view;
}
/**
* Override can_edit_post
*
* @param \stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_edit_post(\stdClass $user, discussion_entity $discussion, post_entity $post): bool {
return $this->edit;
}
/**
* Override can_delete_post
*
* @param \stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @param bool $hasreplies
* @return bool
*/
public function can_delete_post(\stdClass $user, discussion_entity $discussion, post_entity $post,
bool $hasreplies = false): bool {
return $this->delete;
}
/**
* Override can_split_post
*
* @param \stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_split_post(\stdClass $user, discussion_entity $discussion, post_entity $post): bool {
return $this->split;
}
/**
* Override can_reply_to_post
*
* @param \stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_reply_to_post(\stdClass $user, discussion_entity $discussion, post_entity $post): bool {
return $this->reply;
}
/**
* Override can_export_post
*
* @param \stdClass $user The user
* @param post_entity $post The post
* @return bool
*/
public function can_export_post(\stdClass $user, post_entity $post): bool {
return $this->export;
}
/**
* Override can_manually_control_post_read_status
*
* @param \stdClass $user The user
* @return bool
*/
public function can_manually_control_post_read_status(\stdClass $user): bool {
return $this->controlreadstatus;
}
/**
* Override can_reply_privately_to_post
* @param \stdClass $user
* @param post_entity $post
* @return bool
*/
public function can_reply_privately_to_post(\stdClass $user, post_entity $post): bool {
return $this->canreplyprivatelytopost;
}
/**
* Override can_self_enrol
* @param \stdClass $user
* @return bool
*/
public function can_self_enrol(\stdClass $user): bool {
return $this->canenrol;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,152 @@
<?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 data generator for mod_forum.
*
* @package mod_forum
* @category test
* @copyright 2021 Noel De Martin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_mod_forum_generator extends behat_generator_base {
/**
* Get a list of the entities that Behat can create using the generator step.
*
* @return array
*/
protected function get_creatable_entities(): array {
return [
'discussions' => [
'singular' => 'discussion',
'datagenerator' => 'discussion',
'required' => ['forum'],
'switchids' => ['forum' => 'forumid', 'user' => 'userid', 'group' => 'groupid'],
],
'posts' => [
'singular' => 'post',
'datagenerator' => 'post',
'required' => [],
'switchids' => ['forum' => 'forumid', 'user' => 'userid'],
],
];
}
/**
* Get the forum id using an activity idnumber or name.
*
* @param string $idnumberorname The forum activity idnumber or name.
* @return int The forum id
*/
protected function get_forum_id(string $idnumberorname): int {
return $this->get_cm_by_activity_name('forum', $idnumberorname)->instance;
}
/**
* Gets the group id from it's idnumber. It allows using 'All participants' as idnumber.
*
* @throws Exception
* @param string $idnumber
* @return int
*/
protected function get_group_id($idnumber): int {
if ($idnumber === 'All participants') {
return -1;
}
return parent::get_group_id($idnumber);
}
/**
* Preprocess discussion data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_discussion(array $data) {
global $DB, $USER;
$forum = $DB->get_record('forum', ['id' => $data['forumid']]);
unset($data['course']);
unset($data['forumid']);
return array_merge([
'course' => $forum->course,
'forum' => $forum->id,
'userid' => $USER->id,
], $data);
}
/**
* Preprocess post data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_post(array $data) {
global $DB, $USER;
// Get discussion from name.
$discussionfilters = array_filter([
'name' => $data['discussion'] ?? null,
'forum' => $data['forumid'] ?? null,
]);
if (!empty($discussionfilters)) {
if (!$discussionid = $DB->get_field('forum_discussions', 'id', $discussionfilters)) {
throw new Exception('The specified discussion with name "' . $data['name'] . '" could not be found.');
}
$data['discussion'] = $discussionid;
unset($data['forumid']);
}
// Get discussion from parent.
$parentfilters = array_filter([
'subject' => $data['parentsubject'] ?? null,
]);
if (!empty($parentfilters)) {
if (isset($discussionid)) {
$parentfilters['discussion'] = $discussionid;
}
if (!$parent = $DB->get_record('forum_posts', $parentfilters)) {
$parentdescription = implode(' and ', array_filter([
isset($parentfilters['subject']) ? 'subject "' . $parentfilters['subject'] . '"' : null,
]));
throw new Exception('The specified post with ' . $parentdescription . ' could not be found.');
}
$data['parent'] = $parent->id;
$data['discussion'] = $parent->discussion;
unset($data['parentsubject']);
}
// Return processed data.
if (!isset($data['discussion'])) {
throw new Exception('It was not possible to find a discussion to create a post, '.
'please specify discussion or parentsubject.');
}
return array_merge(['userid' => $USER->id], $data);
}
}
+389
View File
@@ -0,0 +1,389 @@
<?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/>.
/**
* Forum module data generator class
*
* @package mod_forum
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_generator extends testing_module_generator {
/**
* @var int keep track of how many forum discussions have been created.
*/
protected $forumdiscussioncount = 0;
/**
* @var int keep track of how many forum posts have been created.
*/
protected $forumpostcount = 0;
/**
* @var int keep track of how many forum subscriptions have been created.
*/
protected $forumsubscriptionscount = 0;
/**
* Get the clock implementation to use when generating data.
*
* @return \core\clock
*/
protected function get_clock(): \core\clock {
return \core\di::get(\core\clock::class);
}
/**
* To be called from data reset code only,
* do not use in tests.
* @return void
*/
public function reset() {
$this->forumdiscussioncount = 0;
$this->forumpostcount = 0;
$this->forumsubscriptionscount = 0;
parent::reset();
}
public function create_instance($record = null, array $options = null) {
global $CFG;
require_once($CFG->dirroot.'/mod/forum/lib.php');
$record = (object)(array)$record;
if (!isset($record->type)) {
$record->type = 'general';
}
if (!isset($record->assessed)) {
$record->assessed = 0;
}
if (!isset($record->scale)) {
$record->scale = 0;
}
if (!isset($record->forcesubscribe)) {
$record->forcesubscribe = FORUM_CHOOSESUBSCRIBE;
}
if (!isset($record->grade_forum)) {
$record->grade_forum = 0;
}
return parent::create_instance($record, (array)$options);
}
/**
* Function to create a dummy subscription.
*
* @param array|stdClass $record
* @return stdClass the subscription object
*/
public function create_subscription($record = null) {
global $DB;
// Increment the forum subscription count.
$this->forumsubscriptionscount++;
$record = (array)$record;
if (!isset($record['course'])) {
throw new coding_exception('course must be present in phpunit_util::create_subscription() $record');
}
if (!isset($record['forum'])) {
throw new coding_exception('forum must be present in phpunit_util::create_subscription() $record');
}
if (!isset($record['userid'])) {
throw new coding_exception('userid must be present in phpunit_util::create_subscription() $record');
}
$record = (object)$record;
// Add the subscription.
$record->id = $DB->insert_record('forum_subscriptions', $record);
return $record;
}
/**
* Function to create a dummy discussion.
*
* @param array|stdClass $record
* @return stdClass the discussion object
*/
public function create_discussion($record = null) {
global $DB;
// Increment the forum discussion count.
$this->forumdiscussioncount++;
$record = (array) $record;
if (!isset($record['course'])) {
throw new coding_exception('course must be present in phpunit_util::create_discussion() $record');
}
if (!isset($record['forum'])) {
throw new coding_exception('forum must be present in phpunit_util::create_discussion() $record');
}
if (!isset($record['userid'])) {
throw new coding_exception('userid must be present in phpunit_util::create_discussion() $record');
}
if (!isset($record['name'])) {
$record['name'] = "Discussion " . $this->forumdiscussioncount;
}
if (!isset($record['subject'])) {
$record['subject'] = "Subject for discussion " . $this->forumdiscussioncount;
}
if (!isset($record['message'])) {
$record['message'] = html_writer::tag('p', 'Message for discussion ' . $this->forumdiscussioncount);
}
if (!isset($record['messageformat'])) {
$record['messageformat'] = editors_get_preferred_format();
}
if (!isset($record['messagetrust'])) {
$record['messagetrust'] = "";
}
if (!isset($record['assessed'])) {
$record['assessed'] = '1';
}
if (!isset($record['groupid'])) {
$record['groupid'] = "-1";
}
if (!isset($record['timestart'])) {
$record['timestart'] = "0";
}
if (!isset($record['timeend'])) {
$record['timeend'] = "0";
}
if (!isset($record['mailnow'])) {
$record['mailnow'] = "0";
}
if (!isset($record['timecreated'])) {
$record['timecreated'] = $this->get_clock()->now()->getTimestamp();
}
if (isset($record['timemodified'])) {
$timemodified = $record['timemodified'];
} else {
$timemodified = $record['timecreated'];
}
if (!isset($record['pinned'])) {
$record['pinned'] = FORUM_DISCUSSION_UNPINNED;
}
if (!isset($record['timelocked'])) {
$record['timelocked'] = 0;
}
if (isset($record['mailed'])) {
$mailed = $record['mailed'];
}
$record = (object) $record;
// Add the discussion.
$record->id = forum_add_discussion($record, null, null, $record->userid);
$post = $DB->get_record('forum_posts', array('discussion' => $record->id));
if (isset($timemodified) || isset($mailed)) {
if (isset($mailed)) {
$post->mailed = $mailed;
}
if (isset($timemodified)) {
// Enforce the time modified.
$record->timemodified = $timemodified;
$post->modified = $post->created = $timemodified;
$DB->update_record('forum_discussions', $record);
}
$DB->update_record('forum_posts', $post);
}
if (property_exists($record, 'tags')) {
$cm = get_coursemodule_from_instance('forum', $record->forum);
$tags = is_array($record->tags) ? $record->tags : preg_split('/,/', $record->tags);
core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id,
context_module::instance($cm->id), $tags);
}
return $record;
}
/**
* Function to create a dummy post.
*
* @param array|stdClass $record
* @return stdClass the post object
*/
public function create_post($record = null) {
global $DB;
// Increment the forum post count.
$this->forumpostcount++;
// Variable to store time.
$time = time() + $this->forumpostcount;
$record = (array) $record;
if (!isset($record['discussion'])) {
throw new coding_exception('discussion must be present in phpunit_util::create_post() $record');
}
if (!isset($record['userid'])) {
throw new coding_exception('userid must be present in phpunit_util::create_post() $record');
}
if (!isset($record['parent'])) {
$record['parent'] = 0;
}
if (!isset($record['subject'])) {
$record['subject'] = 'Forum post subject ' . $this->forumpostcount;
}
if (!isset($record['message'])) {
$record['message'] = html_writer::tag('p', 'Forum message post ' . $this->forumpostcount);
}
if (!isset($record['created'])) {
// If we are using the system clock, then revert to the time + count approach.
// Unfortunately a lot of Forum code relies on things not happening at the same time.
// See MDL-80838 for more information on this issue.
if ($this->get_clock() instanceof \core\system_clock) {
$record['created'] = $time;
} else {
$record['created'] = $this->get_clock()->now()->getTimestamp();
}
}
if (!isset($record['modified'])) {
$record['modified'] = $record['created'];
}
if (!isset($record['mailed'])) {
$record['mailed'] = 0;
}
if (!isset($record['messageformat'])) {
$record['messageformat'] = 0;
}
if (!isset($record['messagetrust'])) {
$record['messagetrust'] = 0;
}
if (!isset($record['attachment'])) {
$record['attachment'] = "";
}
if (!isset($record['totalscore'])) {
$record['totalscore'] = 0;
}
if (!isset($record['mailnow'])) {
$record['mailnow'] = 0;
}
if (!isset($record['deleted'])) {
$record['deleted'] = 0;
}
if (!isset($record['privatereplyto'])) {
$record['privatereplyto'] = 0;
}
$record = (object) $record;
\mod_forum\local\entities\post::add_message_counts($record);
// Add the post.
$record->id = $DB->insert_record('forum_posts', $record);
if (property_exists($record, 'tags')) {
$discussion = $DB->get_record('forum_discussions', ['id' => $record->discussion]);
$cm = get_coursemodule_from_instance('forum', $discussion->forum);
$tags = is_array($record->tags) ? $record->tags : preg_split('/,/', $record->tags);
core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $record->id,
context_module::instance($cm->id), $tags);
}
// Update the last post.
forum_discussion_update_last_post($record->discussion);
return $record;
}
public function create_content($instance, $record = array()) {
global $USER, $DB;
$record = (array)$record + array(
'forum' => $instance->id,
'userid' => $USER->id,
'course' => $instance->course
);
if (empty($record['discussion']) && empty($record['parent'])) {
// Create discussion.
$discussion = $this->create_discussion($record);
$post = $DB->get_record('forum_posts', array('id' => $discussion->firstpost));
} else {
// Create post.
if (empty($record['parent'])) {
$record['parent'] = $DB->get_field('forum_discussions', 'firstpost', array('id' => $record['discussion']), MUST_EXIST);
} else if (empty($record['discussion'])) {
$record['discussion'] = $DB->get_field('forum_posts', 'discussion', array('id' => $record['parent']), MUST_EXIST);
}
$post = $this->create_post($record);
}
return $post;
}
/**
* Extracted from exporter/post.php
*
* Get the HTML to display as a subheading in a post.
*
* @param stdClass $exportedauthor The exported author object
* @param int $timecreated The post time created timestamp if it's to be displayed
* @return string
*/
public function get_author_subheading_html(stdClass $exportedauthor, int $timecreated): string {
$fullname = $exportedauthor->fullname;
$profileurl = $exportedauthor->urls['profile'] ?? null;
$name = $profileurl ? "<a href=\"{$profileurl}\">{$fullname}</a>" : $fullname;
$date = userdate_htmltime($timecreated, get_string('strftimedaydatetime', 'core_langconfig'));
return get_string('bynameondate', 'mod_forum', ['name' => $name, 'date' => $date]);
}
}
+285
View File
@@ -0,0 +1,285 @@
<?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 mod_forum;
/**
* PHPUnit data generator testcase
*
* @package mod_forum
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \mod_forum_generator
*/
final class generator_test extends \advanced_testcase {
public function setUp(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
}
public function tearDown(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
}
public function test_generator(): void {
global $DB;
$this->resetAfterTest(true);
$this->assertEquals(0, $DB->count_records('forum'));
$course = $this->getDataGenerator()->create_course();
/** @var mod_forum_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$this->assertInstanceOf('mod_forum_generator', $generator);
$this->assertEquals('forum', $generator->get_modulename());
$generator->create_instance(['course' => $course->id]);
$generator->create_instance(['course' => $course->id]);
$forum = $generator->create_instance(['course' => $course->id]);
$this->assertEquals(3, $DB->count_records('forum'));
$cm = get_coursemodule_from_instance('forum', $forum->id);
$this->assertEquals($forum->id, $cm->instance);
$this->assertEquals('forum', $cm->modname);
$this->assertEquals($course->id, $cm->course);
$context = \context_module::instance($cm->id);
$this->assertEquals($forum->cmid, $context->instanceid);
// Test gradebook integration using low level DB access - DO NOT USE IN PLUGIN CODE.
$forum = $generator->create_instance(['course' => $course->id, 'assessed' => 1, 'scale' => 100]);
$gitem = $DB->get_record(
'grade_items',
['courseid' => $course->id, 'itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum->id]
);
$this->assertNotEmpty($gitem);
$this->assertEquals(100, $gitem->grademax);
$this->assertEquals(0, $gitem->grademin);
$this->assertEquals(GRADE_TYPE_VALUE, $gitem->gradetype);
}
/**
* Test create_discussion.
*/
public function test_create_discussion(): void {
global $DB;
$this->resetAfterTest(true);
// User that will create the forum.
$user = self::getDataGenerator()->create_user();
// Create course to add the forum to.
$course = self::getDataGenerator()->create_course();
// The forum.
$record = new \stdClass();
$record->course = $course->id;
$forum = self::getDataGenerator()->create_module('forum', $record);
// Add a few discussions.
$record = [];
$record['course'] = $course->id;
$record['forum'] = $forum->id;
$record['userid'] = $user->id;
$record['pinned'] = FORUM_DISCUSSION_PINNED; // Pin one discussion.
self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
$record['pinned'] = FORUM_DISCUSSION_UNPINNED; // No pin for others.
self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Check the discussions were correctly created.
$this->assertEquals(3, $DB->count_records_select(
'forum_discussions',
'forum = :forum',
['forum' => $forum->id]
));
$record['tags'] = ['Cats', 'mice'];
$record = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
$this->assertEquals(
['Cats', 'mice'],
array_values(\core_tag_tag::get_item_tags_array('mod_forum', 'forum_posts', $record->firstpost))
);
}
/**
* Test create_post.
*/
public function test_create_post(): void {
global $DB;
$this->resetAfterTest(true);
// Create a bunch of users.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$user4 = self::getDataGenerator()->create_user();
// Create course to add the forum.
$course = self::getDataGenerator()->create_course();
// The forum.
$record = new \stdClass();
$record->course = $course->id;
$forum = self::getDataGenerator()->create_module('forum', $record);
// Add a discussion.
$record->forum = $forum->id;
$record->userid = $user1->id;
$discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Add a bunch of replies, changing the userid.
$record = new \stdClass();
$record->discussion = $discussion->id;
$record->userid = $user2->id;
self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
$record->userid = $user3->id;
self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
$record->userid = $user4->id;
self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
// Check the posts were correctly created, remember, when creating a discussion a post
// is generated as well, so we should have 4 posts, not 3.
$this->assertEquals(4, $DB->count_records_select(
'forum_posts',
'discussion = :discussion',
['discussion' => $discussion->id]
));
$record->tags = ['Cats', 'mice'];
$record = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
$this->assertEquals(
['Cats', 'mice'],
array_values(\core_tag_tag::get_item_tags_array('mod_forum', 'forum_posts', $record->id))
);
}
public function test_create_content(): void {
global $DB;
$this->resetAfterTest(true);
// Create a bunch of users.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$user4 = self::getDataGenerator()->create_user();
$this->setAdminUser();
// Create course and forum.
$course = self::getDataGenerator()->create_course();
$forum = self::getDataGenerator()->create_module('forum', ['course' => $course]);
$generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
// This should create discussion.
$post1 = $generator->create_content($forum);
// This should create posts in the discussion.
$post2 = $generator->create_content($forum, ['parent' => $post1->id]);
$post3 = $generator->create_content($forum, ['discussion' => $post1->discussion]);
// This should create posts answering another post.
$post4 = $generator->create_content($forum, ['parent' => $post2->id]);
// This should create post with tags.
$post5 = $generator->create_content($forum, ['parent' => $post2->id, 'tags' => ['Cats', 'mice']]);
$discussionrecords = $DB->get_records('forum_discussions', ['forum' => $forum->id]);
$postrecords = $DB->get_records('forum_posts');
$postrecords2 = $DB->get_records('forum_posts', ['discussion' => $post1->discussion]);
$this->assertEquals(1, count($discussionrecords));
$this->assertEquals(5, count($postrecords));
$this->assertEquals(5, count($postrecords2));
$this->assertEquals($post1->id, $discussionrecords[$post1->discussion]->firstpost);
$this->assertEquals($post1->id, $postrecords[$post2->id]->parent);
$this->assertEquals($post1->id, $postrecords[$post3->id]->parent);
$this->assertEquals($post2->id, $postrecords[$post4->id]->parent);
$this->assertEquals(
['Cats', 'mice'],
array_values(\core_tag_tag::get_item_tags_array('mod_forum', 'forum_posts', $post5->id))
);
}
public function test_create_post_time_system(): void {
$this->resetAfterTest(true);
$user = self::getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$forum = self::getDataGenerator()->create_module('forum', (object) [
'course' => $course->id,
]);
$starttime = time();
// Add a discussion.
$discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion((object) [
'course' => $course->id,
'forum' => $forum->id,
'userid' => $user->id,
]);
// Add a post.
$post = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post((object) [
'discussion' => $discussion->id,
'userid' => $user->id,
]);
$this->assertGreaterThanOrEqual($starttime, $discussion->timemodified);
$this->assertGreaterThanOrEqual($starttime, $post->created);
// The fallback behavior is to add the number of created posts to the current time to avoid duplicates.
$this->assertLessThanOrEqual(time() + 1, $discussion->timemodified);
$this->assertLessThanOrEqual(time() + 2, $post->created);
}
public function test_create_post_time_frozen(): void {
$this->resetAfterTest(true);
$clock = $this->mock_clock_with_frozen(100);
$user = self::getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$forum = self::getDataGenerator()->create_module('forum', (object) [
'course' => $course->id,
]);
$starttime = time();
// Add a discussion.
$discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion((object) [
'course' => $course->id,
'forum' => $forum->id,
'userid' => $user->id,
]);
$this->assertEquals(100, $discussion->timemodified);
// Add a post.
$clock->set_to(200);
$post = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post((object) [
'discussion' => $discussion->id,
'userid' => $user->id,
]);
$this->assertEquals(200, $post->created);
}
}
+174
View File
@@ -0,0 +1,174 @@
<?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/>.
/**
* The forum module trait with additional generator helpers.
*
* @package mod_forum
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
trait mod_forum_tests_generator_trait {
/**
* Helper to create the required number of users in the specified course.
* Users are enrolled as students by default.
*
* @param stdClass $course The course object
* @param integer $count The number of users to create
* @param string $role The role to assign users as
* @return array The users created
*/
protected function helper_create_users($course, $count, $role = null) {
$users = array();
for ($i = 0; $i < $count; $i++) {
$user = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user->id, $course->id, $role);
$users[] = $user;
}
return $users;
}
/**
* Create a new discussion and post within the specified forum, as the
* specified author.
*
* @param stdClass $forum The forum to post in
* @param stdClass $author The author to post as
* @param array $fields any other fields in discussion (name, message, messageformat, ...)
* @return array An array containing the discussion object, and the post object
*/
protected function helper_post_to_forum($forum, $author, $fields = array()) {
global $DB;
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
// Create a discussion in the forum, and then add a post to that discussion.
$record = (object)$fields;
$record->course = $forum->course;
$record->userid = $author->id;
$record->forum = $forum->id;
$discussion = $generator->create_discussion($record);
// Retrieve the post which was created by create_discussion.
$post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
return [$discussion, $post];
}
/**
* Update the post time for the specified post by $factor.
*
* @param stdClass $post The post to update
* @param int $factor The amount to update by
*/
protected function helper_update_post_time($post, $factor) {
global $DB;
// Update the post to have a created in the past.
$DB->set_field('forum_posts', 'created', $post->created + $factor, array('id' => $post->id));
}
/**
* Update the subscription time for the specified user/discussion by $factor.
*
* @param stdClass $user The user to update
* @param stdClass $discussion The discussion to update for this user
* @param int $factor The amount to update by
*/
protected function helper_update_subscription_time($user, $discussion, $factor) {
global $DB;
$sub = $DB->get_record('forum_discussion_subs', array('userid' => $user->id, 'discussion' => $discussion->id));
// Update the subscription to have a preference in the past.
$DB->set_field('forum_discussion_subs', 'preference', $sub->preference + $factor, array('id' => $sub->id));
}
/**
* Create a new post within an existing discussion, as the specified author.
*
* @param stdClass $forum The forum to post in
* @param stdClass $discussion The discussion to post in
* @param stdClass $author The author to post as
* @param array $options Additional options to pass to `create_post`
* @return stdClass The forum post
*/
protected function helper_post_to_discussion($forum, $discussion, $author, array $options = []) {
global $DB;
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
// Add a post to the discussion.
$strre = get_string('re', 'forum');
$record = array_merge([
'course' => $forum->course,
'subject' => "{$strre} {$discussion->subject}",
'userid' => $author->id,
'forum' => $forum->id,
'discussion' => $discussion->id,
'mailnow' => 1,
], $options);
$post = $generator->create_post((object) $record);
return $post;
}
/**
* Create a new post within an existing discussion, as the specified author.
*
* @param stdClass $parent The post being replied to
* @param stdClass $author The author to post as
* @param array $options Additional options to pass to `create_post`
* @return stdClass The forum post
*/
protected function helper_reply_to_post($parent, $author, array $options = []) {
global $DB;
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
// Add a post to the discussion.
$strre = get_string('re', 'forum');
$record = (object) array_merge([
'discussion' => $parent->discussion,
'parent' => $parent->id,
'userid' => $author->id,
'mailnow' => 1,
'subject' => $strre . ' ' . $parent->subject,
], $options);
$post = $generator->create_post($record);
return $post;
}
/**
* Gets the role id from it's shortname.
*
* @param string $roleshortname
* @return int
*/
protected function get_role_id($roleshortname) {
global $DB;
return $DB->get_field('role', 'id', ['shortname' => $roleshortname]);
}
}
@@ -0,0 +1,277 @@
<?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 the the Forum gradeitem.
*
* @package mod_forum
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\grade;
use core_grades\component_gradeitem;
use mod_forum\grades\forum_gradeitem as gradeitem;
use mod_forum\local\entities\forum as forum_entity;
use gradingform_controller;
use mod_forum\grades\forum_gradeitem;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../generator_trait.php');
/**
* Tests for the the Forum gradeitem.
*
* @package mod_forum
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class forum_gradeitem_test extends \advanced_testcase {
use \mod_forum_tests_generator_trait;
/**
* Test fetching of a grade for a user when the grade has been created.
*/
public function test_get_grade_for_user_exists(): void {
$forum = $this->get_forum_instance([
'grade_forum' => 0,
]);
$course = $forum->get_course_record();
[$student] = $this->helper_create_users($course, 1);
[$grader] = $this->helper_create_users($course, 1, 'editingteacher');
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
// Create the grade record.
$grade = $gradeitem->create_empty_grade($student, $grader);
$this->assertIsObject($grade);
$this->assertEquals($student->id, $grade->userid);
}
/**
* Test fetching of a grade for a user when the grade has been created.
*/
public function test_user_has_grade(): void {
$forum = $this->get_forum_instance([
'grade_forum' => 100,
]);
$course = $forum->get_course_record();
[$student] = $this->helper_create_users($course, 1);
[$grader] = $this->helper_create_users($course, 1, 'editingteacher');
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$hasgrade = $gradeitem->user_has_grade($student);
$this->assertEquals(false, $hasgrade);
// Create the grade record.
$gradeitem->create_empty_grade($student, $grader);
$hasgrade = $gradeitem->user_has_grade($student);
$this->assertEquals(false, $hasgrade);
// Store a new value.
$gradeitem->store_grade_from_formdata($student, $grader, (object) ['grade' => 97]);
$hasgrade = $gradeitem->user_has_grade($student);
$this->assertEquals(true, $hasgrade);
}
/**
* Ensure that it is possible to get, and update, a grade for a user when simple direct grading is in use.
*/
public function test_get_and_store_grade_for_user_with_simple_direct_grade(): void {
$forum = $this->get_forum_instance([
'grade_forum' => 100,
]);
$course = $forum->get_course_record();
[$student] = $this->helper_create_users($course, 1);
[$grader] = $this->helper_create_users($course, 1, 'editingteacher');
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
// Create the grade record.
$grade = $gradeitem->create_empty_grade($student, $grader);
$this->assertIsObject($grade);
$this->assertEquals($student->id, $grade->userid);
// Store a new value.
$gradeitem->store_grade_from_formdata($student, $grader, (object) ['grade' => 97]);
}
/**
* Ensure that it is possible to get, and update, a grade for a user when a rubric is in use.
*/
public function test_get_and_store_grade_for_user_with_rubric(): void {
global $DB;
$this->resetAfterTest();
$generator = \testing_util::get_data_generator();
$gradinggenerator = $generator->get_plugin_generator('core_grading');
$rubricgenerator = $generator->get_plugin_generator('gradingform_rubric');
$forum = $this->get_forum_instance([
'grade_forum' => 100,
]);
$course = $forum->get_course_record();
$context = $forum->get_context();
[$student] = $this->helper_create_users($course, 1);
[$grader] = $this->helper_create_users($course, 1, 'editingteacher');
[$editor] = $this->helper_create_users($course, 1, 'editingteacher');
// Note: This must be run as a user because it messes with file uploads and drafts.
$this->setUser($editor);
$controller = $rubricgenerator->get_test_rubric($context, 'mod_forum', 'forum');
// Create the forum_gradeitem object that we'll be testing.
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
// The current grade should be null initially.
$this->assertCount(0, $DB->get_records('forum_grades'));
$grade = $gradeitem->get_grade_for_user($student, $grader);
$instance = $gradeitem->get_advanced_grading_instance($grader, $grade);
$this->assertIsObject($grade);
$this->assertEquals($student->id, $grade->userid);
$this->assertEquals($forum->get_id(), $grade->forum);
$rubricgenerator = $generator->get_plugin_generator('gradingform_rubric');
$data = $rubricgenerator->get_submitted_form_data($controller, $grade->id, [
'Spelling is important' => [
'score' => 2,
'remark' => 'Abracadabra',
],
'Pictures' => [
'score' => 1,
'remark' => 'More than one picture',
],
]);
// Store a new value.
$gradeitem->store_grade_from_formdata($student, $grader, (object) [
'instanceid' => $instance->get_id(),
'advancedgrading' => $data,
]);
}
/**
* Test the logic in the should_grade_only_active_users() method.
*
* @covers ::should_grade_only_active_users
* @dataProvider should_grade_only_active_users_provider
* @param bool $showonlyactiveenrolconfig Whether the 'grade_report_showonlyactiveenrol' is enabled or not.
* @param bool $showonlyactiveenrolpreference Whether the 'grade_report_showonlyactiveenrol' preference is enabled or not.
* @param bool $viewsuspendeduserscapability Whether the 'moodle/course:viewsuspendedusers' capability is allowed or not.
* @param bool $expected The expected result.
*/
public function test_should_grade_only_active_users(bool $showonlyactiveenrolconfig, bool $showonlyactiveenrolpreference,
bool $viewsuspendeduserscapability, bool $expected): void {
global $DB;
$this->resetAfterTest();
$forum = $this->get_forum_instance([
'grade_forum' => 100,
]);
$course = $forum->get_course_record();
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$user = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'editingteacher');
$editingteacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher']);
// Set the 'grade_report_showonlyactiveenrol' config setting.
set_config('grade_report_showonlyactiveenrol', $showonlyactiveenrolconfig);
$this->setUser($user);
// Set the 'grade_report_showonlyactiveenrol' user preference.
set_user_preference('grade_report_showonlyactiveenrol', $showonlyactiveenrolpreference);
// Set the 'moodle/course:viewsuspendedusers' user capability.
assign_capability('moodle/course:viewsuspendedusers', $viewsuspendeduserscapability ?
CAP_ALLOW : CAP_PROHIBIT, $editingteacherroleid, \context_course::instance($course->id));
$this->assertEquals($expected, $gradeitem->should_grade_only_active_users());
}
/**
* Data provider for test_should_grade_only_active_users.
*
* @return array
*/
public function should_grade_only_active_users_provider(): array {
return [
'Enabled showonlyactiveenrol setting; enabled showonlyactiveenrol preference; view suspended users capability' =>
[
'showonlyactiveenrolconfig' => true,
'showonlyactiveenrolpreference' => true,
'viewsuspendeduserscapability' => true,
'expected' => true,
],
'Disabled showonlyactiveenrol setting; enabled showonlyactiveenrol preference; view suspended users capability' =>
[
'showonlyactiveenrolconfig' => false,
'showonlyactiveenrolpreference' => true,
'viewsuspendeduserscapability' => true,
'expected' => true,
],
'Disabled showonlyactiveenrol setting; disabled showonlyactiveenrol preference; view suspended users capability' =>
[
'showonlyactiveenrolconfig' => false,
'showonlyactiveenrolpreference' => false,
'viewsuspendeduserscapability' => true,
'expected' => false,
],
'Disabled showonlyactiveenrol setting; disabled showonlyactiveenrol preference; no view suspended users capability' =>
[
'showonlyactiveenrolconfig' => false,
'showonlyactiveenrolpreference' => false,
'viewsuspendeduserscapability' => false,
'expected' => true,
],
'Enabled showonlyactiveenrol setting; enabled showonlyactiveenrol preference; no view suspended users capability' =>
[
'showonlyactiveenrolconfig' => true,
'showonlyactiveenrolpreference' => true,
'viewsuspendeduserscapability' => false,
'expected' => true,
],
];
}
/**
* Get a forum instance.
*
* @param array $config
* @return forum_entity
*/
protected function get_forum_instance(array $config = []): forum_entity {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', array_merge($config, ['course' => $course->id]));
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_forum_vault();
return $vault->get_from_id((int) $forum->id);
}
}
+92
View File
@@ -0,0 +1,92 @@
<?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/>.
declare(strict_types = 1);
namespace mod_forum\grade;
use advanced_testcase;
use core_grades\component_gradeitems;
use coding_exception;
/**
* Unit tests for mod_forum\grades\gradeitems.
*
* @package mod_forum
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class gradeitems_test extends advanced_testcase {
/**
* Ensure that the mappings are present and correct.
*/
public function test_get_itemname_mapping_for_component(): void {
$mappings = component_gradeitems::get_itemname_mapping_for_component('mod_forum');
$this->assertIsArray($mappings);
$this->assertCount(2, $mappings);
$expected = [0 => 'rating', 1 => 'forum'];
// Verify each expected element exists and its value matches.
foreach ($expected as $key => $value) {
$this->assertArrayHasKey($key, $mappings);
$this->assertSame($value, $mappings[$key]);
}
}
/**
* Ensure that the advanced grading only applies to the relevant items.
*/
public function test_get_advancedgrading_itemnames_for_component(): void {
$mappings = component_gradeitems::get_advancedgrading_itemnames_for_component('mod_forum');
$this->assertIsArray($mappings);
$this->assertCount(1, $mappings);
$this->assertContains('forum', $mappings);
$this->assertNotContains('rating', $mappings);
}
/**
* Ensure that the correct items are identified by is_advancedgrading_itemname.
*
* @dataProvider is_advancedgrading_itemname_provider
* @param string $itemname
* @param bool $expected
*/
public function test_is_advancedgrading_itemname(string $itemname, bool $expected): void {
$this->assertEquals(
$expected,
component_gradeitems::is_advancedgrading_itemname('mod_forum', $itemname)
);
}
/**
* Data provider for tests of is_advancedgrading_itemname.
*
* @return array
*/
public function is_advancedgrading_itemname_provider(): array {
return [
'rating is not advanced' => [
'rating',
false,
],
'Whole forum grading is advanced' => [
'forum',
true,
],
];
}
}
+204
View File
@@ -0,0 +1,204 @@
<?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/>.
declare(strict_types = 1);
namespace mod_forum\h5p;
use stdClass;
/**
* Test class covering the H5P canedit class.
*
* @package mod_forum
* @copyright 2021 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \mod_forum\h5p\canedit
*/
class canedit_test extends \advanced_testcase {
/**
* Test the behaviour of can_edit_content().
*
* @covers ::can_edit_content
* @dataProvider can_edit_content_provider
*
* @param string $currentuser User who will call the method.
* @param string $fileauthor Author of the file to check.
* @param string $filecomponent Component of the file to check.
* @param bool $expected Expected result after calling the can_edit_content method.
* @param string $filearea Area of the file to check.
*
* @return void
*/
public function test_can_edit_content(string $currentuser, string $fileauthor, string $filecomponent, bool $expected,
$filearea = 'unittest'): void {
global $USER, $DB;
$this->setRunTestInSeparateProcess(true);
$this->resetAfterTest();
// Create course.
$course = $this->getDataGenerator()->create_course();
$context = \context_course::instance($course->id);
// Create some users.
$this->setAdminUser();
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$users = [
'admin' => $USER,
'teacher' => $teacher,
'student' => $student,
];
// Set current user.
if ($currentuser !== 'admin') {
$this->setUser($users[$currentuser]);
}
$itemid = rand();
if ($filearea === 'post') {
// Create a forum and add a discussion.
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
$record = new stdClass();
$record->course = $course->id;
$record->userid = $users[$fileauthor]->id;
$record->forum = $forum->id;
$discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
$post = $DB->get_record('forum_posts', ['discussion' => $discussion->id]);
$itemid = $post->id;
}
// Create the file.
$filename = 'greeting-card.h5p';
$path = __DIR__ . '/../../../../h5p/tests/fixtures/' . $filename;
if ($filecomponent === 'contentbank') {
$generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank');
$contents = $generator->generate_contentbank_data(
'contenttype_h5p',
1,
(int)$users[$fileauthor]->id,
$context,
true,
$path
);
$content = array_shift($contents);
$file = $content->get_file();
} else {
$filerecord = [
'contextid' => $context->id,
'component' => $filecomponent,
'filearea' => $filearea,
'itemid' => $itemid,
'filepath' => '/',
'filename' => basename($path),
'userid' => $users[$fileauthor]->id,
];
$fs = get_file_storage();
$file = $fs->create_file_from_pathname($filerecord, $path);
}
// Check if the currentuser can edit the file.
$result = canedit::can_edit_content($file);
$this->assertEquals($expected, $result);
}
/**
* Data provider for test_can_edit_content().
*
* @return array
*/
public function can_edit_content_provider(): array {
return [
// Component = mod_forum.
'mod_forum: Admin user is author' => [
'currentuser' => 'admin',
'fileauthor' => 'admin',
'filecomponent' => 'mod_forum',
'expected' => true,
],
'mod_forum: Admin user, teacher is author' => [
'currentuser' => 'admin',
'fileauthor' => 'teacher',
'filecomponent' => 'mod_forum',
'expected' => true,
],
'mod_forum: Teacher user, admin is author' => [
'currentuser' => 'teacher',
'fileauthor' => 'admin',
'filecomponent' => 'mod_forum',
'expected' => true,
],
'mod_forum: Student user, teacher is author' => [
'currentuser' => 'student',
'fileauthor' => 'teacher',
'filecomponent' => 'mod_forum',
'expected' => false,
],
'mod_forum/post: Admin user is author' => [
'currentuser' => 'admin',
'fileauthor' => 'admin',
'filecomponent' => 'mod_forum',
'expected' => true,
'filearea' => 'post',
],
'mod_forum/post: Teacher user, admin is author' => [
'currentuser' => 'teacher',
'fileauthor' => 'admin',
'filecomponent' => 'mod_forum',
'expected' => true,
'filearea' => 'post',
],
'mod_forum/post: Student user, teacher is author' => [
'currentuser' => 'student',
'fileauthor' => 'teacher',
'filecomponent' => 'mod_forum',
'expected' => false,
'filearea' => 'post',
],
// Component <> mod_forum.
'mod_page: Admin user is author' => [
'currentuser' => 'admin',
'fileauthor' => 'admin',
'filecomponent' => 'mod_page',
'expected' => false,
],
// Unexisting components.
'Unexisting component' => [
'currentuser' => 'admin',
'fileauthor' => 'admin',
'filecomponent' => 'unexisting_component',
'expected' => false,
],
'Unexisting module activity' => [
'currentuser' => 'admin',
'fileauthor' => 'admin',
'filecomponent' => 'mod_unexisting',
'expected' => false,
],
'Unexisting block' => [
'currentuser' => 'admin',
'fileauthor' => 'admin',
'filecomponent' => 'block_unexisting',
'expected' => false,
],
];
}
}
File diff suppressed because it is too large Load Diff
+102
View File
@@ -0,0 +1,102 @@
<?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 mod_forum;
/**
* Tests for the local\container class.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \mod_forum\local\container
*/
class local_container_test extends \advanced_testcase {
/**
* Ensure that a renderer factory is returned.
*
* @covers ::get_renderer_factory
*/
public function test_get_renderer_factory(): void {
$this->assertInstanceOf(\mod_forum\local\factories\renderer::class, \mod_forum\local\container::get_renderer_factory());
}
/**
* Ensure that a legacy_data_mapper_factory factory is returned.
*
* @covers ::get_legacy_data_mapper_factory
*/
public function test_get_legacy_data_mapper_factory(): void {
$this->assertInstanceOf(
\mod_forum\local\factories\legacy_data_mapper::class,
\mod_forum\local\container::get_legacy_data_mapper_factory()
);
}
/**
* Ensure that a exporter factory is returned.
*
* @covers ::get_exporter_factory
*/
public function test_get_exporter_factory(): void {
$this->assertInstanceOf(\mod_forum\local\factories\exporter::class, \mod_forum\local\container::get_exporter_factory());
}
/**
* Ensure that a vault factory is returned.
*
* @covers ::get_vault_factory
*/
public function test_get_vault_factory(): void {
$this->assertInstanceOf(\mod_forum\local\factories\vault::class, \mod_forum\local\container::get_vault_factory());
}
/**
* Ensure that a manager factory is returned.
*
* @covers ::get_manager_factory
*/
public function test_get_manager_factory(): void {
$this->assertInstanceOf(\mod_forum\local\factories\manager::class, \mod_forum\local\container::get_manager_factory());
}
/**
* Ensure that a entity factory is returned.
*
* @covers ::get_entity_factory
*/
public function test_get_entity_factory(): void {
$this->assertInstanceOf(\mod_forum\local\factories\entity::class, \mod_forum\local\container::get_entity_factory());
}
/**
* Ensure that a builder factory is returned.
*
* @covers ::get_builder_factory
*/
public function test_get_builder_factory(): void {
$this->assertInstanceOf(\mod_forum\local\factories\builder::class, \mod_forum\local\container::get_builder_factory());
}
/**
* Ensure that a url factory is returned.
*
* @covers ::get_url_factory
*/
public function test_get_url_factory(): void {
$this->assertInstanceOf(\mod_forum\local\factories\url::class, \mod_forum\local\container::get_url_factory());
}
}
+74
View File
@@ -0,0 +1,74 @@
<?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/>.
/**
* File containing the forum module local library function tests.
*
* @package mod_forum
* @category test
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* Class mod_forum_locallib_testcase.
*
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class locallib_test extends \advanced_testcase {
public function test_forum_update_calendar(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
// Create a course.
$course = $this->getDataGenerator()->create_course();
// Create a forum activity.
$time = time();
$forum = $this->getDataGenerator()->create_module('forum',
array(
'course' => $course->id,
'duedate' => $time
)
);
// Check that there is now an event in the database.
$events = $DB->get_records('event');
$this->assertCount(1, $events);
// Get the event.
$event = reset($events);
// Confirm the event is correct.
$this->assertEquals('forum', $event->modulename);
$this->assertEquals($forum->id, $event->instance);
$this->assertEquals(CALENDAR_EVENT_TYPE_ACTION, $event->type);
$this->assertEquals(FORUM_EVENT_TYPE_DUE, $event->eventtype);
$this->assertEquals($time, $event->timestart);
$this->assertEquals($time, $event->timesort);
}
}
+243
View File
@@ -0,0 +1,243 @@
<?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 mod_forum;
use mod_forum_tests_cron_trait;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/forum/lib.php');
require_once(__DIR__ . '/cron_trait.php');
require_once(__DIR__ . '/generator_trait.php');
/**
* The forum module mail generation tests for groups.
*
* @copyright 2013 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mail_group_test extends \advanced_testcase {
// Make use of the cron tester trait.
use mod_forum_tests_cron_trait;
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* @var \phpunit_message_sink
*/
protected $messagesink;
/**
* @var \phpunit_mailer_sink
*/
protected $mailsink;
public function setUp(): void {
global $CFG;
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
\mod_forum\subscriptions::reset_discussion_cache();
// Messaging is not compatible with transactions...
$this->preventResetByRollback();
// Catch all messages.
$this->messagesink = $this->redirectMessages();
$this->mailsink = $this->redirectEmails();
// Forcibly reduce the maxeditingtime to a second in the past to
// ensure that messages are sent out.
$CFG->maxeditingtime = -1;
}
public function tearDown(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
$this->messagesink->clear();
$this->messagesink->close();
unset($this->messagesink);
$this->mailsink->clear();
$this->mailsink->close();
unset($this->mailsink);
}
/**
* Ensure that posts written in a forum marked for separate groups includes notifications for the members of that
* group, and any user with accessallgroups.
*/
public function test_separate_group(): void {
global $CFG, $DB;
$this->resetAfterTest(true);
// Create a course, with a forum.
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
'forcesubscribe' => FORUM_INITIALSUBSCRIBE,
'groupmode' => SEPARATEGROUPS,
]);
// Create three students:
// - author, enrolled in group A; and
// - recipient, enrolled in group B; and
// - other, enrolled in the course, but no groups.
list($author, $recipient, $otheruser) = $this->helper_create_users($course, 3);
// Create one teacher, not in any group and no accessallgroups capability.
list($teacher) = $this->helper_create_users($course, 1, 'teacher');
// Create one editing teacher, not in any group but with accessallgroups capability.
list($editingteacher) = $this->helper_create_users($course, 1, 'editingteacher');
$groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
$groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
$this->getDataGenerator()->create_group_member([
'groupid' => $groupa->id,
'userid' => $author->id,
]);
$this->getDataGenerator()->create_group_member([
'groupid' => $groupb->id,
'userid' => $recipient->id,
]);
// Post a discussion to the forum.
list($discussion, $post) = $this->helper_post_to_forum($forum, $author, [
'groupid' => $groupa->id,
]);
// Only the author should receive.
$expect = [
'author' => (object) [
'userid' => $author->id,
'messages' => 1,
],
'recipient' => (object) [
'userid' => $recipient->id,
'messages' => 0,
],
'otheruser' => (object) [
'userid' => $otheruser->id,
'messages' => 0,
],
'teacher' => (object) [
'userid' => $teacher->id,
'messages' => 0,
],
'editingteacher' => (object) [
'userid' => $editingteacher->id,
'messages' => 1,
],
];
$this->queue_tasks_and_assert($expect);
// No notifications should be queued.
$this->send_notifications_and_assert($author, [$post]);
$this->send_notifications_and_assert($recipient, []);
$this->send_notifications_and_assert($otheruser, []);
$this->send_notifications_and_assert($teacher, []);
$this->send_notifications_and_assert($editingteacher, [$post]);
}
/**
* Ensure that posts written in a forum marked for visible groups includes notifications for the members of that
* group, and any user with accessallgroups.
*/
public function test_visible_group(): void {
global $CFG, $DB;
$this->resetAfterTest(true);
// Create a course, with a forum.
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
'forcesubscribe' => FORUM_INITIALSUBSCRIBE,
'groupmode' => VISIBLEGROUPS,
]);
// Create three students:
// - author, enrolled in group A; and
// - recipient, enrolled in group B; and
// - other, enrolled in the course, but no groups.
list($author, $recipient, $otheruser) = $this->helper_create_users($course, 3);
// Create one teacher, not in any group and no accessallgroups capability.
list($teacher) = $this->helper_create_users($course, 1, 'teacher');
// Create one editing teacher, not in any group but with accessallgroups capability.
list($editingteacher) = $this->helper_create_users($course, 1, 'editingteacher');
$groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
$groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
$this->getDataGenerator()->create_group_member([
'groupid' => $groupa->id,
'userid' => $author->id,
]);
$this->getDataGenerator()->create_group_member([
'groupid' => $groupb->id,
'userid' => $recipient->id,
]);
// Post a discussion to the forum.
list($discussion, $post) = $this->helper_post_to_forum($forum, $author, [
'groupid' => $groupa->id,
]);
// Only the author should receive.
$expect = [
'author' => (object) [
'userid' => $author->id,
'messages' => 1,
],
'recipient' => (object) [
'userid' => $recipient->id,
'messages' => 0,
],
'otheruser' => (object) [
'userid' => $otheruser->id,
'messages' => 0,
],
'teacher' => (object) [
'userid' => $teacher->id,
'messages' => 0,
],
'editingteacher' => (object) [
'userid' => $editingteacher->id,
'messages' => 1,
],
];
$this->queue_tasks_and_assert($expect);
// No notifications should be queued.
$this->send_notifications_and_assert($author, [$post]);
$this->send_notifications_and_assert($recipient, []);
$this->send_notifications_and_assert($otheruser, []);
$this->send_notifications_and_assert($teacher, []);
$this->send_notifications_and_assert($editingteacher, [$post]);
}
}
File diff suppressed because it is too large Load Diff
+892
View File
@@ -0,0 +1,892 @@
<?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 mod_forum;
use mod_forum_tests_cron_trait;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die;
require_once(__DIR__ . '/cron_trait.php');
require_once(__DIR__ . '/generator_trait.php');
/**
* The module forums external functions unit tests
*
* @package mod_forum
* @category test
* @copyright 2013 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class maildigest_test extends \advanced_testcase {
// Make use of the cron tester trait.
use mod_forum_tests_cron_trait;
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/** @var \phpunit_message_sink */
protected $messagesink;
/** @var \phpunit_message_sink */
protected $mailsink;
/**
* Set up message and mail sinks, and set up other requirements for the
* cron to be tested here.
*/
public function setUp(): void {
global $CFG;
// Messaging is not compatible with transactions...
$this->preventResetByRollback();
// Catch all messages
$this->messagesink = $this->redirectMessages();
$this->mailsink = $this->redirectEmails();
// Confirm that we have an empty message sink so far.
$messages = $this->messagesink->get_messages_by_component('mod_forum');
$this->assertEquals(0, count($messages));
$messages = $this->mailsink->get_messages();
$this->assertEquals(0, count($messages));
// Tell Moodle that we've not sent any digest messages out recently.
$CFG->digestmailtimelast = 0;
// And set the digest sending time to a negative number - this has
// the effect of making it 11pm the previous day.
$CFG->digestmailtime = -1;
// Forcibly reduce the maxeditingtime to a one second to ensure that
// messages are sent out.
$CFG->maxeditingtime = 1;
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
\mod_forum\subscriptions::reset_discussion_cache();
}
/**
* Clear the message sinks set up in this test.
*/
public function tearDown(): void {
$this->messagesink->clear();
$this->messagesink->close();
$this->mailsink->clear();
$this->mailsink->close();
}
/**
* Setup a user, course, and forums.
*
* @return stdClass containing the list of forums, courses, forumids,
* and the user enrolled in them.
*/
protected function helper_setup_user_in_course() {
global $DB;
$return = new \stdClass();
$return->courses = new \stdClass();
$return->forums = new \stdClass();
$return->forumids = array();
// Create a user.
$user = $this->getDataGenerator()->create_user();
$return->user = $user;
// Create courses to add the modules.
$return->courses->course1 = $this->getDataGenerator()->create_course();
// Create forums.
$record = new \stdClass();
$record->course = $return->courses->course1->id;
$record->forcesubscribe = 1;
$return->forums->forum1 = $this->getDataGenerator()->create_module('forum', $record);
$return->forumsids[] = $return->forums->forum1->id;
$return->forums->forum2 = $this->getDataGenerator()->create_module('forum', $record);
$return->forumsids[] = $return->forums->forum2->id;
// Check the forum was correctly created.
list ($test, $params) = $DB->get_in_or_equal($return->forumsids);
// Enrol the user in the courses.
// DataGenerator->enrol_user automatically sets a role for the user
$this->getDataGenerator()->enrol_user($return->user->id, $return->courses->course1->id);
return $return;
}
public function test_set_maildigest(): void {
global $DB;
$this->resetAfterTest(true);
$helper = $this->helper_setup_user_in_course();
$user = $helper->user;
$course1 = $helper->courses->course1;
$forum1 = $helper->forums->forum1;
// Set to the user.
self::setUser($helper->user);
// Confirm that there is no current value.
$currentsetting = $DB->get_record('forum_digests', array(
'forum' => $forum1->id,
'userid' => $user->id,
));
$this->assertFalse($currentsetting);
// Test with each of the valid values:
// 0, 1, and 2 are valid values.
forum_set_user_maildigest($forum1, 0, $user);
$currentsetting = $DB->get_record('forum_digests', array(
'forum' => $forum1->id,
'userid' => $user->id,
));
$this->assertEquals($currentsetting->maildigest, 0);
forum_set_user_maildigest($forum1, 1, $user);
$currentsetting = $DB->get_record('forum_digests', array(
'forum' => $forum1->id,
'userid' => $user->id,
));
$this->assertEquals($currentsetting->maildigest, 1);
forum_set_user_maildigest($forum1, 2, $user);
$currentsetting = $DB->get_record('forum_digests', array(
'forum' => $forum1->id,
'userid' => $user->id,
));
$this->assertEquals($currentsetting->maildigest, 2);
// And the default value - this should delete the record again
forum_set_user_maildigest($forum1, -1, $user);
$currentsetting = $DB->get_record('forum_digests', array(
'forum' => $forum1->id,
'userid' => $user->id,
));
$this->assertFalse($currentsetting);
// Try with an invalid value.
$this->expectException('moodle_exception');
forum_set_user_maildigest($forum1, 42, $user);
}
public function test_get_user_digest_options_default(): void {
global $USER, $DB;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$helper = $this->helper_setup_user_in_course();
$user = $helper->user;
$course1 = $helper->courses->course1;
$forum1 = $helper->forums->forum1;
// Set to the user.
self::setUser($helper->user);
// We test against these options.
$digestoptions = array(
'0' => get_string('emaildigestoffshort', 'mod_forum'),
'1' => get_string('emaildigestcompleteshort', 'mod_forum'),
'2' => get_string('emaildigestsubjectsshort', 'mod_forum'),
);
// The default settings is 0.
$this->assertEquals(0, $user->maildigest);
$options = forum_get_user_digest_options();
$this->assertEquals($options[-1], get_string('emaildigestdefault', 'mod_forum', $digestoptions[0]));
// Update the setting to 1.
$USER->maildigest = 1;
$this->assertEquals(1, $USER->maildigest);
$options = forum_get_user_digest_options();
$this->assertEquals($options[-1], get_string('emaildigestdefault', 'mod_forum', $digestoptions[1]));
// Update the setting to 2.
$USER->maildigest = 2;
$this->assertEquals(2, $USER->maildigest);
$options = forum_get_user_digest_options();
$this->assertEquals($options[-1], get_string('emaildigestdefault', 'mod_forum', $digestoptions[2]));
}
public function test_get_user_digest_options_sorting(): void {
global $USER, $DB;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$helper = $this->helper_setup_user_in_course();
$user = $helper->user;
$course1 = $helper->courses->course1;
$forum1 = $helper->forums->forum1;
// Set to the user.
self::setUser($helper->user);
// Retrieve the list of applicable options.
$options = forum_get_user_digest_options();
// The default option must always be at the top of the list.
$lastoption = -2;
foreach ($options as $value => $description) {
$this->assertGreaterThan($lastoption, $value);
$lastoption = $value;
}
}
public function test_cron_no_posts(): void {
global $DB;
$this->resetAfterTest(true);
// Initially the forum cron should generate no messages as we've made no posts.
$expect = [];
$this->queue_tasks_and_assert($expect);
}
/**
* Sends several notifications to one user as:
* * single messages based on a user profile setting.
*/
public function test_cron_profile_single_mails(): void {
global $DB;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$forum2 = $userhelper->forums->forum2;
// Add 5 discussions to forum 1.
$posts = [];
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$posts[] = $post;
}
// Add 5 discussions to forum 2.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum2, $user, ['mailnow' => 1]);
$posts[] = $post;
}
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 0, array('id' => $user->id));
// Set the maildigest preference for forum1 to default.
forum_set_user_maildigest($forum1, -1, $user);
// Set the maildigest preference for forum2 to default.
forum_set_user_maildigest($forum2, -1, $user);
// No digests mails should be sent, but 10 forum mails will be sent.
$expect = [
(object) [
'userid' => $user->id,
'messages' => 10,
'digests' => 0,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_notifications_and_assert($user, $posts);
}
/**
* Sends several notifications to one user as:
* * daily digests coming from the user profile setting.
*/
public function test_cron_profile_digest_email(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$forum2 = $userhelper->forums->forum2;
$posts = [];
// Add 5 discussions to forum 1.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$posts[] = $post;
}
// Add 5 discussions to forum 2.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum2, $user, ['mailnow' => 1]);
$posts[] = $post;
}
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 1, array('id' => $user->id));
// Set the maildigest preference for forum1 to default.
forum_set_user_maildigest($forum1, -1, $user);
// Set the maildigest preference for forum2 to default.
forum_set_user_maildigest($forum2, -1, $user);
// No digests mails should be sent, but 10 forum mails will be sent.
$expect = [
(object) [
'userid' => $user->id,
'messages' => 0,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_digests_and_assert($user, $posts);
}
/**
* Send digests to a user who cannot view fullnames
*/
public function test_cron_digest_view_fullnames_off(): void {
global $DB, $CFG;
$CFG->fullnamedisplay = 'lastname';
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$posts = [];
// Add 1 discussions to forum 1.
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$posts[] = $post;
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 1, array('id' => $user->id));
// No digests mails should be sent, but 1 forum mails will be sent.
$expect = [
(object) [
'userid' => $user->id,
'messages' => 0,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_digests_and_assert($user, $posts);
// The user does not, by default, have permission to view the fullname.
$messages = $this->messagesink->get_messages_by_component('mod_forum');
$messages = reset($messages);
$messagecontent = $messages->fullmessage;
// Assert that the expected name is present (lastname only).
$this->assertStringContainsString(fullname($user, false), $messagecontent);
// Assert that the full name is not present (firstname lastname only).
$this->assertStringNotContainsString(fullname($user, true), $messagecontent);
}
/**
* Send digests to a user who can view fullnames.
*/
public function test_cron_digest_view_fullnames_on(): void {
global $DB, $CFG;
$CFG->fullnamedisplay = 'lastname';
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$posts = [];
assign_capability(
'moodle/site:viewfullnames',
CAP_ALLOW,
$DB->get_field('role', 'id', ['shortname' => 'student']),
\context_course::instance($course1->id)
);
// Add 1 discussions to forum 1.
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$posts[] = $post;
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 1, array('id' => $user->id));
// No digests mails should be sent, but 1 forum mails will be sent.
$expect = [
(object) [
'userid' => $user->id,
'messages' => 0,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_digests_and_assert($user, $posts);
// The user does not, by default, have permission to view the fullname.
// However we have given the user that capability so we expect to see both firstname and lastname.
$messages = $this->messagesink->get_messages_by_component('mod_forum');
$messages = reset($messages);
$messagecontent = $messages->fullmessage;
// Assert that the expected name is present (lastname only).
$this->assertStringContainsString(fullname($user, false), $messagecontent);
// Assert that the full name is also present (firstname lastname only).
$this->assertStringContainsString(fullname($user, true), $messagecontent);
}
/**
* Sends several notifications to one user as:
* * daily digests coming from the per-forum setting; and
* * single e-mails from the profile setting.
*/
public function test_cron_mixed_email_1(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$forum2 = $userhelper->forums->forum2;
$posts = [];
$digests = [];
// Add 5 discussions to forum 1.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$digests[] = $post;
}
// Add 5 discussions to forum 2.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum2, $user, ['mailnow' => 1]);
$posts[] = $post;
}
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 0, array('id' => $user->id));
// Set the maildigest preference for forum1 to digest.
forum_set_user_maildigest($forum1, 1, $user);
// Set the maildigest preference for forum2 to default (single).
forum_set_user_maildigest($forum2, -1, $user);
// One digest e-mail should be sent, and five individual notifications.
$expect = [
(object) [
'userid' => $user->id,
'messages' => 5,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_notifications_and_assert($user, $posts);
$this->send_digests_and_assert($user, $digests);
}
/**
* Sends several notifications to one user as:
* * single e-mails from the per-forum setting; and
* * daily digests coming from the per-user setting.
*/
public function test_cron_mixed_email_2(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$forum2 = $userhelper->forums->forum2;
$posts = [];
$digests = [];
// Add 5 discussions to forum 1.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$digests[] = $post;
}
// Add 5 discussions to forum 2.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum2, $user, ['mailnow' => 1]);
$posts[] = $post;
}
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 1, array('id' => $user->id));
// Set the maildigest preference for forum1 to digest.
forum_set_user_maildigest($forum1, -1, $user);
// Set the maildigest preference for forum2 to single.
forum_set_user_maildigest($forum2, 0, $user);
// One digest e-mail should be sent, and five individual notifications.
$expect = [
(object) [
'userid' => $user->id,
'messages' => 5,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_notifications_and_assert($user, $posts);
$this->send_digests_and_assert($user, $digests);
}
/**
* Sends several notifications to one user as:
* * daily digests coming from the per-forum setting.
*/
public function test_cron_forum_digest_email(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$forum2 = $userhelper->forums->forum2;
$fulldigests = [];
$shortdigests = [];
// Add 5 discussions to forum 1.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$fulldigests[] = $post;
}
// Add 5 discussions to forum 2.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum2, $user, ['mailnow' => 1]);
$shortdigests[] = $post;
}
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 0, array('id' => $user->id));
// Set the maildigest preference for forum1 to digest (complete).
forum_set_user_maildigest($forum1, 1, $user);
// Set the maildigest preference for forum2 to digest (short).
forum_set_user_maildigest($forum2, 2, $user);
// One digest e-mail should be sent, and no individual notifications.
$expect = [
(object) [
'userid' => $user->id,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_digests_and_assert($user, $fulldigests, $shortdigests);
}
/**
* The digest being in the past is queued til the next day.
*/
public function test_cron_digest_previous_day(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$forum2 = $userhelper->forums->forum2;
$fulldigests = [];
$shortdigests = [];
// Add 1 discussions to forum 1.
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$fulldigests[] = $post;
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 1, array('id' => $user->id));
// Set the digest time to midnight.
$CFG->digestmailtime = 0;
// One digest e-mail should be sent, and no individual notifications.
$expect = [
(object) [
'userid' => $user->id,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$tasks = $DB->get_records('task_adhoc', ['component' => 'mod_forum']);
$task = reset($tasks);
$this->assertGreaterThanOrEqual(time(), $task->nextruntime);
}
/**
* The digest being in the future is queued for today.
*/
public function test_cron_digest_same_day(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$forum2 = $userhelper->forums->forum2;
$fulldigests = [];
$shortdigests = [];
// Add 1 discussions to forum 1.
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$fulldigests[] = $post;
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 1, array('id' => $user->id));
// Set the digest time to the future (magic, shouldn't work).
$CFG->digestmailtime = 25;
// One digest e-mail should be sent, and no individual notifications.
$expect = [
(object) [
'userid' => $user->id,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$tasks = $DB->get_records('task_adhoc', ['component' => 'mod_forum']);
$task = reset($tasks);
$digesttime = usergetmidnight(time(), \core_date::get_server_timezone()) + ($CFG->digestmailtime * 3600);
$this->assertLessThanOrEqual($digesttime, $task->nextruntime);
}
/**
* Tests that if a new message is posted after the days digest time,
* but before that days digests are sent a new task is created.
*/
public function test_cron_digest_queue_next_before_current_processed(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$forum1 = $userhelper->forums->forum1;
// Add 1 discussions to forum 1.
$this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
// Set the tested user's default maildigest setting.
$DB->set_field('user', 'maildigest', 1, ['id' => $user->id]);
// Set the digest time to the future (magic, shouldn't work).
$CFG->digestmailtime = 25;
// One digest e-mail should be sent, and no individual notifications.
$expect = [
(object) [
'userid' => $user->id,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
// Set the digest time to midnight.
$CFG->digestmailtime = 0;
// Add another discussions to forum 1.
$this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
// One digest e-mail should be sent, and no individual notifications.
$expect = [
(object) [
'userid' => $user->id,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
// There should now be two tasks queued.
$tasks = $DB->get_records('task_adhoc', ['component' => 'mod_forum']);
$this->assertCount(2, $tasks);
// Add yet another another discussions to forum 1.
$this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
// One digest e-mail should be sent, and no individual notifications.
$expect = [
(object) [
'userid' => $user->id,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
// There should still be two tasks queued.
$tasks = $DB->get_records('task_adhoc', ['component' => 'mod_forum']);
$this->assertCount(2, $tasks);
}
/**
* The sending of a digest marks posts as read if automatic message read marking is set.
*/
public function test_cron_digest_marks_posts_read(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Disable the 'Manual message read marking' option.
$CFG->forum_usermarksread = false;
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$posts = [];
// Set the tested user's default maildigest, trackforums, read tracking settings.
$DB->set_field('user', 'maildigest', 1, ['id' => $user->id]);
$DB->set_field('user', 'trackforums', 1, ['id' => $user->id]);
set_user_preference('forum_markasreadonnotification', 1, $user->id);
// Set the maildigest preference for forum1 to default.
forum_set_user_maildigest($forum1, -1, $user);
// Add 5 discussions to forum 1.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$posts[] = $post;
}
// There should be unread posts for the forum.
$expectedposts = [
$forum1->id => (object) [
'id' => $forum1->id,
'unread' => count($posts),
],
];
$this->assertEquals($expectedposts, forum_tp_get_course_unread_posts($user->id, $course1->id));
// One digest mail should be sent and no other messages.
$expect = [
(object) [
'userid' => $user->id,
'messages' => 0,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_digests_and_assert($user, $posts);
// Verify that there are no unread posts for any forums.
$this->assertEmpty(forum_tp_get_course_unread_posts($user->id, $course1->id));
}
/**
* The sending of a digest does not mark posts as read when manual message read marking is set.
*/
public function test_cron_digest_leaves_posts_unread(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
// Enable the 'Manual message read marking' option.
$CFG->forum_usermarksread = true;
// Set up a basic user enrolled in a course.
$userhelper = $this->helper_setup_user_in_course();
$user = $userhelper->user;
$course1 = $userhelper->courses->course1;
$forum1 = $userhelper->forums->forum1;
$posts = [];
// Set the tested user's default maildigest, trackforums, read tracking settings.
$DB->set_field('user', 'maildigest', 1, ['id' => $user->id]);
$DB->set_field('user', 'trackforums', 1, ['id' => $user->id]);
set_user_preference('forum_markasreadonnotification', 1, $user->id);
// Set the maildigest preference for forum1 to default.
forum_set_user_maildigest($forum1, -1, $user);
// Add 5 discussions to forum 1.
for ($i = 0; $i < 5; $i++) {
list($discussion, $post) = $this->helper_post_to_forum($forum1, $user, ['mailnow' => 1]);
$posts[] = $post;
}
// There should be unread posts for the forum.
$expectedposts = [
$forum1->id => (object) [
'id' => $forum1->id,
'unread' => count($posts),
],
];
$this->assertEquals($expectedposts, forum_tp_get_course_unread_posts($user->id, $course1->id));
// One digest mail should be sent and no other messages.
$expect = [
(object) [
'userid' => $user->id,
'messages' => 0,
'digests' => 1,
],
];
$this->queue_tasks_and_assert($expect);
$this->send_digests_and_assert($user, $posts);
// Verify that there are still the same unread posts for the forum.
$this->assertEquals($expectedposts, forum_tp_get_course_unread_posts($user->id, $course1->id));
}
}
File diff suppressed because it is too large Load Diff
+186
View File
@@ -0,0 +1,186 @@
<?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 mod_forum;
use mod_forum\output\forum_post_email;
/**
* Tests for the forum output/email class.
*
* @package mod_forum
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class output_email_test extends \advanced_testcase {
/**
* Data provider for the postdate function tests.
*/
public function postdate_provider() {
return array(
'Timed discussions disabled, timestart unset' => array(
'globalconfig' => array(
'forum_enabletimedposts' => 0,
),
'forumconfig' => array(
),
'postconfig' => array(
'modified' => 1000,
),
'discussionconfig' => array(
),
'expectation' => 1000,
),
'Timed discussions disabled, timestart set and newer' => array(
'globalconfig' => array(
'forum_enabletimedposts' => 0,
),
'forumconfig' => array(
),
'postconfig' => array(
'modified' => 1000,
),
'discussionconfig' => array(
'timestart' => 2000,
),
'expectation' => 1000,
),
'Timed discussions disabled, timestart set but older' => array(
'globalconfig' => array(
'forum_enabletimedposts' => 0,
),
'forumconfig' => array(
),
'postconfig' => array(
'modified' => 1000,
),
'discussionconfig' => array(
'timestart' => 500,
),
'expectation' => 1000,
),
'Timed discussions enabled, timestart unset' => array(
'globalconfig' => array(
'forum_enabletimedposts' => 1,
),
'forumconfig' => array(
),
'postconfig' => array(
'modified' => 1000,
),
'discussionconfig' => array(
),
'expectation' => 1000,
),
'Timed discussions enabled, timestart set and newer' => array(
'globalconfig' => array(
'forum_enabletimedposts' => 1,
),
'forumconfig' => array(
),
'postconfig' => array(
'modified' => 1000,
),
'discussionconfig' => array(
'timestart' => 2000,
),
'expectation' => 2000,
),
'Timed discussions enabled, timestart set but older' => array(
'globalconfig' => array(
'forum_enabletimedposts' => 1,
),
'forumconfig' => array(
),
'postconfig' => array(
'modified' => 1000,
),
'discussionconfig' => array(
'timestart' => 500,
),
'expectation' => 1000,
),
);
}
/**
* Test for the forum email renderable postdate.
*
* @dataProvider postdate_provider
*
* @param array $globalconfig The configuration to set on $CFG
* @param array $forumconfig The configuration for this forum
* @param array $postconfig The configuration for this post
* @param array $discussionconfig The configuration for this discussion
* @param string $expectation The expected date
*/
public function test_postdate($globalconfig, $forumconfig, $postconfig, $discussionconfig, $expectation): void {
global $CFG, $DB;
$this->resetAfterTest(true);
// Apply the global configuration.
foreach ($globalconfig as $key => $value) {
$CFG->$key = $value;
}
// Create the fixture.
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', (object) array('course' => $course->id));
$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
$this->getDataGenerator()->enrol_user($user->id, $course->id);
// Create a new discussion.
$discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion(
(object) array_merge($discussionconfig, array(
'course' => $course->id,
'forum' => $forum->id,
'userid' => $user->id,
)));
// Apply the discussion configuration.
// Some settings are ignored by the generator and must be set manually.
$discussion = $DB->get_record('forum_discussions', array('id' => $discussion->id));
foreach ($discussionconfig as $key => $value) {
$discussion->$key = $value;
}
$DB->update_record('forum_discussions', $discussion);
// Apply the post configuration.
// Some settings are ignored by the generator and must be set manually.
$post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
foreach ($postconfig as $key => $value) {
$post->$key = $value;
}
$DB->update_record('forum_posts', $post);
// Create the renderable.
$renderable = new forum_post_email(
$course,
$cm,
$forum,
$discussion,
$post,
$user,
$user,
true
);
// Check the postdate matches our expectations.
$this->assertEquals(userdate($expectation, "", \core_date::get_user_timezone($user)), $renderable->get_postdate());
}
}
+138
View File
@@ -0,0 +1,138 @@
<?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 mod_forum;
/**
* Class mod_forum_portfolio_caller_testcase
*
* Tests behaviour of the forum_portfolio_caller class.
*
* @package mod_forum
* @copyright 2018 onwards Totara Learning Solutions LTD {@link http://www.totaralms.com/}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Brendan Cox <brendan.cox@totaralearning.com>
*/
class portfolio_caller_test extends \advanced_testcase {
/**
* Ensure that a file will be loaded in an instance of the caller when supplied valid and
* accessible post and attachment file ids.
*/
public function test_file_in_user_post_is_loaded(): void {
global $CFG;
require_once($CFG->dirroot . '/mod/forum/locallib.php');
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
$context = \context_module::instance($forum->cmid);
/* @var mod_forum_generator $forumgenerator */
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$discussion = $forumgenerator->create_discussion(
array(
'course' => $course->id,
'forum' => $forum->id,
'userid' => $user->id,
'attachment' => 1
)
);
$fs = get_file_storage();
$dummy = (object) array(
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $discussion->firstpost,
'filepath' => '/',
'filename' => 'myassignmnent.pdf'
);
$firstpostfile = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
$caller = new \forum_portfolio_caller(array(
'postid' => $discussion->firstpost,
'attachment' => $firstpostfile->get_id()
));
$caller->load_data();
$this->assertEquals($caller->get_sha1_file(), $firstpostfile->get_contenthash());
}
/**
* Ensure that files will not be loaded if the supplied attachment id is for a file that is not attached to
* the supplied post id.
*/
public function test_file_not_in_user_post_not_loaded(): void {
global $CFG;
require_once($CFG->dirroot . '/mod/forum/locallib.php');
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
$context = \context_module::instance($forum->cmid);
/* @var mod_forum_generator $forumgenerator */
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$discussion = $forumgenerator->create_discussion(
array(
'course' => $course->id,
'forum' => $forum->id,
'userid' => $user->id,
'attachment' => 1
)
);
$fs = get_file_storage();
$dummyone = (object) array(
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $discussion->firstpost,
'filepath' => '/',
'filename' => 'myassignmnent.pdf'
);
$firstpostfile = $fs->create_file_from_string($dummyone, 'Content of ' . $dummyone->filename);
// Create a second post and add a file there.
$secondpost = $forumgenerator->create_post(
array(
'discussion' => $discussion->id,
'userid' => $user->id,
'attachment' => 1
)
);
$dummytwo = (object) array(
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $secondpost->id,
'filepath' => '/',
'filename' => 'myotherthing.pdf'
);
$secondpostfile = $fs->create_file_from_string($dummytwo, 'Content of ' . $dummytwo->filename);
$caller = new \forum_portfolio_caller(array(
'postid' => $discussion->firstpost,
'attachment' => $secondpostfile->get_id()
));
$this->expectExceptionMessage('Sorry, the requested file could not be found');
$caller->load_data();
}
}
File diff suppressed because it is too large Load Diff
+239
View File
@@ -0,0 +1,239 @@
<?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 mod_forum;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/forum/lib.php');
require_once($CFG->dirroot . '/mod/forum/locallib.php');
require_once(__DIR__ . '/generator_trait.php');
/**
* Tests for private reply functionality.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class private_replies_test extends \advanced_testcase {
use mod_forum_tests_generator_trait;
/**
* Setup before tests.
*/
public function setUp(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
}
/**
* Tear down after tests.
*/
public function tearDown(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
}
/**
* Ensure that the forum_post_is_visible_privately function reports that a post is visible to a user when another
* user wrote the post, and it is not private.
*/
public function test_forum_post_is_visible_privately_not_private(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student] = $this->helper_create_users($course, 1, 'student');
[$teacher] = $this->helper_create_users($course, 1, 'teacher');
[$discussion] = $this->helper_post_to_forum($forum, $teacher);
$post = $this->helper_post_to_discussion($forum, $discussion, $teacher);
$this->setUser($student);
$cm = get_coursemodule_from_instance('forum', $forum->id);
$this->assertTrue(forum_post_is_visible_privately($post, $cm));
}
/**
* Ensure that the forum_post_is_visible_privately function reports that a post is visible to a user when another
* user wrote the post, and the user under test is the intended recipient.
*/
public function test_forum_post_is_visible_privately_private_to_user(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student] = $this->helper_create_users($course, 1, 'student');
[$teacher] = $this->helper_create_users($course, 1, 'teacher');
[$discussion] = $this->helper_post_to_forum($forum, $teacher);
$post = $this->helper_post_to_discussion($forum, $discussion, $teacher, [
'privatereplyto' => $student->id,
]);
$this->setUser($student);
$cm = get_coursemodule_from_instance('forum', $forum->id);
$this->assertTrue(forum_post_is_visible_privately($post, $cm));
}
/**
* Ensure that the forum_post_is_visible_privately function reports that a post is visible to a user when another
* user wrote the post, and the user under test is a role with the view capability.
*/
public function test_forum_post_is_visible_privately_private_to_user_view_as_teacher(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student] = $this->helper_create_users($course, 1, 'student');
[$teacher, $otherteacher] = $this->helper_create_users($course, 2, 'teacher');
[$discussion] = $this->helper_post_to_forum($forum, $teacher);
$post = $this->helper_post_to_discussion($forum, $discussion, $teacher, [
'privatereplyto' => $student->id,
]);
$this->setUser($otherteacher);
$cm = get_coursemodule_from_instance('forum', $forum->id);
$this->assertTrue(forum_post_is_visible_privately($post, $cm));
}
/**
* Ensure that the forum_post_is_visible_privately function reports that a post is not visible to a user when
* another user wrote the post, and the user under test is a role without the view capability.
*/
public function test_forum_post_is_visible_privately_private_to_user_view_as_other_student(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student, $otherstudent] = $this->helper_create_users($course, 2, 'student');
[$teacher] = $this->helper_create_users($course, 1, 'teacher');
[$discussion] = $this->helper_post_to_forum($forum, $teacher);
$post = $this->helper_post_to_discussion($forum, $discussion, $teacher, [
'privatereplyto' => $student->id,
]);
$this->setUser($otherstudent);
$cm = get_coursemodule_from_instance('forum', $forum->id);
$this->assertFalse(forum_post_is_visible_privately($post, $cm));
}
/**
* Ensure that the forum_post_is_visible_privately function reports that a post is visible to a user who wrote a
* private reply, but not longer holds the view capability.
*/
public function test_forum_post_is_visible_privately_private_to_user_view_as_author(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student] = $this->helper_create_users($course, 1, 'student');
[$teacher] = $this->helper_create_users($course, 1, 'teacher');
[$discussion] = $this->helper_post_to_forum($forum, $teacher);
$post = $this->helper_post_to_discussion($forum, $discussion, $teacher, [
'privatereplyto' => $student->id,
]);
unassign_capability('mod/forum:readprivatereplies', $this->get_role_id('teacher'));
$this->setUser($teacher);
$cm = get_coursemodule_from_instance('forum', $forum->id);
$this->assertTrue(forum_post_is_visible_privately($post, $cm));
}
/**
* Ensure that the forum_user_can_reply_privately returns true for a teacher replying to a forum post.
*/
public function test_forum_user_can_reply_privately_as_teacher(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student] = $this->helper_create_users($course, 1, 'student');
[$teacher] = $this->helper_create_users($course, 1, 'teacher');
[, $post] = $this->helper_post_to_forum($forum, $student);
$this->setUser($teacher);
$cm = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($cm->id);
$this->assertTrue(forum_user_can_reply_privately($context, $post));
}
/**
* Ensure that the forum_user_can_reply_privately returns true for a teacher replying to a forum post.
*/
public function test_forum_user_can_reply_privately_as_student(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student, $otherstudent] = $this->helper_create_users($course, 2, 'student');
[, $post] = $this->helper_post_to_forum($forum, $student);
$this->setUser($otherstudent);
$cm = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($cm->id);
$this->assertFalse(forum_user_can_reply_privately($context, $post));
}
/**
* Ensure that the forum_user_can_reply_privately returns false where the parent post is already a private reply.
*/
public function test_forum_user_can_reply_privately_parent_is_already_private(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student] = $this->helper_create_users($course, 1, 'student');
[$teacher] = $this->helper_create_users($course, 1, 'teacher');
[$discussion] = $this->helper_post_to_forum($forum, $student);
$post = $this->helper_post_to_discussion($forum, $discussion, $teacher, ['privatereplyto' => $student->id]);
$this->setUser($teacher);
$cm = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($cm->id);
$this->assertFalse(forum_user_can_reply_privately($context, $post));
}
}
+151
View File
@@ -0,0 +1,151 @@
<?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 mod_forum;
use mod_forum_tests_cron_trait;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/forum/lib.php');
require_once(__DIR__ . '/cron_trait.php');
require_once(__DIR__ . '/generator_trait.php');
/**
* The forum module mail generation tests for groups.
*
* @package mod_forum
* @copyright 2013 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qanda_test extends \advanced_testcase {
// Make use of the cron tester trait.
use mod_forum_tests_cron_trait;
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* @var \phpunit_message_sink
*/
protected $messagesink;
/**
* @var \phpunit_mailer_sink
*/
protected $mailsink;
public function setUp(): void {
global $CFG;
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
\mod_forum\subscriptions::reset_discussion_cache();
// Messaging is not compatible with transactions...
$this->preventResetByRollback();
// Catch all messages.
$this->messagesink = $this->redirectMessages();
$this->mailsink = $this->redirectEmails();
// Forcibly reduce the maxeditingtime to a second in the past to
// ensure that messages are sent out.
$CFG->maxeditingtime = -1;
}
public function tearDown(): void {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
$this->messagesink->clear();
$this->messagesink->close();
unset($this->messagesink);
$this->mailsink->clear();
$this->mailsink->close();
unset($this->mailsink);
}
/**
* Test that a user who has not posted in a q&a forum does not receive
* notificatinos.
*/
public function test_user_has_not_posted(): void {
global $CFG, $DB;
$this->resetAfterTest(true);
// Create a course, with a forum.
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
'forcesubscribe' => FORUM_INITIALSUBSCRIBE,
'groupmode' => SEPARATEGROUPS,
'type' => 'qanda',
]);
// Create three students:
// - author, enrolled in group A; and
// - recipient, enrolled in group B; and
// - other, enrolled in the course, but no groups.
list($author, $recipient, $otheruser) = $this->helper_create_users($course, 3);
// Create one editing teacher, not in any group but with accessallgroups capability.
list($editingteacher) = $this->helper_create_users($course, 1, 'editingteacher');
// Post a discussion to the forum.
list($discussion, $post) = $this->helper_post_to_forum($forum, $editingteacher);
$reply = $this->helper_reply_to_post($post, $author);
$otherreply = $this->helper_reply_to_post($post, $recipient);
$DB->execute("UPDATE {forum_posts} SET modified = modified - 1");
$DB->execute("UPDATE {forum_posts} SET created = created - 1");
$DB->execute("UPDATE {forum_discussions} SET timemodified = timemodified - 1");
// Only the author, recipient, and teachers should receive.
$expect = [
'author' => (object) [
'userid' => $author->id,
'messages' => 3,
],
'recipient' => (object) [
'userid' => $recipient->id,
'messages' => 3,
],
'otheruser' => (object) [
'userid' => $otheruser->id,
'messages' => 3,
],
'editingteacher' => (object) [
'userid' => $editingteacher->id,
'messages' => 3,
],
];
$this->queue_tasks_and_assert($expect);
$posts = [$post, $reply, $otherreply];
// No notifications should be queued.
$this->send_notifications_and_assert($author, $posts);
$this->send_notifications_and_assert($recipient, $posts);
$this->send_notifications_and_assert($otheruser, [$post]);
$this->send_notifications_and_assert($editingteacher, $posts);
}
}
+97
View File
@@ -0,0 +1,97 @@
<?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 mod_forum;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once(__DIR__ . '/generator_trait.php');
require_once("{$CFG->dirroot}/mod/forum/rsslib.php");
/**
* Tests for the forum implementation of the RSS component.
*
* @package mod_forum
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class rsslib_test extends \advanced_testcase {
// Include the mod_forum test helpers.
// This includes functions to create forums, users, discussions, and posts.
use \mod_forum_tests_generator_trait;
/**
* Ensure that deleted posts are not included.
*/
public function test_forum_rss_feed_discussions_sql_respect_deleted(): void {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
$cm = get_coursemodule_from_instance('forum', $forum->id);
list($user, $otheruser) = $this->helper_create_users($course, 2);
// Post twice.
$this->helper_post_to_forum($forum, $otheruser);
list($discussion, $post) = $this->helper_post_to_forum($forum, $otheruser);
list($sql, $params) = forum_rss_feed_discussions_sql($forum, $cm);
$discussions = $DB->get_records_sql($sql, $params);
$this->assertCount(2, $discussions);
$post->deleted = 1;
$DB->update_record('forum_posts', $post);
list($sql, $params) = forum_rss_feed_discussions_sql($forum, $cm);
$discussions = $DB->get_records_sql($sql, $params);
$this->assertCount(1, $discussions);
}
/**
* Ensure that deleted posts are not included.
*/
public function test_forum_rss_feed_posts_sql_respect_deleted(): void {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
$cm = get_coursemodule_from_instance('forum', $forum->id);
list($user, $otheruser) = $this->helper_create_users($course, 2);
// Post twice.
$this->helper_post_to_forum($forum, $otheruser);
list($discussion, $post) = $this->helper_post_to_forum($forum, $otheruser);
list($sql, $params) = forum_rss_feed_posts_sql($forum, $cm);
$posts = $DB->get_records_sql($sql, $params);
$this->assertCount(2, $posts);
$post->deleted = 1;
$DB->update_record('forum_posts', $post);
list($sql, $params) = forum_rss_feed_posts_sql($forum, $cm);
$posts = $DB->get_records_sql($sql, $params);
$this->assertCount(1, $posts);
}
}
+522
View File
@@ -0,0 +1,522 @@
<?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/>.
/**
* Forum search unit tests.
*
* @package mod_forum
* @category test
* @copyright 2015 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\search;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
require_once($CFG->dirroot . '/mod/forum/tests/generator/lib.php');
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* Provides the unit tests for forum search.
*
* @package mod_forum
* @category test
* @copyright 2015 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class search_test extends \advanced_testcase {
/**
* @var string Area id
*/
protected $forumpostareaid = null;
public function setUp(): void {
$this->resetAfterTest(true);
set_config('enableglobalsearch', true);
$this->forumpostareaid = \core_search\manager::generate_areaid('mod_forum', 'post');
// 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();
}
/**
* Availability.
*
* @return void
*/
public function test_search_enabled(): void {
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
list($componentname, $varname) = $searcharea->get_config_var_name();
// Enabled by default once global search is enabled.
$this->assertTrue($searcharea->is_enabled());
set_config($varname . '_enabled', 0, $componentname);
$this->assertFalse($searcharea->is_enabled());
set_config($varname . '_enabled', 1, $componentname);
$this->assertTrue($searcharea->is_enabled());
}
/**
* Indexing mod forum contents.
*
* @return void
*/
public function test_posts_indexing(): void {
global $DB;
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
$this->assertInstanceOf('\mod_forum\search\post', $searcharea);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$course1 = self::getDataGenerator()->create_course();
$course2 = self::getDataGenerator()->create_course();
$this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
$this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student');
$record = new \stdClass();
$record->course = $course1->id;
// Available for both student and teacher.
$forum1 = self::getDataGenerator()->create_module('forum', $record);
// Create discussion1.
$record = new \stdClass();
$record->course = $course1->id;
$record->userid = $user1->id;
$record->forum = $forum1->id;
$record->message = 'discussion';
$discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Create post1 in discussion1.
$record = new \stdClass();
$record->discussion = $discussion1->id;
$record->parent = $discussion1->firstpost;
$record->userid = $user2->id;
$record->message = 'post2';
$discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
// 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);
// Static caches are working.
$dbreads = $DB->perf_get_reads();
$doc = $searcharea->get_document($record);
$this->assertEquals($dbreads, $DB->perf_get_reads());
$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(2, $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();
// Context test: create another forum with 1 post.
$forum2 = self::getDataGenerator()->create_module('forum', ['course' => $course1->id]);
$record = new \stdClass();
$record->course = $course1->id;
$record->userid = $user1->id;
$record->forum = $forum2->id;
$record->message = 'discussion';
self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Test indexing with each forum then combined course context.
$rs = $searcharea->get_document_recordset(0, \context_module::instance($forum1->cmid));
$this->assertEquals(2, iterator_count($rs));
$rs->close();
$rs = $searcharea->get_document_recordset(0, \context_module::instance($forum2->cmid));
$this->assertEquals(1, iterator_count($rs));
$rs->close();
$rs = $searcharea->get_document_recordset(0, \context_course::instance($course1->id));
$this->assertEquals(3, iterator_count($rs));
$rs->close();
}
/**
* Document contents.
*
* @return void
*/
public function test_posts_document(): void {
global $DB;
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
$this->assertInstanceOf('\mod_forum\search\post', $searcharea);
$user = self::getDataGenerator()->create_user();
$course1 = self::getDataGenerator()->create_course();
$this->getDataGenerator()->enrol_user($user->id, $course1->id, 'teacher');
$record = new \stdClass();
$record->course = $course1->id;
$forum1 = self::getDataGenerator()->create_module('forum', $record);
// Teacher only.
$forum2 = self::getDataGenerator()->create_module('forum', $record);
set_coursemodule_visible($forum2->cmid, 0);
// Create discussion1.
$record = new \stdClass();
$record->course = $course1->id;
$record->userid = $user->id;
$record->forum = $forum1->id;
$record->message = 'discussion';
$record->groupid = 0;
$discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Create post1 in discussion1.
$record = new \stdClass();
$record->discussion = $discussion1->id;
$record->parent = $discussion1->firstpost;
$record->userid = $user->id;
$record->subject = 'subject1';
$record->message = 'post1';
$record->groupid = -1;
$discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
$post1 = $DB->get_record('forum_posts', array('id' => $discussion1reply1->id));
$post1->forumid = $forum1->id;
$post1->courseid = $forum1->course;
$post1->groupid = -1;
$doc = $searcharea->get_document($post1);
$this->assertInstanceOf('\core_search\document', $doc);
$this->assertEquals($discussion1reply1->id, $doc->get('itemid'));
$this->assertEquals($this->forumpostareaid . '-' . $discussion1reply1->id, $doc->get('id'));
$this->assertEquals($course1->id, $doc->get('courseid'));
$this->assertEquals($user->id, $doc->get('userid'));
$this->assertEquals($discussion1reply1->subject, $doc->get('title'));
$this->assertEquals($discussion1reply1->message, $doc->get('content'));
}
/**
* Group support for forum posts.
*/
public function test_posts_group_support(): void {
// Get the search area and test generators.
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
$generator = $this->getDataGenerator();
$forumgenerator = $generator->get_plugin_generator('mod_forum');
// Create a course, a user, and two groups.
$course = $generator->create_course();
$user = $generator->create_user();
$generator->enrol_user($user->id, $course->id, 'teacher');
$group1 = $generator->create_group(['courseid' => $course->id]);
$group2 = $generator->create_group(['courseid' => $course->id]);
// Separate groups forum.
$forum = self::getDataGenerator()->create_module('forum', ['course' => $course->id,
'groupmode' => SEPARATEGROUPS]);
// Create discussion with each group and one for all groups. One has a post in.
$discussion1 = $forumgenerator->create_discussion(['course' => $course->id,
'userid' => $user->id, 'forum' => $forum->id, 'message' => 'd1',
'groupid' => $group1->id]);
$forumgenerator->create_discussion(['course' => $course->id,
'userid' => $user->id, 'forum' => $forum->id, 'message' => 'd2',
'groupid' => $group2->id]);
$forumgenerator->create_discussion(['course' => $course->id,
'userid' => $user->id, 'forum' => $forum->id, 'message' => 'd3']);
// Create a reply in discussion1.
$forumgenerator->create_post(['discussion' => $discussion1->id, 'parent' => $discussion1->firstpost,
'userid' => $user->id, 'message' => 'p1']);
// Do the indexing of all 4 posts.
$rs = $searcharea->get_recordset_by_timestamp(0);
$results = [];
foreach ($rs as $rec) {
$results[$rec->message] = $rec;
}
$rs->close();
$this->assertCount(4, $results);
// Check each document has the correct groupid.
$doc = $searcharea->get_document($results['d1']);
$this->assertTrue($doc->is_set('groupid'));
$this->assertEquals($group1->id, $doc->get('groupid'));
$doc = $searcharea->get_document($results['d2']);
$this->assertTrue($doc->is_set('groupid'));
$this->assertEquals($group2->id, $doc->get('groupid'));
$doc = $searcharea->get_document($results['d3']);
$this->assertFalse($doc->is_set('groupid'));
$doc = $searcharea->get_document($results['p1']);
$this->assertTrue($doc->is_set('groupid'));
$this->assertEquals($group1->id, $doc->get('groupid'));
// While we're here, also test that the search area requests restriction by group.
$modinfo = get_fast_modinfo($course);
$this->assertTrue($searcharea->restrict_cm_access_by_group($modinfo->get_cm($forum->cmid)));
// In visible groups mode, it won't request restriction by group.
set_coursemodule_groupmode($forum->cmid, VISIBLEGROUPS);
$modinfo = get_fast_modinfo($course);
$this->assertFalse($searcharea->restrict_cm_access_by_group($modinfo->get_cm($forum->cmid)));
}
/**
* Document accesses.
*
* @return void
*/
public function test_posts_access(): void {
global $DB;
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$course1 = self::getDataGenerator()->create_course();
$course2 = self::getDataGenerator()->create_course();
$this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'teacher');
$this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student');
$record = new \stdClass();
$record->course = $course1->id;
// Available for both student and teacher.
$forum1 = self::getDataGenerator()->create_module('forum', $record);
// Teacher only.
$forum2 = self::getDataGenerator()->create_module('forum', $record);
set_coursemodule_visible($forum2->cmid, 0);
// Create discussion1.
$record = new \stdClass();
$record->course = $course1->id;
$record->userid = $user1->id;
$record->forum = $forum1->id;
$record->message = 'discussion';
$discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Create post1 in discussion1.
$record = new \stdClass();
$record->discussion = $discussion1->id;
$record->parent = $discussion1->firstpost;
$record->userid = $user2->id;
$record->message = 'post1';
$discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
// Create discussion2 only visible to teacher.
$record = new \stdClass();
$record->course = $course1->id;
$record->userid = $user1->id;
$record->forum = $forum2->id;
$record->message = 'discussion';
$discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Create post2 in discussion2.
$record = new \stdClass();
$record->discussion = $discussion2->id;
$record->parent = $discussion2->firstpost;
$record->userid = $user1->id;
$record->message = 'post2';
$discussion2reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
$this->setUser($user2);
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($discussion1reply1->id));
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($discussion2reply1->id));
}
/**
* Test for post attachments.
*
* @return void
*/
public function test_attach_files(): void {
global $DB;
$fs = get_file_storage();
// Returns the instance as long as the area is supported.
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
$this->assertInstanceOf('\mod_forum\search\post', $searcharea);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$course1 = self::getDataGenerator()->create_course();
$this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
$this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student');
$record = new \stdClass();
$record->course = $course1->id;
$forum1 = self::getDataGenerator()->create_module('forum', $record);
// Create discussion1.
$record = new \stdClass();
$record->course = $course1->id;
$record->userid = $user1->id;
$record->forum = $forum1->id;
$record->message = 'discussion';
$record->attachemt = 1;
$discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Attach 2 file to the discussion post.
$post = $DB->get_record('forum_posts', array('discussion' => $discussion1->id));
$filerecord = array(
'contextid' => \context_module::instance($forum1->cmid)->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post->id,
'filepath' => '/',
'filename' => 'myfile1'
);
$file1 = $fs->create_file_from_string($filerecord, 'Some contents 1');
$filerecord['filename'] = 'myfile2';
$file2 = $fs->create_file_from_string($filerecord, 'Some contents 2');
// Create post1 in discussion1.
$record = new \stdClass();
$record->discussion = $discussion1->id;
$record->parent = $discussion1->firstpost;
$record->userid = $user2->id;
$record->message = 'post2';
$record->attachemt = 1;
$discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
$filerecord['itemid'] = $discussion1reply1->id;
$filerecord['filename'] = 'myfile3';
$file3 = $fs->create_file_from_string($filerecord, 'Some contents 3');
// Create post2 in discussion1.
$record = new \stdClass();
$record->discussion = $discussion1->id;
$record->parent = $discussion1->firstpost;
$record->userid = $user2->id;
$record->message = 'post3';
$discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
// Now get all the posts and see if they have the right files attached.
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
$recordset = $searcharea->get_recordset_by_timestamp(0);
$nrecords = 0;
foreach ($recordset as $record) {
$doc = $searcharea->get_document($record);
$searcharea->attach_files($doc);
$files = $doc->get_files();
// Now check that each doc has the right files on it.
switch ($doc->get('itemid')) {
case ($post->id):
$this->assertCount(2, $files);
$this->assertEquals($file1->get_id(), $files[$file1->get_id()]->get_id());
$this->assertEquals($file2->get_id(), $files[$file2->get_id()]->get_id());
break;
case ($discussion1reply1->id):
$this->assertCount(1, $files);
$this->assertEquals($file3->get_id(), $files[$file3->get_id()]->get_id());
break;
case ($discussion1reply2->id):
$this->assertCount(0, $files);
break;
default:
$this->fail('Unexpected post returned');
break;
}
$nrecords++;
}
$recordset->close();
$this->assertEquals(3, $nrecords);
}
/**
* Tests that reindexing works in order starting from the forum with most recent discussion.
*/
public function test_posts_get_contexts_to_reindex(): void {
global $DB;
$generator = $this->getDataGenerator();
$adminuser = get_admin();
$course1 = $generator->create_course();
$course2 = $generator->create_course();
$time = time() - 1000;
// Create 3 forums (two in course 1, one in course 2 - doesn't make a difference).
$forum1 = $generator->create_module('forum', ['course' => $course1->id]);
$forum2 = $generator->create_module('forum', ['course' => $course1->id]);
$forum3 = $generator->create_module('forum', ['course' => $course2->id]);
$forum4 = $generator->create_module('forum', ['course' => $course2->id]);
// Hack added time for the course_modules entries. These should not be used (they would
// be used by the base class implementation). We are setting this so that the order would
// be 4, 3, 2, 1 if this ordering were used (newest first).
$DB->set_field('course_modules', 'added', $time + 100, ['id' => $forum1->cmid]);
$DB->set_field('course_modules', 'added', $time + 110, ['id' => $forum2->cmid]);
$DB->set_field('course_modules', 'added', $time + 120, ['id' => $forum3->cmid]);
$DB->set_field('course_modules', 'added', $time + 130, ['id' => $forum4->cmid]);
$forumgenerator = $generator->get_plugin_generator('mod_forum');
// Create one discussion in forums 1 and 3, three in forum 2, and none in forum 4.
$forumgenerator->create_discussion(['course' => $course1->id,
'forum' => $forum1->id, 'userid' => $adminuser->id, 'timemodified' => $time + 20]);
$forumgenerator->create_discussion(['course' => $course1->id,
'forum' => $forum2->id, 'userid' => $adminuser->id, 'timemodified' => $time + 10]);
$forumgenerator->create_discussion(['course' => $course1->id,
'forum' => $forum2->id, 'userid' => $adminuser->id, 'timemodified' => $time + 30]);
$forumgenerator->create_discussion(['course' => $course1->id,
'forum' => $forum2->id, 'userid' => $adminuser->id, 'timemodified' => $time + 11]);
$forumgenerator->create_discussion(['course' => $course2->id,
'forum' => $forum3->id, 'userid' => $adminuser->id, 'timemodified' => $time + 25]);
// Get the contexts in reindex order.
$area = \core_search\manager::get_search_area($this->forumpostareaid);
$contexts = iterator_to_array($area->get_contexts_to_reindex(), false);
// We expect them in order of newest discussion. Forum 4 is not included at all (which is
// correct because it has no content).
$expected = [
\context_module::instance($forum2->cmid),
\context_module::instance($forum3->cmid),
\context_module::instance($forum1->cmid)
];
$this->assertEquals($expected, $contexts);
}
}
File diff suppressed because it is too large Load Diff
+75
View File
@@ -0,0 +1,75 @@
<?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 mod_forum;
/**
* The author vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class vaults_author_test extends \advanced_testcase {
/**
* Test get_from_id.
*/
public function test_get_from_id(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$authorvault = $vaultfactory->get_author_vault();
$author = $authorvault->get_from_id($user->id);
$this->assertEquals($user->id, $author->get_id());
}
/**
* Test get_context_ids_for_author_ids.
*/
public function test_get_context_ids_for_author_ids(): void {
$this->resetAfterTest();
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$userid1 = $user1->id;
$userid2 = $user2->id;
$userid3 = $user3->id;
$fakeuserid = $user3->id + 1000;
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$authorvault = $vaultfactory->get_author_vault();
$user1context = \context_user::instance($user1->id);
$user2context = \context_user::instance($user2->id);
$user3context = \context_user::instance($user3->id);
$user1contextid = $user1context->id;
$user2contextid = $user2context->id;
$user3contextid = $user3context->id;
$fakeusercontextid = null;
$userids = [$userid1, $userid2, $userid3, $fakeuserid];
$expected = [
$userid1 => $user1contextid,
$userid2 => $user2contextid,
$userid3 => $user3contextid,
$fakeuserid => $fakeusercontextid
];
$this->assertEquals($expected, $authorvault->get_context_ids_for_author_ids($userids));
}
}
@@ -0,0 +1,554 @@
<?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 mod_forum;
use mod_forum_external;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/forum/externallib.php');
require_once(__DIR__ . '/generator_trait.php');
/**
* The discussion_list vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class vaults_discussion_list_test extends \advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/** @var \mod_forum\local\vaults\discussion_list */
private $vault;
/**
* Set up function for tests.
*/
public function setUp(): void {
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$this->vault = $vaultfactory->get_discussions_in_forum_vault();
}
/**
* Test get_from_id.
*/
public function test_get_from_id(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion, $post] = $this->helper_post_to_forum($forum, $user);
$discussionlist = $vault->get_from_id($discussion->id);
$this->assertEquals($discussion->id, $discussionlist->get_discussion()->get_id());
$this->assertEquals($post->id, $discussionlist->get_first_post()->get_id());
$this->assertEquals($user->id, $discussionlist->get_first_post_author()->get_id());
$this->assertEquals($user->id, $discussionlist->get_latest_post_author()->get_id());
}
/**
* Test get_from_forum_id.
*/
public function test_get_from_forum_id(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
self::setUser($user);
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->getDataGenerator()->enrol_user($user->id, $course->id, null, 'manual');
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, $user->id, null,
0, 0));
$now = time();
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
[$discussion3, $post4] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 8, 'timemodified' => 4]);
// By default orders the discussions by last post.
$summaries = array_values($vault->get_from_forum_id($forum->id, false, null, null,
0, 0));
$this->assertCount(3, $summaries);
$this->assertEquals($discussion3->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null,
0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[3]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, false, $user->id, null,
0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[3]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null,
1, 0));
$this->assertCount(1, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null,
1, 1));
$this->assertCount(1, $summaries);
$this->assertEquals($discussion3->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null,
1, 2));
$this->assertCount(1, $summaries);
$this->assertEquals($discussion2->id, $summaries[0]->get_discussion()->get_id());
// Create 2 replies for $post1.
$this->helper_reply_to_post($post1, $user);
$this->helper_reply_to_post($post1, $user);
// Create 3 replies for $post2.
$this->helper_reply_to_post($post2, $user);
$this->helper_reply_to_post($post2, $user);
$this->helper_reply_to_post($post2, $user);
// Create 1 reply for $post3.
$this->helper_reply_to_post($post3, $user);
// Sort discussions by last post DESC.
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_LASTPOST_DESC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[3]->get_discussion()->get_id());
// Sort discussions by last post ASC.
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_LASTPOST_ASC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion3->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[3]->get_discussion()->get_id());
// Sort discussions by replies DESC.
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_REPLIES_DESC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion2->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[3]->get_discussion()->get_id());
// Sort discussions by replies ASC.
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_REPLIES_ASC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion3->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[3]->get_discussion()->get_id());
// Sort discussions by discussion created DESC.
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_CREATED_DESC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion3->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[3]->get_discussion()->get_id());
// Sort discussions by discussion created ASC.
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_CREATED_ASC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[3]->get_discussion()->get_id());
// Sort discussions when there is a pinned discussion.
$this->pin_discussion($discussion1);
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_LASTPOST_ASC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[3]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_LASTPOST_DESC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[3]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_REPLIES_DESC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[3]->get_discussion()->get_id());
// Sort discussions where there is a pinned discussion and several starred discussions.
$this->star_discussion($discussion3, 1);
$this->star_discussion($hiddendiscussion, 1);
$summaries = array_values($vault->get_from_forum_id($forum->id, true, $user->id,
$vault::SORTORDER_REPLIES_DESC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[3]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, $user->id,
$vault::SORTORDER_REPLIES_ASC, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion3->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[3]->get_discussion()->get_id());
}
/**
* Test get_from_forum_id_and_group_id.
*/
public function test_get_from_forum_id_and_group_id(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
self::setUser($user);
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->getDataGenerator()->enrol_user($user->id, $course->id, null, 'manual');
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, true,
null, 0, 0, $user));
$now = time();
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
[$groupdiscussion1, $post4] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now - 8, 'timemodified' => 4, 'groupid' => 1]
);
[$groupdiscussion2, $post5] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now - 7, 'timemodified' => 5, 'groupid' => 2]
);
[$hiddengroupdiscussion, $post6] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now + 11, 'timemodified' => 6, 'groupid' => 3]
);
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
null, null, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[5]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], false,
$user->id, null, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[5]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
null, null, 1, 0));
$this->assertCount(1, $summaries);
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
null, null, 1, 1));
$this->assertCount(1, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
null, null, 1, 2));
$this->assertCount(1, $summaries);
$this->assertEquals($groupdiscussion2->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], false,
null, null, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($groupdiscussion2->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[3]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [], true,
null, null, 0, 0));
$this->assertCount(3, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
// Add 4 replies to $post1.
$this->helper_reply_to_post($post1, $user);
$this->helper_reply_to_post($post1, $user);
$this->helper_reply_to_post($post1, $user);
$this->helper_reply_to_post($post1, $user);
// Add 5 replies to $post2.
$this->helper_reply_to_post($post2, $user);
$this->helper_reply_to_post($post2, $user);
$this->helper_reply_to_post($post2, $user);
$this->helper_reply_to_post($post2, $user);
$this->helper_reply_to_post($post2, $user);
// Add 3 replies to $post3.
$this->helper_reply_to_post($post3, $user);
$this->helper_reply_to_post($post3, $user);
$this->helper_reply_to_post($post3, $user);
// Add 2 replies to $post4.
$this->helper_reply_to_post($post4, $user);
$this->helper_reply_to_post($post4, $user);
// Add 1 reply to $post5.
$this->helper_reply_to_post($post5, $user);
// Sort discussions by last post DESC.
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
$user->id, $vault::SORTORDER_LASTPOST_DESC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($groupdiscussion2->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($hiddengroupdiscussion->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[5]->get_discussion()->get_id());
// Sort discussions by last post ASC.
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
$user->id, $vault::SORTORDER_LASTPOST_ASC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($hiddengroupdiscussion->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[5]->get_discussion()->get_id());
// Sort discussions by replies DESC.
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
$user->id, $vault::SORTORDER_REPLIES_DESC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($discussion2->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($hiddengroupdiscussion->id, $summaries[5]->get_discussion()->get_id());
// Sort discussions by replies ASC.
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
$user->id, $vault::SORTORDER_REPLIES_ASC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[5]->get_discussion()->get_id());
// Sort discussions by discussion created DESC.
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
$user->id, $vault::SORTORDER_CREATED_DESC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[5]->get_discussion()->get_id());
// Sort discussions by discussion created ASC.
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true,
$user->id, $vault::SORTORDER_CREATED_ASC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($hiddengroupdiscussion->id, $summaries[5]->get_discussion()->get_id());
// Sort discussions when there is a pinned discussion.
$this->pin_discussion($discussion1);
$this->pin_discussion($hiddendiscussion);
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_LASTPOST_DESC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($hiddengroupdiscussion->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[5]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_LASTPOST_ASC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($hiddengroupdiscussion->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[5]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null,
$vault::SORTORDER_REPLIES_DESC, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($hiddengroupdiscussion->id, $summaries[5]->get_discussion()->get_id());
}
/**
* Test get_total_discussion_count_from_forum_id.
*/
public function test_get_total_discussion_count_from_forum_id(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals(0, $vault->get_total_discussion_count_from_forum_id($forum->id, true,
null));
$now = time();
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
$this->assertEquals(2, $vault->get_total_discussion_count_from_forum_id($forum->id, false,
null));
$this->assertEquals(3, $vault->get_total_discussion_count_from_forum_id($forum->id, true,
null));
$this->assertEquals(3, $vault->get_total_discussion_count_from_forum_id($forum->id, false,
$user->id));
}
/**
* Test get_total_discussion_count_from_forum_id_and_group_id.
*/
public function test_get_total_discussion_count_from_forum_id_and_group_id(): void {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
self::setUser($user);
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, null,
null, 0, 0));
$now = time();
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
[$groupdiscussion1, $post4] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now - 8, 'timemodified' => 4, 'groupid' => 1]
);
[$groupdiscussion2, $post5] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now - 7, 'timemodified' => 5, 'groupid' => 2]
);
[$hiddengroupdiscussion, $post6] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now + 11, 'timemodified' => 6, 'groupid' => 3]
);
$this->assertEquals(6, $vault->get_total_discussion_count_from_forum_id_and_group_id($forum->id, [1, 2, 3],
true, null));
$this->assertEquals(6, $vault->get_total_discussion_count_from_forum_id_and_group_id(
$forum->id,
[1, 2, 3],
false,
$user->id
));
$this->assertEquals(4, $vault->get_total_discussion_count_from_forum_id_and_group_id($forum->id, [1, 2, 3],
false, null));
$this->assertEquals(3, $vault->get_total_discussion_count_from_forum_id_and_group_id($forum->id, [],
true, null));
}
/**
* Pin a duscussion.
*
* @param \stdClass $discussion
*/
private function pin_discussion(\stdClass $discussion) {
global $DB;
$DB->update_record('forum_discussions',
(object) array('id' => $discussion->id, 'pinned' => FORUM_DISCUSSION_PINNED));
}
/**
* Star a duscussion.
*
* @param \stdClass $discussion
* @param bool $targetstate The new starred state of the discussion (0 => unstar, 1 => star)
*/
private function star_discussion(\stdClass $discussion, bool $targetstate) {
mod_forum_external::toggle_favourite_state($discussion->id, $targetstate);
}
}
+118
View File
@@ -0,0 +1,118 @@
<?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 mod_forum;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The discussion vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class vaults_discussion_test extends \advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/** @var \mod_forum\local\vaults\discussion */
private $vault;
/**
* Set up function for tests.
*/
public function setUp(): void {
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$this->vault = $vaultfactory->get_discussion_vault();
}
/**
* Test get_from_id.
*/
public function test_get_from_id(): void {
$this->resetAfterTest();
$vault = $this->vault;
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussionrecord, $post] = $this->helper_post_to_forum($forum, $user);
$discussion = $vault->get_from_id($discussionrecord->id);
$this->assertEquals($discussionrecord->id, $discussion->get_id());
}
/**
* Test get_first_discussion_in_forum.
*/
public function test_get_first_discussion_in_forum(): void {
$this->resetAfterTest();
$vault = $this->vault;
$entityfactory = \mod_forum\local\container::get_entity_factory();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$forumentity = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$this->assertEquals(null, $vault->get_first_discussion_in_forum($forumentity));
[$discussion1, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 2]);
[$discussion2, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 1]);
[$discussion3, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 3]);
$discussionentity = $vault->get_first_discussion_in_forum($forumentity);
$this->assertEquals($discussion2->id, $discussionentity->get_id());
}
/**
* Test get_all_discussions_in_forum
*/
public function test_get_all_discussions_in_forum(): void {
$this->resetAfterTest();
$vault = $this->vault;
$entityfactory = \mod_forum\local\container::get_entity_factory();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
$forumentity = $entityfactory->get_forum_from_stdClass($forum, $context, $coursemodule, $course);
$this->assertEquals([], $vault->get_all_discussions_in_forum($forumentity));
[$discussion1, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 2]);
[$discussion2, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 1]);
[$discussion3, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 3]);
$discussionentity = $vault->get_all_discussions_in_forum($forumentity);
$this->assertArrayHasKey($discussion1->id, $discussionentity); // Order is not guaranteed, so just verify element existence.
$this->assertArrayHasKey($discussion2->id, $discussionentity);
$this->assertArrayHasKey($discussion3->id, $discussionentity);
}
}
+151
View File
@@ -0,0 +1,151 @@
<?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 mod_forum;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The forum vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \mod_forum\local\vaults\forum
*/
class vaults_forum_test extends \advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* Test get_from_id.
*
* @covers ::get_from_id
*/
public function test_get_from_id(): void {
$this->resetAfterTest();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_forum_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$entity = $vault->get_from_id($forum->id);
$this->assertEquals($forum->id, $entity->get_id());
}
/**
* Test get_from_course_module_id.
*
* @covers ::get_from_course_module_id
*/
public function test_get_from_course_module_id(): void {
$this->resetAfterTest();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_forum_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule1 = get_coursemodule_from_instance('forum', $forum1->id);
$coursemodule2 = get_coursemodule_from_instance('forum', $forum2->id);
// Don't exist.
$entity = $vault->get_from_course_module_id($coursemodule1->id + 100);
$this->assertEquals(null, $entity);
$entity = $vault->get_from_course_module_id($coursemodule1->id);
$this->assertEquals($forum1->id, $entity->get_id());
}
/**
* Test get_from_course_module_ids.
*
* @covers ::get_from_course_module_ids
*/
public function test_get_from_course_module_ids(): void {
$this->resetAfterTest();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_forum_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule1 = get_coursemodule_from_instance('forum', $forum1->id);
$coursemodule2 = get_coursemodule_from_instance('forum', $forum2->id);
// Don't exist.
$entities = array_values($vault->get_from_course_module_ids([$coursemodule1->id + 100, $coursemodule1->id + 200]));
$this->assertEquals([], $entities);
$entities = array_values($vault->get_from_course_module_ids([$coursemodule1->id, $coursemodule2->id]));
usort($entities, function($a, $b) {
return $a->get_id() <=> $b->get_id();
});
$this->assertCount(2, $entities);
$this->assertEquals($forum1->id, $entities[0]->get_id());
$this->assertEquals($forum2->id, $entities[1]->get_id());
$entities = array_values($vault->get_from_course_module_ids([$coursemodule1->id]));
usort($entities, function($a, $b) {
return $a->get_id() <=> $b->get_id();
});
$this->assertCount(1, $entities);
$this->assertEquals($forum1->id, $entities[0]->get_id());
}
/**
* Test get_from_post_id.
*
* @covers ::get_from_post_id
*/
public function test_get_from_post_id(): void {
$this->resetAfterTest();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_forum_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion, $post] = $this->helper_post_to_forum($forum, $user);
$reply = $this->helper_reply_to_post($post, $user);
$otherforum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$otherdiscussion, $otherpost] = $this->helper_post_to_forum($otherforum, $user);
$otherreply = $this->helper_reply_to_post($otherpost, $user);
$entity = $vault->get_from_post_id($post->id);
$this->assertEquals($forum->id, $entity->get_id());
$entity = $vault->get_from_post_id($reply->id);
$this->assertEquals($forum->id, $entity->get_id());
$this->assertEmpty($vault->get_from_post_id(-1));
}
}
@@ -0,0 +1,171 @@
<?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 mod_forum;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The post_attachment vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class vaults_post_attachment_test extends \advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* Test get_attachments_for_posts.
*/
public function test_get_attachments_for_posts(): void {
$this->resetAfterTest();
$filestorage = get_file_storage();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_post_attachment_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
[$discussion, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
$attachment1 = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post1->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$attachment2 = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post2->id,
'filepath' => '/',
'filename' => 'example2.jpg',
],
'image contents'
);
$post1 = $entityfactory->get_post_from_stdClass($post1);
$post2 = $entityfactory->get_post_from_stdClass($post2);
$post3 = $entityfactory->get_post_from_stdClass($post3);
$results = $vault->get_attachments_for_posts(\context_system::instance(), [$post1, $post2, $post3]);
$this->assertCount(3, $results);
$this->assertEquals([], $results[$post1->get_id()]);
$this->assertEquals([], $results[$post2->get_id()]);
$this->assertEquals([], $results[$post3->get_id()]);
$results = $vault->get_attachments_for_posts($context, [$post1]);
$this->assertCount(1, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$results = $vault->get_attachments_for_posts($context, [$post1, $post2]);
$this->assertCount(2, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$this->assertEquals($attachment2->get_filename(), $results[$post2->get_id()][0]->get_filename());
$results = $vault->get_attachments_for_posts($context, [$post1, $post2, $post3]);
$this->assertCount(3, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$this->assertEquals($attachment2->get_filename(), $results[$post2->get_id()][0]->get_filename());
$this->assertEquals([], $results[$post3->get_id()]);
}
/**
* Test get_inline_attachments_for_posts.
*/
public function test_get_inline_attachments_for_posts(): void {
$this->resetAfterTest();
$filestorage = get_file_storage();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_post_attachment_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = \context_module::instance($coursemodule->id);
[$discussion, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
$attachment1 = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'post',
'itemid' => $post1->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$attachment2 = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'post',
'itemid' => $post2->id,
'filepath' => '/',
'filename' => 'example2.jpg',
],
'image contents'
);
$post1 = $entityfactory->get_post_from_stdClass($post1);
$post2 = $entityfactory->get_post_from_stdClass($post2);
$post3 = $entityfactory->get_post_from_stdClass($post3);
$results = $vault->get_inline_attachments_for_posts(\context_system::instance(), [$post1, $post2, $post3]);
$this->assertCount(3, $results);
$this->assertEquals([], $results[$post1->get_id()]);
$this->assertEquals([], $results[$post2->get_id()]);
$this->assertEquals([], $results[$post3->get_id()]);
$results = $vault->get_inline_attachments_for_posts($context, [$post1]);
$this->assertCount(1, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$results = $vault->get_inline_attachments_for_posts($context, [$post1, $post2]);
$this->assertCount(2, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$this->assertEquals($attachment2->get_filename(), $results[$post2->get_id()][0]->get_filename());
$results = $vault->get_inline_attachments_for_posts($context, [$post1, $post2, $post3]);
$this->assertCount(3, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$this->assertEquals($attachment2->get_filename(), $results[$post2->get_id()][0]->get_filename());
$this->assertEquals([], $results[$post3->get_id()]);
}
}
@@ -0,0 +1,95 @@
<?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 mod_forum;
use mod_forum_tests_generator_trait;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The post read receipt collection vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class vaults_post_read_receipt_collection_test extends \advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* Test get_from_user_id_and_post_ids.
*/
public function test_get_from_user_id_and_post_ids(): void {
global $DB;
$this->resetAfterTest();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_post_read_receipt_collection_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$otheruser = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user);
$post3 = $this->helper_reply_to_post($post2, $user);
$DB->insert_record('forum_read', [
'userid' => $user->id,
'forumid' => $forum->id,
'discussionid' => $discussion1->id,
'postid' => $post1->id,
'firstread' => time(),
'lastread' => time()
]);
// Receipt for other user.
$DB->insert_record('forum_read', [
'userid' => $otheruser->id,
'forumid' => $forum->id,
'discussionid' => $discussion1->id,
'postid' => $post1->id,
'firstread' => time(),
'lastread' => time()
]);
$DB->insert_record('forum_read', [
'userid' => $user->id,
'forumid' => $forum->id,
'discussionid' => $discussion2->id,
'postid' => $post3->id,
'firstread' => time(),
'lastread' => time()
]);
$post1 = $entityfactory->get_post_from_stdClass($post1);
$post2 = $entityfactory->get_post_from_stdClass($post2);
$post3 = $entityfactory->get_post_from_stdClass($post3);
$collection = $vault->get_from_user_id_and_post_ids($user->id, [$post1->get_id(), $post2->get_id()]);
// True because there is a read receipt for this user.
$this->assertTrue($collection->has_user_read_post($user, $post1));
// False because other user wasn't included in the fetch.
$this->assertFalse($collection->has_user_read_post($otheruser, $post1));
// No read receipt.
$this->assertFalse($collection->has_user_read_post($user, $post2));
// Wasn't included in fetch.
$this->assertFalse($collection->has_user_read_post($user, $post3));
}
}
File diff suppressed because it is too large Load Diff