From f6da0c1ff068e0c8ff28713446f757e471e173ff Mon Sep 17 00:00:00 2001 From: Laurent David Date: Fri, 1 Aug 2025 10:40:22 +0200 Subject: [PATCH] MDL-83889 mod_bigbluebuttonbn: Add date classes and tests --- .upgradenotes/MDL-83889-2025080108444783.yml | 5 + public/mod/bigbluebuttonbn/classes/dates.php | 116 ++++++++++ .../templates/room_view.mustache | 22 +- ...bluebuttonbn_activity_availability.feature | 12 +- .../mod/bigbluebuttonbn/tests/dates_test.php | 217 ++++++++++++++++++ .../bigbluebuttonbn/tests/generator/lib.php | 18 +- .../bigbluebuttonbn/tests/generator_test.php | 105 +++++++++ 7 files changed, 469 insertions(+), 26 deletions(-) create mode 100644 .upgradenotes/MDL-83889-2025080108444783.yml create mode 100644 public/mod/bigbluebuttonbn/classes/dates.php create mode 100644 public/mod/bigbluebuttonbn/tests/dates_test.php create mode 100644 public/mod/bigbluebuttonbn/tests/generator_test.php diff --git a/.upgradenotes/MDL-83889-2025080108444783.yml b/.upgradenotes/MDL-83889-2025080108444783.yml new file mode 100644 index 00000000000..ae518842735 --- /dev/null +++ b/.upgradenotes/MDL-83889-2025080108444783.yml @@ -0,0 +1,5 @@ +issueNumber: MDL-83889 +notes: + mod_bigbluebuttonbn: + - message: Add activity_dates class to BigblueButton module. + type: improved diff --git a/public/mod/bigbluebuttonbn/classes/dates.php b/public/mod/bigbluebuttonbn/classes/dates.php new file mode 100644 index 00000000000..bfc214b9099 --- /dev/null +++ b/public/mod/bigbluebuttonbn/classes/dates.php @@ -0,0 +1,116 @@ +. + +declare(strict_types=1); + +namespace mod_bigbluebuttonbn; + +use cm_info; +use core\activity_dates; + +/** + * Class for fetching the important dates in mod_bigbluebuttonbn for a given module instance and a user. + * + * @package mod_bigbluebuttonbn + * @copyright 2025 Laurent David + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class dates extends activity_dates { + /** + * Returns the activity due date. + * + * @var int|null $timeclose the activity due date + */ + protected ?int $timeclose = null; + /** + * @var int|null $timeopen the activity open date + */ + protected ?int $timeopen = null; + + /** + * @var instance the instance of the activity + */ + protected instance $instance; + + /** + * activity_dates constructor. + * + * @param cm_info $cm course module + * @param int $userid user id + */ + public function __construct(cm_info $cm, int $userid) { + parent::__construct($cm, $userid); + $this->instance = instance::get_from_cmid((int) $cm->id); + } + + /** + * Returns a list of important dates in mod_choice + * + * @return array + */ + protected function get_dates(): array { + $timeopen = $this->instance->get_instance_var('openingtime'); + $timeclose = $this->instance->get_instance_var('closingtime'); + $now = time(); + $dates = []; + + if ($timeopen) { + $openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened'; + $dates[] = [ + 'dataid' => 'timeopen', + 'label' => get_string($openlabelid, 'course'), + 'timestamp' => (int) $timeopen, + ]; + $this->timeopen = (int) $timeopen; + } + + if ($timeclose) { + $closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed'; + $dates[] = [ + 'dataid' => 'timeclose', + 'label' => get_string($closelabelid, 'course'), + 'timestamp' => (int) $timeclose, + ]; + $this->timeclose = (int) $timeclose; + } + + return $dates; + } + + /** + * Returns the activity due date. + * + * @return int|null + */ + public function get_close_date(): ?int { + if (!isset($this->timeclose)) { + $this->get_dates(); + } + return $this->timeclose; + } + + /** + * Returns the activity open date. + * + * @return int|null + */ + public function get_open_date(): ?int { + if (!isset($this->timeopen)) { + $this->get_dates(); + } + return $this->timeopen; + } +} diff --git a/public/mod/bigbluebuttonbn/templates/room_view.mustache b/public/mod/bigbluebuttonbn/templates/room_view.mustache index 2aebf5bd8c6..d55e1e489d4 100644 --- a/public/mod/bigbluebuttonbn/templates/room_view.mustache +++ b/public/mod/bigbluebuttonbn/templates/room_view.mustache @@ -40,25 +40,6 @@ {{#userdate}} {{startedat}}, {{#str}} strftimetime, core_langconfig{{/str}} {{/userdate}}. {{statusmessage}} - {{/statusrunning}} - {{^statusrunning}} -
{{statusmessage}}
- {{/statusrunning}} -
- {{#openingtime}} - - {{#str}}mod_form_field_openingtime, bigbluebuttonbn{{/str}}: - {{#userdate}} {{.}}, {{#str}} strftimedaydatetime, langconfig {{/str}} {{/userdate}} - - {{/openingtime}} - {{#closingtime}} -
- {{#str}}mod_form_field_closingtime, bigbluebuttonbn{{/str}}: - {{#userdate}} {{.}}, {{#str}} strftimedaydatetime, langconfig {{/str}} {{/userdate}} -
- {{/closingtime}} -
- {{#statusrunning}}
{{#moderatorplural}}{{#str}}view_message_moderators, mod_bigbluebuttonbn{{/str}}{{/moderatorplural}} @@ -74,6 +55,9 @@ {{participantcount}}
{{/statusrunning}} + {{^statusrunning}} +
{{statusmessage}}
+ {{/statusrunning}}
diff --git a/public/mod/bigbluebuttonbn/tests/behat/bigbluebuttonbn_activity_availability.feature b/public/mod/bigbluebuttonbn/tests/behat/bigbluebuttonbn_activity_availability.feature index 34ea0cdfe94..0e5666381d1 100644 --- a/public/mod/bigbluebuttonbn/tests/behat/bigbluebuttonbn_activity_availability.feature +++ b/public/mod/bigbluebuttonbn/tests/behat/bigbluebuttonbn_activity_availability.feature @@ -28,9 +28,9 @@ Feature: Manage BigBlueButton session timings | C1 | bigbluebuttonbn | BBB 1 | | | When I am on the "BBB 1" "bigbluebuttonbn activity" page logged in as student1 And "Join session" "link" exist - And I should see "Open:" + And I should see ":" And I should see "%A, %d %B %Y##" - And I should see "Close:" + And I should see ":" And I should see "%A, %d %B %Y##" And I am viewing calendar in "month" view And I see "BBB 1" @@ -39,7 +39,7 @@ Feature: Manage BigBlueButton session timings And I see "BBB 1" in the "Upcoming events" "block" Examples: - | openingtime | closingtime | calendarvisibility | buttonvisibility | upcomingeventvisibility | - | ##now +1 minute## | ##now +5 minutes## | should | should not | should | - | ##1 hour ago## | ##+2 hours## | should | should | should not | - | ##yesterday## | ##yesterday +3 hours## | should not | should not | should not | + | opens | closes | openingtime | closingtime | calendarvisibility | buttonvisibility | upcomingeventvisibility | + | Opens | Closes | ##now +1 minute## | ##now +5 minutes## | should | should not | should | + | Opened | Closes | ##1 hour ago## | ##+2 hours## | should | should | should not | + | Opened | Closed | ##yesterday## | ##yesterday +3 hours## | should not | should not | should not | diff --git a/public/mod/bigbluebuttonbn/tests/dates_test.php b/public/mod/bigbluebuttonbn/tests/dates_test.php new file mode 100644 index 00000000000..bdc73b64dc7 --- /dev/null +++ b/public/mod/bigbluebuttonbn/tests/dates_test.php @@ -0,0 +1,217 @@ +. + +declare(strict_types=1); + +namespace mod_bigbluebuttonbn; + +use advanced_testcase; +use core\activity_dates; + +/** + * Class for unit testing mod_bigbluebutton\dates. + * + * @package mod_bigbluebuttonbn + * @copyright 2025 Laurent David + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \mod_bigbluebuttonbn\dates + */ +final class dates_test extends advanced_testcase { + use \mod_bigbluebuttonbn\test\testcase_helper_trait; + + /** + * Data provider for get_dates_for_module(). + * + * @return array[] + */ + public static function get_dates_for_module_provider(): array { + $clock = \core\di::get(\core\clock::class); + $now = $clock->time(); + $open = $now - DAYSECS; + $close = $now + DAYSECS; + + return [ + 'Without any dates' => [ + null, null, [], + ], + 'Only with opening time' => [ + $open, + null, + [ + [ + 'label' => get_string('activitydate:opened', 'course'), + 'timestamp' => $open, + 'dataid' => 'timeopen', + ], + ], + ], + 'Only with closing time' => [ + null, + $close, + [ + [ + 'label' => get_string('activitydate:closes', 'course'), + 'timestamp' => $close, + 'dataid' => 'timeclose', + ], + ], + ], + 'With both times' => [ + $open, + $close, + [ + [ + 'label' => get_string('activitydate:opened', 'course'), + 'timestamp' => $open, + 'dataid' => 'timeopen', + ], + [ + 'label' => get_string('activitydate:closes', 'course'), + 'timestamp' => $close, + 'dataid' => 'timeclose', + ], + ], + ], + 'With both times in the future' => [ + $now + DAYSECS, + $now + (2 * DAYSECS), + [ + [ + 'label' => get_string('activitydate:opens', 'course'), + 'timestamp' => $now + DAYSECS, + 'dataid' => 'timeopen', + ], + [ + 'label' => get_string('activitydate:closes', 'course'), + 'timestamp' => $now + (2 * DAYSECS), + 'dataid' => 'timeclose', + ], + ], + ], + 'With both times in the past' => [ + $now - (2 * DAYSECS), + $now - DAYSECS, + [ + [ + 'label' => get_string('activitydate:opened', 'course'), + 'timestamp' => $now - (2 * DAYSECS), + 'dataid' => 'timeopen', + ], + [ + 'label' => get_string('activitydate:closed', 'course'), + 'timestamp' => $now - DAYSECS, + 'dataid' => 'timeclose', + ], + ], + ], + ]; + } + + /** + * Test for get_dates_for_module(). + * + * @param int|null $open Opening time in the BigBlueButton. + * @param int|null $close Closing time in the BigBlueButton. + * @param array $expected The expected value of calling get_dates_for_module() + * @covers ::get_dates_for_module + * @dataProvider get_dates_for_module_provider + */ + public function test_get_dates_for_module( + ?int $open, + ?int $close, + array $expected + ): void { + $this->resetAfterTest(); + ['user' => $user, 'cm' => $cm] = $this->setup_instance($open, $close); + $this->setUser($user); + $dates = activity_dates::get_dates_for_module($cm, (int) $user->id); + + $this->assertEquals($expected, $dates); + } + + + /** + * Test for get_open_date(). + * + * @param int|null $open Opening time in the BigBlueButton. + * @param int|null $close Closing time in the BigBlueButton. + * @covers ::get_open_date + * @dataProvider get_dates_for_module_provider + */ + public function test_get_open_date( + ?int $open, + ?int $close, + ): void { + + $this->resetAfterTest(); + ['user' => $user, 'cm' => $cm] = $this->setup_instance($open, $close); + $this->setUser($user); + $dates = new \mod_bigbluebuttonbn\dates($cm, (int) $user->id); + + $this->assertEquals($open, $dates->get_open_date()); + } + + /** + * Test for get_close_date(). + * + * @param int|null $open Opening time in the BigBlueButton. + * @param int|null $close Closing time in the BigBlueButton. + * @covers ::get_close_date + * @dataProvider get_dates_for_module_provider + */ + public function test_get_close_date( + ?int $open, + ?int $close, + ): void { + + $this->resetAfterTest(); + ['user' => $user, 'cm' => $cm] = $this->setup_instance($open, $close); + $dates = new \mod_bigbluebuttonbn\dates($cm, (int) $user->id); + + $this->assertEquals($close, $dates->get_close_date()); + } + + /** + * Setup a BigBlueButton activity instance. + * + * @param int|null $open Opening time in the BigBlueButton. + * @param int|null $close Closing time in the BigBlueButton. + * @return array with keys 'user' and 'cm'. + */ + private function setup_instance( + ?int $open, + ?int $close, + ): array { + $generator = $this->getDataGenerator(); + + $course = $generator->create_course(); + $user = $generator->create_user(); + $generator->enrol_user($user->id, $course->id); + $data = []; + if ($open !== null) { + $data['openingtime'] = $open; + } + if ($close !== null) { + $data['closingtime'] = $close; + } + $this->setAdminUser(); + [$bbactivitycontext, $bbactivitycm, $bbactivity] = $this->create_instance( + $course, + $data + ); + return ['user' => $user, 'cm' => $bbactivitycm]; + } +} diff --git a/public/mod/bigbluebuttonbn/tests/generator/lib.php b/public/mod/bigbluebuttonbn/tests/generator/lib.php index 07a6adb378d..1af9c572259 100644 --- a/public/mod/bigbluebuttonbn/tests/generator/lib.php +++ b/public/mod/bigbluebuttonbn/tests/generator/lib.php @@ -69,11 +69,27 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator { "recordings_preview" => 0, "grade" => 0, ]; - $record = (array) $record; $record['participants'] = json_encode($this->get_participants_from_record($record)); + if (!empty($record['openingtime'])) { + if (is_numeric($record['openingtime'])) { + $record['openingtime'] = intval($record['openingtime']); + } else { + // If it is a string, we assume it is a timestamp. + $record['openingtime'] = strtotime($record['openingtime']); + } + } + if (!empty($record['closingtime'])) { + if (is_numeric($record['closingtime'])) { + $record['closingtime'] = intval($record['closingtime']); + } else { + // If it is a string, we assume it is a timestamp. + $record['closingtime'] = strtotime($record['closingtime']); + } + } + foreach ($defaults as $key => $value) { if (!isset($record[$key])) { $record[$key] = $value; diff --git a/public/mod/bigbluebuttonbn/tests/generator_test.php b/public/mod/bigbluebuttonbn/tests/generator_test.php new file mode 100644 index 00000000000..199792f09a6 --- /dev/null +++ b/public/mod/bigbluebuttonbn/tests/generator_test.php @@ -0,0 +1,105 @@ +. + +namespace mod_bigbluebuttonbn; + +/** + * Genarator tests class for mod_bigbluebuttonbn. + * + * @package mod_bigbluebuttonbn + * @category test + * @copyright 2025 Laurent David + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class generator_test extends \advanced_testcase { + /** + * Test the creation of a bigbluebuttonbn instance. + * @covers \mod_bigbluebuttonbn_generator::create_instance + */ + public function test_create_instance(): void { + $db = \core\di::get(\moodle_database::class); + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $bigbluebuttonbn = $this->getDataGenerator()->create_module('bigbluebuttonbn', ['course' => $course]); + $records = $db->get_records('bigbluebuttonbn', ['course' => $course->id], 'id'); + $this->assertEquals(1, count($records)); + $this->assertTrue(array_key_exists($bigbluebuttonbn->id, $records)); + + $params = ['course' => $course->id, 'name' => 'Another bigbluebuttonbn']; + $bigbluebuttonbn = $this->getDataGenerator()->create_module('bigbluebuttonbn', $params); + $records = $db->get_records('bigbluebuttonbn', ['course' => $course->id], 'id'); + $this->assertEquals(2, count($records)); + $this->assertEquals('Another bigbluebuttonbn', $records[$bigbluebuttonbn->id]->name); + } + + + /** + * Test the creation of a bigbluebuttonbn instance with a custom name. + * + * @param string|int $opening The opening time as a timestamp or human-readable date. + * @param string|int $closing The closing time as a timestamp or human-readable date + * @param int $expectedopening The expected opening time as a timestamp. + * @param int $expectedclosing The expected closing time as a timestamp. + * @covers \mod_bigbluebuttonbn_generator::create_instance + * @dataProvider provider_create_instance_with_name + */ + public function test_create_instance_with_dates( + string|int $opening, + string|int $closing, + int $expectedopening, + int $expectedclosing + ): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $params = ['course' => $course->id, 'openingtime' => $opening, 'closingtime' => $closing]; + $bigbluebuttonbn = $this->getDataGenerator()->create_module('bigbluebuttonbn', $params); + $instance = \mod_bigbluebuttonbn\instance::get_from_instanceid($bigbluebuttonbn->id); + $this->assertEquals($expectedopening, $instance->get_instance_var('openingtime')); + $this->assertEquals($expectedclosing, $instance->get_instance_var('closingtime')); + } + + /** + * Data provider for test_create_instance_with_dates. + * + * @return array[] + */ + public static function provider_create_instance_with_name(): array { + global $CFG; + require_once($CFG->libdir . '/testing/classes/frozen_clock.php'); + $clock = new \frozen_clock(); + \core\di::set(\core\clock::class, $clock); + $opening = $clock->time(); + $closing = $opening + DAYSECS; + return [ + 'Timestamp' => [ + $opening, + $closing, + $opening, + $closing, + ], + 'Human date' => [ + userdate($opening, get_string('strftimedatetimeaccurate', 'langconfig')), + userdate($closing, get_string('strftimedatetimeaccurate', 'langconfig')), + $opening, + $closing, + ], + ]; + } +}