From 411dc7c7c9208f76e8638c90614266a5c6ec54b6 Mon Sep 17 00:00:00 2001 From: Daniel Thies Date: Mon, 13 Jan 2025 12:45:01 -0600 Subject: [PATCH] Add backup and external function test --- CHANGES | 2 + tests/backup/restore_tasks_test.php | 126 ++++++++++++++++ tests/external/raise_hand_test.php | 222 ++++++++++++++++++++++++++++ tests/generator/lib.php | 4 +- version.php | 4 +- 5 files changed, 355 insertions(+), 3 deletions(-) create mode 100644 tests/backup/restore_tasks_test.php create mode 100644 tests/external/raise_hand_test.php diff --git a/CHANGES b/CHANGES index 429c567..4935828 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ Block deft response +1.2.6 Add php unit testing + 1.2.5 Bug fixes and style improvement 1.2.4 Support venue directly in Moodle App diff --git a/tests/backup/restore_tasks_test.php b/tests/backup/restore_tasks_test.php new file mode 100644 index 0000000..052481a --- /dev/null +++ b/tests/backup/restore_tasks_test.php @@ -0,0 +1,126 @@ +. + +namespace block_deft\backup; + +defined('MOODLE_INTERNAL') || die(); + +use block_deft\task; + +global $CFG; +require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php"); + +/** + * Restore tasks tests. + * + * @package block_deft + * @copyright 2024 Daniel Thies + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \backup_deft_block_structure_step + * @covers \restore_deft_block_structure_step + * @group block_deft + */ +final class restore_tasks_test extends \restore_date_testcase { + /** + * Test restore dates. + */ + public function test_restore_tasks(): void { + global $DB; + + $this->resetAfterTest(true); + + // Create courses. + $course1 = $this->getDataGenerator()->create_course(); + + /** @var \block_deft_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('block_deft'); + + $block = $this->add_deft_block_in_context(\core\context\course::instance($course1->id)); + $task = $generator->create_task($block->instance->id); + + $this->assertEquals(1, count(task::get_records(['instance' => $block->instance->id]))); + + // Do backup and restore. + $newcourseid = $this->backup_and_restore($course1); + + $this->assertEquals(2, count(task::get_records([]))); + } + + /** + * Creates a Deft response block on a context. + * + * @param \context $context The context on which we want to put the block. + * @return \block_base The created block instance. + * @throws \coding_exception + */ + protected function add_deft_block_in_context(\context $context) { + global $DB; + + $course = null; + + $page = new \moodle_page(); + $page->set_context($context); + + switch ($context->contextlevel) { + case CONTEXT_SYSTEM: + $page->set_pagelayout('frontpage'); + $page->set_pagetype('site-index'); + break; + case CONTEXT_COURSE: + $page->set_pagelayout('standard'); + $page->set_pagetype('course-view'); + $course = $DB->get_record('course', ['id' => $context->instanceid]); + $page->set_course($course); + break; + case CONTEXT_MODULE: + $page->set_pagelayout('standard'); + $mod = $DB->get_field_sql("SELECT m.name + FROM {modules} m + JOIN {course_modules} cm on cm.module = m.id + WHERE cm.id = ?", [$context->instanceid]); + $page->set_pagetype("mod-$mod-view"); + break; + case CONTEXT_USER: + $page->set_pagelayout('mydashboard'); + $page->set_pagetype('my-index'); + break; + default: + throw new coding_exception('Unsupported context for test'); + } + + $page->blocks->load_blocks(); + + $page->blocks->add_block_at_end_of_default_region('deft'); + + // We need to use another page object as load_blocks() only loads the blocks once. + $page2 = new \moodle_page(); + $page2->set_context($page->context); + $page2->set_pagelayout($page->pagelayout); + $page2->set_pagetype($page->pagetype); + if ($course) { + $page2->set_course($course); + } + + $page->blocks->load_blocks(); + $page2->blocks->load_blocks(); + $blocks = $page2->blocks->get_blocks_for_region($page2->blocks->get_default_region()); + $block = end($blocks); + + $block = block_instance('deft', $block->instance); + + return $block; + } +} diff --git a/tests/external/raise_hand_test.php b/tests/external/raise_hand_test.php new file mode 100644 index 0000000..bc8062d --- /dev/null +++ b/tests/external/raise_hand_test.php @@ -0,0 +1,222 @@ +. + +namespace block_deft\external; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); + +use core_external\external_api; +use externallib_advanced_testcase; +use stdClass; +use context_block; +use course_modinfo; +use block_deft\venue_manager; + +/** + * External function test for raise_hand + * + * @package block_deft + * @copyright 2024 Daniel Thies + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \block_deft\external\raise_hand + * @group mod_plenum + */ +final class raise_hand_test extends externallib_advanced_testcase { + /** + * Test test_raise_hand invalid id. + */ + public function test_raise_hand_invalid_id(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Setup scenario. + $scenario = $this->setup_scenario(); + + $result = raise_hand::execute(true, ''); + $result = external_api::clean_returnvalue(raise_hand::execute_returns(), $result); + $this->assertFalse($result['status']); + + $result = raise_hand::execute(false, ''); + $result = external_api::clean_returnvalue(raise_hand::execute_returns(), $result); + $this->assertFalse($result['status']); + } + + /** + * Test test_raise_hand user not enrolled. + */ + public function test_raise_hand_user_not_enrolled(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Setup scenario. + $scenario = $this->setup_scenario(); + + // Test not-enrolled user. + $usernotenrolled = self::getDataGenerator()->create_user(); + $this->setUser($usernotenrolled); + $this->expectException('moodle_exception'); + $manager = new venue_manager($scenario->contextblock, $scenario->task); + raise_hand::execute($scenario->contextblock->id, true); + $this->assertTrue($result['status']); + } + + /** + * Test test_raise_hand user student. + */ + public function test_raise_hand_user_student(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Setup scenario. + $scenario = $this->setup_scenario(); + + $this->setUser($scenario->student); + + $manager = new venue_manager($scenario->contextblock, $scenario->task); + + // Trigger and capture the event. + $sink = $this->redirectEvents(); + + $result = raise_hand::execute(true, ''); + $result = external_api::clean_returnvalue(raise_hand::execute_returns(), $result); + $this->assertTrue($result['status']); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = array_shift($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\block_deft\event\hand_raise_sent', $event); + $this->assertEquals($scenario->contextblock, $event->get_context()); + $venue = new \moodle_url('/blocks/deft/venue.php', ['task' => $scenario->task->get('id')]); + $this->assertEquals($venue, $event->get_url()); + $this->assertEventContextNotUsed($event); + $this->assertNotEmpty($event->get_name()); + + $data = $event->get_data(); + $this->assertEquals($scenario->student->id, $data['userid']); + } + + /** + * Test test_lower_hand user student. + */ + public function test_lower_hand_user_student(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Setup scenario. + $scenario = $this->setup_scenario(); + + $this->setUser($scenario->student); + + $manager = new venue_manager($scenario->contextblock, $scenario->task); + + // Trigger and capture the event. + $sink = $this->redirectEvents(); + + $result = raise_hand::execute(false, ''); + $result = external_api::clean_returnvalue(raise_hand::execute_returns(), $result); + $this->assertTrue($result['status']); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = array_shift($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\block_deft\event\hand_lower_sent', $event); + $this->assertEquals($scenario->contextblock, $event->get_context()); + $venue = new \moodle_url('/blocks/deft/venue.php', ['task' => $scenario->task->get('id')]); + $this->assertEquals($venue, $event->get_url()); + $this->assertEventContextNotUsed($event); + $this->assertNotEmpty($event->get_name()); + + $data = $event->get_data(); + $this->assertEquals($scenario->student->id, $data['userid']); + } + + /** + * Test test_raise_hand user missing capabilities. + */ + public function test_raise_hand_user_missing_capabilities(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Setup scenario. + $scenario = $this->setup_scenario(); + + $studentrole = $DB->get_record('role', ['shortname' => 'student']); + // Test user with no capabilities. + // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles. + assign_capability('block/deft:joinvenue', CAP_PROHIBIT, $studentrole->id, $scenario->contextcourse->id); + // Empty all the caches that may be affected by this change. + accesslib_clear_all_caches_for_unit_testing(); + course_modinfo::clear_instance_cache(); + + $this->setUser($scenario->student); + $manager = new venue_manager($scenario->contextblock, $scenario->task); + + $result = raise_hand::execute(true, ''); + $result = external_api::clean_returnvalue(raise_hand::execute_returns(), $result); + $this->assertFalse($result['status']); + + $result = raise_hand::execute(false, ''); + $result = external_api::clean_returnvalue(raise_hand::execute_returns(), $result); + $this->assertFalse($result['status']); + } + + /** + * Create a scenario to use into the tests. + * + * @return stdClass $scenario + */ + protected function setup_scenario() { + + $course = $this->getDataGenerator()->create_course(); + $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $contextcourse = \core\context\course::instance($course->id); + + $scenario = new stdClass(); + $scenario->contextcourse = $contextcourse; + $scenario->student = $student; + + /** @var \block_deft_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('block_deft'); + + $this->instance = $generator->create_instance(['parentcontextid' => $scenario->contextcourse->id]); + $scenario->contextblock = context_block::instance($this->instance->id); + $scenario->task = $generator->create_task($this->instance->id, [ + 'type' => 'venue', + 'visible' => 1, + ], [ + 'intro' => [ + 'text' => '', + 'format' => FORMAT_MOODLE, + ], + 'connection' => 'peer', + 'content' => '', + 'limit' => 10, + 'windowoption' => 'openinpopup', + ]); + + return $scenario; + } +} diff --git a/tests/generator/lib.php b/tests/generator/lib.php index cf6ead3..1ef98b5 100644 --- a/tests/generator/lib.php +++ b/tests/generator/lib.php @@ -17,7 +17,7 @@ use block_deft\task; /** - * Recently accessed courses block data generator class. + * Recently Deft response block data generator class. * * @package block_deft * @category test @@ -36,6 +36,8 @@ class block_deft_generator extends testing_block_generator { public function create_task(int $instanceid, $options = [], $data = []): task { global $USER; + $options = (object)$options; + $record = new stdClass(); $record->instance = $instanceid; $record->type = $options->type ?? 'text'; diff --git a/version.php b/version.php index d3a0821..6aaa815 100644 --- a/version.php +++ b/version.php @@ -25,8 +25,8 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'block_deft'; -$plugin->release = '1.2.5'; -$plugin->version = 2023042918; +$plugin->release = '1.2.6'; +$plugin->version = 2023042919; $plugin->requires = 2023042400; $plugin->maturity = MATURITY_STABLE; $plugin->dependencies = [