Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e591ddc4e8 | |||
| e1767fa80f | |||
| e25033e42d | |||
| dd2c19eb09 | |||
| 3b8b8d7d84 | |||
| 5378285a9a | |||
| 3a098250f1 | |||
| 148ddd26e7 | |||
| cdf432ee17 | |||
| b1e918edd8 | |||
| f58df6fa88 | |||
| eae562817d | |||
| 033a59f9c5 | |||
| a34a8636be | |||
| 08d2e7c136 | |||
| 886bbcbdb8 | |||
| 0c3e759d98 | |||
| 607a452a74 | |||
| 3ef6e04f3c | |||
| dfc7dcd062 | |||
| 80d773868c | |||
| eb127cc93b | |||
| 67f869a081 | |||
| a11a9ca5aa | |||
| 6aeecbdf46 |
@@ -0,0 +1,34 @@
|
||||
@tool_behat
|
||||
Feature: Confirm that we can open multiple browser tabs
|
||||
In order to use multiple browser tabs
|
||||
As a test writer
|
||||
I need the relevant Behat steps to work
|
||||
|
||||
@javascript @_switch_window
|
||||
Scenario: Open multiple browser tabs
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
| Course 2 | C2 |
|
||||
| Course 3 | C3 |
|
||||
And I am on the "C1" "Course" page logged in as "admin"
|
||||
|
||||
# Open a new tab on the same page.
|
||||
When I open a tab named "CourseViewer1" on the current page
|
||||
And I should see "Course 1" in the "h1" "css_element"
|
||||
And I am on the "C2" "Course" page
|
||||
|
||||
# Open new tab for specified page with identifier.
|
||||
And I open a tab named "CourseViewer2" on the "C3" "Course" page
|
||||
|
||||
# And for a specified page without identifier.
|
||||
And I open a tab named "CourseViewer4" on the "My courses" page
|
||||
|
||||
# Switch between all the tabs and confirm their different contents.
|
||||
Then I should see "No courses"
|
||||
And I switch to "CourseViewer2" tab
|
||||
And I should see "Course 3" in the "h1" "css_element"
|
||||
And I switch to "CourseViewer1" tab
|
||||
And I should see "Course 2" in the "h1" "css_element"
|
||||
And I switch to the main tab
|
||||
And I should see "Course 1" in the "h1" "css_element"
|
||||
@@ -0,0 +1,52 @@
|
||||
@core @core_badges
|
||||
Feature: Delete course badge already awarded
|
||||
As a teacher
|
||||
I can delete awarded course badge
|
||||
|
||||
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 |
|
||||
| Course 1 | C1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
|
||||
@javascript
|
||||
Scenario Outline: Delete course badge already awarded to student
|
||||
Given the following "core_badges > Badges" exist:
|
||||
| name | course | description | image | status | type |
|
||||
| <badgename> | C1 | <badgename> description | badges/tests/behat/badge.png | active | 2 |
|
||||
And the following "core_badges > Criterias" exist:
|
||||
| badge | role |
|
||||
| <badgename> | editingteacher |
|
||||
And the following "core_badges > Issued badges" exist:
|
||||
| badge | user |
|
||||
| <badgename> | student1 |
|
||||
When I am on the "Course 1" "enrolled users" page logged in as "teacher1"
|
||||
And I click on "Student 1" "link"
|
||||
Then I should see "<badgename>"
|
||||
And I am on the "Course 1" course page
|
||||
# Navigate to Manage Badges page in order to delete the badge
|
||||
And I navigate to "Badges > Manage badges" in current page administration
|
||||
# Delete the badge
|
||||
And I click on "Delete" "icon" in the "<badgename>" "table_row"
|
||||
And I press "<deleteoption>"
|
||||
And I am on the "Course 1" "enrolled users" page
|
||||
And I click on "Student 1" "link"
|
||||
# Confirm that Badge 1 is retained as awarded badge but Badge 2 is not
|
||||
And I <visibility> see "<badgename>"
|
||||
And I am on the "Course 1" course page
|
||||
# Navigate to Badges page to confirm that no badges exist, hence, Manage badges would not exist
|
||||
And I navigate to "Badges" in current page administration
|
||||
# Confirm that badges are sucessfully deleted
|
||||
And I should see "There are currently no badges available for users to earn."
|
||||
|
||||
Examples:
|
||||
| badgename | deleteoption | visibility |
|
||||
| Badge 1 | Delete and keep existing issued badges | should |
|
||||
| Badge 2 | Delete and remove existing issued badges | should not |
|
||||
@@ -25,11 +25,16 @@ Feature: Add a comment to the comments block
|
||||
Scenario: Add a comment with Javascript enabled
|
||||
When I add "I'm a comment from student1" comment to comments block
|
||||
Then I should see "I'm a comment from student1"
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I should see "I'm a comment from student1"
|
||||
|
||||
Scenario: Add a comment with Javascript disabled
|
||||
When I follow "Show comments"
|
||||
And I add "I'm a comment from student1" comment to comments block
|
||||
Then I should see "I'm a comment from student1"
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I follow "Show comments"
|
||||
And I should see "I'm a comment from student1"
|
||||
|
||||
@javascript
|
||||
Scenario: Test comment block pagination
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
@block @block_comments
|
||||
Feature: Enable Block comments on a course page and view comments
|
||||
In order to enable the comments block on a course page
|
||||
As a teacher
|
||||
I can add the comments block to the course page
|
||||
|
||||
Scenario: Add the comments block on the course page and add comments
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | Frist | teacher1@example.com |
|
||||
| student1 | Student | First | student1@example.com |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And the following "blocks" exist:
|
||||
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
|
||||
| comments | Course | C1 | course-view-* | side-pre |
|
||||
And I am on the "Course 1" course page logged in as teacher1
|
||||
And I follow "Show comments"
|
||||
And I add "I'm a comment from the teacher" comment to comments block
|
||||
When I am on the "Course 1" course page logged in as student1
|
||||
And I follow "Show comments"
|
||||
Then I should see "I'm a comment from the teacher"
|
||||
@@ -176,7 +176,23 @@ class behat_calendar extends behat_base {
|
||||
* @return void
|
||||
*/
|
||||
public function i_am_viewing_site_calendar() {
|
||||
$url = new moodle_url('/calendar/view.php', ['view' => 'month']);
|
||||
$this->i_am_viewing_calendar_in_view('month');
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a specific view in the calendar.
|
||||
*
|
||||
* @Given /^I am viewing calendar in "([^"]+)" view$/
|
||||
* @param string $view The calendar view ('month', 'day' and 'upcoming') to navigate to.
|
||||
* @return void
|
||||
*/
|
||||
public function i_am_viewing_calendar_in_view(string $view): void {
|
||||
|
||||
if (!in_array($view, ['month', 'day', 'upcoming'])) {
|
||||
throw new Exception("Invalid calendar view. Allowed values are: 'month', 'day' and 'upcoming'");
|
||||
}
|
||||
|
||||
$url = new moodle_url('/calendar/view.php', ['view' => $view]);
|
||||
$this->execute('behat_general::i_visit', [$url]);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -183,12 +183,7 @@ class graded_users_iterator {
|
||||
}
|
||||
}
|
||||
|
||||
$userfieldsapi = \core_user\fields::for_identity($coursecontext, false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
|
||||
// This need to be fixed - webservices in grade/report/user/classes/external/user.php don't check permission properly.
|
||||
$userfields .= ', u.idnumber, u.institution, u.department';
|
||||
|
||||
$userfields = 'u.*';
|
||||
$customfieldssql = '';
|
||||
if ($this->allowusercustomfields && !empty($CFG->grade_export_customprofilefields)) {
|
||||
$customfieldscount = 0;
|
||||
@@ -222,7 +217,8 @@ class graded_users_iterator {
|
||||
$this->users_rs = $DB->get_recordset_sql($users_sql, $params);
|
||||
|
||||
if (!$this->onlyactive) {
|
||||
$this->suspendedusers = get_suspended_userids($coursecontext);
|
||||
$context = context_course::instance($this->course->id);
|
||||
$this->suspendedusers = get_suspended_userids($context);
|
||||
} else {
|
||||
$this->suspendedusers = array();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'Lingua';
|
||||
$string['moodlelogo'] = 'Mercu Moodle';
|
||||
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
||||
define("core/str",["exports","jquery","core/ajax","core/localstorage"],(function(_exports,_jquery,_ajax,_localstorage){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
define("core/str",["exports","jquery","core/ajax","core/config","core/localstorage"],(function(_exports,_jquery,_ajax,_config,_localstorage){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* Fetch and return language strings.
|
||||
*
|
||||
@@ -7,6 +7,6 @@ define("core/str",["exports","jquery","core/ajax","core/localstorage"],(function
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 2.9
|
||||
*
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.get_strings=_exports.get_string=_exports.cache_strings=void 0,_jquery=_interopRequireDefault(_jquery),_ajax=_interopRequireDefault(_ajax),_localstorage=_interopRequireDefault(_localstorage);let promiseCache=[];_exports.get_string=(key,component,param,lang)=>get_strings([{key:key,component:component,param:param,lang:lang}]).then((results=>results[0]));const get_strings=requests=>{let requestData=[];const pageLang=(0,_jquery.default)("html").attr("lang").replace(/-/g,"_"),stringPromises=requests.map((request=>{let{component:component,key:key,param:param,lang:lang=pageLang}=request;component||(component="core");const cacheKey=(_ref=>{let{key:key,component:component,lang:lang=pageLang}=_ref;return"core_str/".concat(key,"/").concat(component,"/").concat(lang)})({key:key,component:component,lang:lang}),buildReturn=promise=>(promiseCache[cacheKey]=promise,promise);if(component in M.str&&key in M.str[component])return buildReturn(new Promise((resolve=>{resolve(M.util.get_string(key,component,param,lang))})));const cached=_localstorage.default.get(cacheKey);return cached?(M.str[component]={...M.str[component],[key]:cached},buildReturn(new Promise((resolve=>{resolve(M.util.get_string(key,component,param,lang))})))):cacheKey in promiseCache?buildReturn(promiseCache[cacheKey]).then((()=>M.util.get_string(key,component,param,lang))):buildReturn(new Promise(((resolve,reject)=>{requestData.push({methodname:"core_get_string",args:{stringid:key,stringparams:[],component:component,lang:lang},done:str=>{M.str[component]={...M.str[component],[key]:str},_localstorage.default.set(cacheKey,str),resolve(M.util.get_string(key,component,param,lang))},fail:reject})})))}));return requestData.length&&_ajax.default.call(requestData,!0,!1,!1,0,M.cfg.langrev),_jquery.default.when.apply(_jquery.default,stringPromises).then((function(){for(var _len=arguments.length,strings=new Array(_len),_key=0;_key<_len;_key++)strings[_key]=arguments[_key];return strings}))};_exports.get_strings=get_strings;_exports.cache_strings=strings=>{const defaultLang=(0,_jquery.default)("html").attr("lang").replace(/-/g,"_");strings.forEach((_ref2=>{let{key:key,component:component,value:value,lang:lang=defaultLang}=_ref2;const cacheKey=["core_str",key,component,lang].join("/");component in M.str&&key in M.str[component]||(component in M.str||(M.str[component]={}),M.str[component][key]=value),_localstorage.default.get(cacheKey)||_localstorage.default.set(cacheKey,value),cacheKey in promiseCache||(promiseCache[cacheKey]=_jquery.default.Deferred().resolve(value).promise())}))}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.get_strings=_exports.get_string=_exports.cache_strings=void 0,_jquery=_interopRequireDefault(_jquery),_ajax=_interopRequireDefault(_ajax),_config=_interopRequireDefault(_config),_localstorage=_interopRequireDefault(_localstorage);let promiseCache=[];_exports.get_string=(key,component,param,lang)=>get_strings([{key:key,component:component,param:param,lang:lang}]).then((results=>results[0]));const get_strings=requests=>{let requestData=[];const pageLang=_config.default.language,stringPromises=requests.map((request=>{let{component:component,key:key,param:param,lang:lang=pageLang}=request;component||(component="core");const cacheKey=(_ref=>{let{key:key,component:component,lang:lang=pageLang}=_ref;return"core_str/".concat(key,"/").concat(component,"/").concat(lang)})({key:key,component:component,lang:lang}),buildReturn=promise=>(promiseCache[cacheKey]=promise,promise);if(component in M.str&&key in M.str[component])return buildReturn(new Promise((resolve=>{resolve(M.util.get_string(key,component,param,lang))})));const cached=_localstorage.default.get(cacheKey);return cached?(M.str[component]={...M.str[component],[key]:cached},buildReturn(new Promise((resolve=>{resolve(M.util.get_string(key,component,param,lang))})))):cacheKey in promiseCache?buildReturn(promiseCache[cacheKey]).then((()=>M.util.get_string(key,component,param,lang))):buildReturn(new Promise(((resolve,reject)=>{requestData.push({methodname:"core_get_string",args:{stringid:key,stringparams:[],component:component,lang:lang},done:str=>{M.str[component]={...M.str[component],[key]:str},_localstorage.default.set(cacheKey,str),resolve(M.util.get_string(key,component,param,lang))},fail:reject})})))}));return requestData.length&&_ajax.default.call(requestData,!0,!1,!1,0,M.cfg.langrev),_jquery.default.when.apply(_jquery.default,stringPromises).then((function(){for(var _len=arguments.length,strings=new Array(_len),_key=0;_key<_len;_key++)strings[_key]=arguments[_key];return strings}))};_exports.get_strings=get_strings;_exports.cache_strings=strings=>{strings.forEach((_ref2=>{let{key:key,component:component,value:value,lang:lang=_config.default.language}=_ref2;const cacheKey=["core_str",key,component,lang].join("/");component in M.str&&key in M.str[component]||(component in M.str||(M.str[component]={}),M.str[component][key]=value),_localstorage.default.get(cacheKey)||_localstorage.default.set(cacheKey,value),cacheKey in promiseCache||(promiseCache[cacheKey]=_jquery.default.Deferred().resolve(value).promise())}))}}));
|
||||
|
||||
//# sourceMappingURL=str.min.js.map
|
||||
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -5,6 +5,6 @@
|
||||
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define("core/user_date",["jquery","core/ajax","core/sessionstorage","core/config"],(function($,Ajax,Storage,Config){var promisesCache={},getKey=function(request){return"core_user_date/"+$("html").attr("lang").replace(/-/g,"_")+"/"+Config.usertimezone+"/"+request.timestamp+"/"+request.format},loadDatesFromServer=function(dates){var args=dates.map((function(data){var fixDay=data.hasOwnProperty("fixday")?data.fixday:1,fixHour=data.hasOwnProperty("fixhour")?data.fixhour:1;return{timestamp:data.timestamp,format:data.format,type:data.type||null,fixday:fixDay,fixhour:fixHour}})),request={methodname:"core_get_user_dates",args:{contextid:Config.contextid,timestamps:args}};return Ajax.call([request],!0,!0)[0].then((function(results){results.dates.forEach((function(value,index){var date=dates[index];!function(key,value){Storage.set(key,value)}(getKey(date),value),date.deferred.resolve(value)}))})).catch((function(ex){dates.forEach((function(date){date.deferred.reject(ex)}))}))};return{get:function(requests){var ajaxRequests=[],promises=[];return requests.forEach((function(request){var key=getKey(request);if(function(key){return void 0!==promisesCache[key]}(key))promises.push(function(key){return promisesCache[key]}(key));else{var deferred=$.Deferred(),cached=function(key){return Storage.get(key)}(key);cached?deferred.resolve(cached):(request.deferred=deferred,ajaxRequests.push(request)),function(key,promise){promisesCache[key]=promise}(key,deferred.promise()),promises.push(deferred.promise())}})),ajaxRequests.length&&loadDatesFromServer(ajaxRequests),$.when.apply($,promises).then((function(){return 1===arguments.length?[arguments[0]]:Array.apply(null,arguments)}))},getUserMidnightForTimestamp:function(timestamp,todayMidnight){var future=timestamp>todayMidnight,diffSeconds=Math.abs(timestamp-todayMidnight),diffDaysInSeconds=86400*(future?Math.floor(diffSeconds/86400):Math.ceil(diffSeconds/86400));return future?todayMidnight+diffDaysInSeconds:todayMidnight-diffDaysInSeconds}}}));
|
||||
define("core/user_date",["jquery","core/ajax","core/sessionstorage","core/config"],(function($,Ajax,Storage,Config){var promisesCache={},getKey=function(request){return"core_user_date/"+Config.language+"/"+Config.usertimezone+"/"+request.timestamp+"/"+request.format},loadDatesFromServer=function(dates){var args=dates.map((function(data){var fixDay=data.hasOwnProperty("fixday")?data.fixday:1,fixHour=data.hasOwnProperty("fixhour")?data.fixhour:1;return{timestamp:data.timestamp,format:data.format,type:data.type||null,fixday:fixDay,fixhour:fixHour}})),request={methodname:"core_get_user_dates",args:{contextid:Config.contextid,timestamps:args}};return Ajax.call([request],!0,!0)[0].then((function(results){results.dates.forEach((function(value,index){var date=dates[index];!function(key,value){Storage.set(key,value)}(getKey(date),value),date.deferred.resolve(value)}))})).catch((function(ex){dates.forEach((function(date){date.deferred.reject(ex)}))}))};return{get:function(requests){var ajaxRequests=[],promises=[];return requests.forEach((function(request){var key=getKey(request);if(function(key){return void 0!==promisesCache[key]}(key))promises.push(function(key){return promisesCache[key]}(key));else{var deferred=$.Deferred(),cached=function(key){return Storage.get(key)}(key);cached?deferred.resolve(cached):(request.deferred=deferred,ajaxRequests.push(request)),function(key,promise){promisesCache[key]=promise}(key,deferred.promise()),promises.push(deferred.promise())}})),ajaxRequests.length&&loadDatesFromServer(ajaxRequests),$.when.apply($,promises).then((function(){return 1===arguments.length?[arguments[0]]:Array.apply(null,arguments)}))},getUserMidnightForTimestamp:function(timestamp,todayMidnight){var future=timestamp>todayMidnight,diffSeconds=Math.abs(timestamp-todayMidnight),diffDaysInSeconds=86400*(future?Math.floor(diffSeconds/86400):Math.ceil(diffSeconds/86400));return future?todayMidnight+diffDaysInSeconds:todayMidnight-diffDaysInSeconds}}}));
|
||||
|
||||
//# sourceMappingURL=user_date.min.js.map
|
||||
File diff suppressed because one or more lines are too long
+4
-5
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
import $ from 'jquery';
|
||||
import Ajax from 'core/ajax';
|
||||
import Config from 'core/config';
|
||||
import LocalStorage from 'core/localstorage';
|
||||
|
||||
// Module cache for the promises so that we don't make multiple
|
||||
@@ -101,7 +102,7 @@ export const get_string = (key, component, param, lang) => {
|
||||
// eslint-disable-next-line camelcase
|
||||
export const get_strings = (requests) => {
|
||||
let requestData = [];
|
||||
const pageLang = $('html').attr('lang').replace(/-/g, '_');
|
||||
const pageLang = Config.language;
|
||||
|
||||
// Helper function to construct the cache key.
|
||||
const getCacheKey = ({key, component, lang = pageLang}) => `core_str/${key}/${component}/${lang}`;
|
||||
@@ -191,13 +192,11 @@ export const get_strings = (requests) => {
|
||||
* @param {string} strings.key The string identifer to fetch
|
||||
* @param {string} strings.value The string value
|
||||
* @param {string} [strings.component='core'] The componet to fetch from
|
||||
* @param {string} [strings.lang] The language to fetch a string for. Defaults to current page language.
|
||||
* @param {string} [strings.lang=Config.language] The language to fetch a string for. Defaults to current page language.
|
||||
*/
|
||||
// eslint-disable-next-line camelcase
|
||||
export const cache_strings = (strings) => {
|
||||
const defaultLang = $('html').attr('lang').replace(/-/g, '_');
|
||||
|
||||
strings.forEach(({key, component, value, lang = defaultLang}) => {
|
||||
strings.forEach(({key, component, value, lang = Config.language}) => {
|
||||
const cacheKey = ['core_str', key, component, lang].join('/');
|
||||
|
||||
// Check M.str caching.
|
||||
|
||||
@@ -184,7 +184,7 @@ function(
|
||||
component: component,
|
||||
template: name,
|
||||
themename: theme,
|
||||
lang: $('html').attr('lang').replace(/-/g, '_')
|
||||
lang: config.language,
|
||||
}
|
||||
});
|
||||
// Remember the index in the requests list for this template so that
|
||||
|
||||
@@ -36,9 +36,8 @@ define(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'],
|
||||
* @return {string}
|
||||
*/
|
||||
var getKey = function(request) {
|
||||
var language = $('html').attr('lang').replace(/-/g, '_');
|
||||
return 'core_user_date/' +
|
||||
language + '/' +
|
||||
Config.language + '/' +
|
||||
Config.usertimezone + '/' +
|
||||
request.timestamp + '/' +
|
||||
request.format;
|
||||
|
||||
@@ -470,13 +470,7 @@ class behat_core_generator extends behat_generator_base {
|
||||
}
|
||||
}
|
||||
|
||||
// Custom exception.
|
||||
try {
|
||||
$this->datagenerator->create_module($activityname, $data, $cmoptions);
|
||||
} catch (coding_exception $e) {
|
||||
throw new Exception('\'' . $activityname . '\' activity can not be added using this step,' .
|
||||
' use the step \'I add a "ACTIVITY_OR_RESOURCE_NAME_STRING" to section "SECTION_NUMBER"\' instead');
|
||||
}
|
||||
$this->datagenerator->create_module($activityname, $data, $cmoptions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -335,6 +335,7 @@ class page_requirements_manager {
|
||||
'admin' => $CFG->admin,
|
||||
'svgicons' => $page->theme->use_svg_icons(),
|
||||
'usertimezone' => usertimezone(),
|
||||
'language' => current_language(),
|
||||
'courseId' => isset($courseid) ? (int) $courseid : 0,
|
||||
'courseContextId' => isset($coursecontext) ? $coursecontext->id : 0,
|
||||
'contextid' => $contextid,
|
||||
|
||||
@@ -200,7 +200,7 @@ class behat_general extends behat_base {
|
||||
/**
|
||||
* Switches to the specified window. Useful when interacting with popup windows.
|
||||
*
|
||||
* @Given /^I switch to "(?P<window_name_string>(?:[^"]|\\")*)" window$/
|
||||
* @Given /^I switch to "(?P<window_name_string>(?:[^"]|\\")*)" (window|tab)$/
|
||||
* @param string $windowname
|
||||
*/
|
||||
public function switch_to_window($windowname) {
|
||||
@@ -232,7 +232,7 @@ class behat_general extends behat_base {
|
||||
/**
|
||||
* Switches to the main Moodle window. Useful when you finish interacting with popup windows.
|
||||
*
|
||||
* @Given /^I switch to the main window$/
|
||||
* @Given /^I switch to the main (window|tab)$/
|
||||
*/
|
||||
public function switch_to_the_main_window() {
|
||||
$this->switch_to_window(self::MAIN_WINDOW_NAME);
|
||||
|
||||
@@ -909,6 +909,63 @@ class behat_navigation extends behat_base {
|
||||
throw new Exception('Unrecognised core page type "' . $type . '."');
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new tab with given name on the same URL as current page and switches to it.
|
||||
*
|
||||
* @param string $name Tab name that can be used for switching later (no whitespace)
|
||||
* @When /^I open a tab named "(?<name>[^"]*)" on the current page$/
|
||||
*/
|
||||
public function i_open_a_tab_on_the_current_page(string $name): void {
|
||||
$this->open_tab($name, 'location.href');
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new tab with given name on specified page, and switches to it.
|
||||
*
|
||||
* @param string $name Tab name that can be used for switching later (no whitespace)
|
||||
* @param string $page Page name
|
||||
* @When /^I open a tab named "(?<name>[^"]*)" on the "(?<page>[^"]*)" page$/
|
||||
*/
|
||||
public function i_open_a_tab_on_the_page(string $name, string $page): void {
|
||||
if ($page === 'current') {
|
||||
$jstarget = 'location.href';
|
||||
} else {
|
||||
$jstarget = '"' . addslashes_js($this->resolve_page_helper($page)->out(false)) . '"';
|
||||
}
|
||||
$this->open_tab($name, $jstarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new tab with given name (on specified page), and switches to it.
|
||||
*
|
||||
* @param string $name Tab name that can be used for switching later (no whitespace)
|
||||
* @param string $identifier Page identifier
|
||||
* @param string $page Page type
|
||||
* @When /^I open a tab named "(?<name>[^"]*)" on the "(?<identifier>[^"]*)" "(?<page>[^"]*)" page$/
|
||||
*/
|
||||
public function i_open_a_tab_on_the_page_instance(string $name, string $identifier, string $page): void {
|
||||
$this->open_tab($name, '"' . addslashes_js(
|
||||
$this->resolve_page_instance_helper($identifier, $page)->out(false)) . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new tab at the given target URL.
|
||||
*
|
||||
* @param string $name Name for tab
|
||||
* @param string $jstarget Target in JavaScript syntax, i.e. if a string, must be quoted
|
||||
*/
|
||||
protected function open_tab(string $name, string $jstarget): void {
|
||||
// Tab names aren't allowed spaces, and our JavaScript below doesn't do any escaping.
|
||||
if (clean_param($name, PARAM_ALPHANUMEXT) !== $name) {
|
||||
throw new Exception('Tab name may not contain whitespace or special characters: "' . $name . '"');
|
||||
}
|
||||
|
||||
// Normally you can't open a tab unless in response to a user action, but presumably Behat
|
||||
// is exempt from this restriction, because it works to just open it directly.
|
||||
$this->execute_script('window.open(' . $jstarget . ', "' . $name . '");');
|
||||
$this->execute('behat_general::switch_to_window', [$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the course homepage. (Consider using 'I am on the "shortname" "Course" page' step instead.)
|
||||
*
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
This files describes API changes in core libraries and APIs,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 4.1.8 ===
|
||||
|
||||
* The current page language is available in new `core/config` language property for Javascript modules
|
||||
|
||||
=== 4.1.7 ===
|
||||
* Add a new parameter to the debounce (core/utils) function allow it to create its own own Pending promise.
|
||||
(via options.pending). This is a backport of patch MDL-78779.
|
||||
|
||||
@@ -17,18 +17,19 @@ Feature: Chat calendar entries
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
|
||||
Scenario: Create a chat activity and do not publish the start date to the calendar
|
||||
Scenario Outline: Create a chat activity with repeated chat times set
|
||||
# Create an activity with repeated chat times
|
||||
Given the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | schedule |
|
||||
| chat | Test chat name | Test chat description | C1 | chat1 | 0 |
|
||||
| activity | course | name | schedule |
|
||||
| chat | C1 | Chat 1 | <scheduleset> |
|
||||
And I log in as "teacher1"
|
||||
When I follow "Calendar" in the user menu
|
||||
Then I should not see "Test chat name"
|
||||
# Confirm Chat activity visibility based on schedule
|
||||
When I am viewing calendar in "upcoming" view
|
||||
Then I <chatvisibility> see "Chat 1"
|
||||
|
||||
Scenario: Create a chat activity and publish the start date to the calendar
|
||||
Given the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | schedule |
|
||||
| chat | Test chat name | Test chat description | C1 | chat1 | 1 |
|
||||
And I log in as "teacher1"
|
||||
When I follow "Calendar" in the user menu
|
||||
Then I should see "Test chat name"
|
||||
Examples:
|
||||
| scheduleset | chatvisibility |
|
||||
| 0 | should not |
|
||||
| 1 | should |
|
||||
| 2 | should |
|
||||
| 3 | should |
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
@mod @mod_chat @javascript
|
||||
Feature: View past chat sessions
|
||||
In order for students to view past chat sessions
|
||||
As a teacher
|
||||
I need to be able to change the mod/chat:readlog permission for students
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | One | teacher1@example.com |
|
||||
| student1 | Student | One | student1@example.com |
|
||||
And the following "course" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
|
||||
Scenario Outline: View past chat sessions
|
||||
# Display or hide past chat session based on example data
|
||||
Given the following "activities" exist:
|
||||
| activity | course | name | studentlogs |
|
||||
| chat | C1 | Chat 1 | <studentlogvalue> |
|
||||
And I am on the "Chat 1" "chat activity" page logged in as teacher1
|
||||
# Display or hide past chat session by default based on mod/chat:readlog setting
|
||||
And the following "role capability" exists:
|
||||
| role | student |
|
||||
| mod/chat:readlog | <readpermission> |
|
||||
# Enter chat activity to create a session
|
||||
And I click on "Enter the chat" "link"
|
||||
# Close chat window
|
||||
When I close all opened windows
|
||||
And I reload the page
|
||||
# Confirm that past chat sessions is always visible for teacher
|
||||
Then I should see "Past sessions"
|
||||
# Confirm past chat sessions visibility for student based on settings
|
||||
And I am on the "Chat 1" "chat activity" page logged in as student1
|
||||
And I <sessionvisibility> see "Past sessions"
|
||||
|
||||
# Regardless of studentlogvalue value if readpermission is allowed, then Past sessions will be visible for students
|
||||
Examples:
|
||||
| studentlogvalue | readpermission | sessionvisibility |
|
||||
| 0 | allow | should |
|
||||
| 1 | allow | should |
|
||||
| 0 | prohibit | should not |
|
||||
| 1 | prohibit | should |
|
||||
@@ -0,0 +1,82 @@
|
||||
@mod @mod_lesson
|
||||
Feature: Retake lesson activity
|
||||
In order for student to retake a lesson activity
|
||||
As a teacher
|
||||
I should be able to allow retakes
|
||||
|
||||
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 |
|
||||
| Course 1 | C1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
# Generate lesson with retakes enabled, maxgrade = 100 and handling of re-takes = use mean
|
||||
And the following "activities" exist:
|
||||
| activity | name | course | retake | grade[modgrade_point] | usemaxgrade |
|
||||
| lesson | Test lesson name | C1 | 1 | 100 | 0 |
|
||||
# Generate question pages
|
||||
And the following "mod_lesson > page" exist:
|
||||
| lesson | qtype | title | content |
|
||||
| Test lesson name | multichoice | Question 1 | Which is not a plant? |
|
||||
| Test lesson name | multichoice | Question 2 | Which is a plant? |
|
||||
| Test lesson name | multichoice | Question 3 | Which is a plant and a colour? |
|
||||
# Generate question answers
|
||||
And the following "mod_lesson > answers" exist:
|
||||
| page | answer | jumpto | score |
|
||||
| Question 1 | Brown | Next page | 1 |
|
||||
| Question 1 | Lavender | Next page | 0 |
|
||||
| Question 2 | Brown | Next page | 0 |
|
||||
| Question 2 | Lavender | Next page | 1 |
|
||||
| Question 3 | Brown | Next page | 0 |
|
||||
| Question 3 | Lavender | Next page | 1 |
|
||||
|
||||
Scenario: A student can retake a lesson
|
||||
# First attempt - all correct
|
||||
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
|
||||
And I set the following fields to these values:
|
||||
| Brown | 1 |
|
||||
And I press "Submit"
|
||||
And I set the following fields to these values:
|
||||
| Lavender | 1 |
|
||||
And I press "Submit"
|
||||
And I set the following fields to these values:
|
||||
| Lavender | 1 |
|
||||
And I press "Submit"
|
||||
# Confirm that lesson can be retaken
|
||||
When I am on the "Test lesson name" "lesson activity" page
|
||||
Then I should see "Which is not a plant?"
|
||||
# Second attempt - only 1 correct
|
||||
And I set the following fields to these values:
|
||||
| Lavender | 1 |
|
||||
And I press "Submit"
|
||||
And I set the following fields to these values:
|
||||
| Brown | 1 |
|
||||
And I press "Submit"
|
||||
And I set the following fields to these values:
|
||||
| Lavender | 1 |
|
||||
And I press "Submit"
|
||||
# Check that grade is 66.67 (mean of the 2 attempts)
|
||||
And I click on "View grades" "link"
|
||||
And "Test lesson name" row "Grade" column of "generaltable" table should contain "66.67"
|
||||
# Change handling of re-takes = use maximum
|
||||
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
|
||||
And I set the following fields to these values:
|
||||
| Handling of re-takes | Use maximum |
|
||||
And I press "Save and display"
|
||||
# Confirm that lesson grade is the maximum of the 2 attempts (100)
|
||||
And I am on the "Course 1" "grades > user > View" page logged in as student1
|
||||
And "Test lesson name" row "Grade" column of "generaltable" table should contain "100.00"
|
||||
# Disable lesson retake
|
||||
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
|
||||
And I set the following fields to these values:
|
||||
| Re-takes allowed | No |
|
||||
And I press "Save and display"
|
||||
# Confirm lesson cannot be retaken
|
||||
And I am on the "Test lesson name" "lesson activity" page logged in as student1
|
||||
And I should see "You are not allowed to retake this lesson."
|
||||
@@ -16,51 +16,76 @@ Feature: In a lesson activity, students can review the answers they gave to ques
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And the following "activity" exist:
|
||||
| activity | name | course | idnumber |
|
||||
| lesson | Test lesson name | C1 | lesson1 |
|
||||
And the following "activity" exists:
|
||||
| activity | lesson |
|
||||
| name | Test lesson name |
|
||||
| course | C1 |
|
||||
| idnumber | lesson1 |
|
||||
# Display ongoing score = Yes
|
||||
| ongoing | 1 |
|
||||
# Slideshow = Yes
|
||||
| slideshow | 1 |
|
||||
# Maximum number of answers = 10
|
||||
| maxanswers | 10 |
|
||||
# Allow student review = Yes
|
||||
| modattempts | 1 |
|
||||
# Maximum number of attempts per question
|
||||
| maxattempts | 3 |
|
||||
# Custom scoring = No
|
||||
| custom | 0 |
|
||||
# Re-takes allowed = Yes
|
||||
| retake | 1 |
|
||||
And the following "mod_lesson > pages" exist:
|
||||
| lesson | qtype | title | content |
|
||||
| Test lesson name | numeric | Hardest question ever | 1 + 1? |
|
||||
| Test lesson name | truefalse | Next question | Paper is made from trees. |
|
||||
And the following "mod_lesson > answers" exist:
|
||||
| page | answer | response | jumpto | score |
|
||||
| Hardest question ever | 2 | Correct answer | Next page | 1 |
|
||||
| Hardest question ever | 1 | Incorrect answer | This page | 0 |
|
||||
| Next question | True | Correct | Next page | 1 |
|
||||
| Next question | False | Wrong | This page | 0 |
|
||||
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
|
||||
And I set the following fields to these values:
|
||||
| Display ongoing score | Yes |
|
||||
| Slideshow | Yes |
|
||||
| Maximum number of answers | 10 |
|
||||
| Allow student review | Yes |
|
||||
| Maximum number of attempts per question | 3 |
|
||||
| Custom scoring | No |
|
||||
| Re-takes allowed | Yes |
|
||||
And I press "Save and display"
|
||||
| page | answer | response | jumpto | score |
|
||||
| Hardest question ever | 2 | Correct answer 1 | Next page | 1 |
|
||||
| Hardest question ever | 1 | Incorrect answer 1 | This page | 0 |
|
||||
| Next question | True | Correct answer 2 | Next page | 1 |
|
||||
| Next question | False | Incorrect answer 2 | This page | 0 |
|
||||
And I am on the "Test lesson name" "lesson activity" page logged in as student1
|
||||
And I should see "You have answered 0 correctly out of 0 attempts."
|
||||
And I should see "1 + 1?"
|
||||
And I wait "1" seconds
|
||||
And I set the following fields to these values:
|
||||
| Your answer | 1 |
|
||||
And I press "Submit"
|
||||
And I should see "You have answered 0 correctly out of 1 attempts."
|
||||
And I press "Continue"
|
||||
And I should see "1 + 1?"
|
||||
And I wait "1" seconds
|
||||
And I set the following fields to these values:
|
||||
| Your answer | 2 |
|
||||
And I press "Submit"
|
||||
And I should see "You have answered 1 correctly out of 2 attempts."
|
||||
And I press "Continue"
|
||||
And I should see "Paper is made from trees."
|
||||
And I wait "1" seconds
|
||||
And I set the following fields to these values:
|
||||
| True | 1 |
|
||||
And I press "Submit"
|
||||
And I should see "You have answered 2 correctly out of 3 attempts."
|
||||
And I should see "Paper is made from trees."
|
||||
And I press "Continue"
|
||||
And I should see "Congratulations - end of lesson reached"
|
||||
And I should see "Number of questions answered: 2"
|
||||
And I should see "Number of correct answers: 2"
|
||||
And I should see "Your score is 2 (out of 3)."
|
||||
When I follow "Review lesson"
|
||||
Then I should see "You have answered 2 correctly out of 3 attempts."
|
||||
And I should see "1 + 1?"
|
||||
And I press "Next page"
|
||||
And I should see "You have answered 2 correctly out of 3 attempts."
|
||||
And I should see "1 + 1?"
|
||||
And I should see "Correct answer 1"
|
||||
And I press "Continue"
|
||||
And I should see "You have answered 2 correctly out of 3 attempts."
|
||||
And I should see "Paper is made from trees."
|
||||
And I press "Next page"
|
||||
And I should see "You have answered 2 correctly out of 3 attempts."
|
||||
And I should see "Paper is made from trees."
|
||||
And I should see "Correct answer 2"
|
||||
And I press "Continue"
|
||||
And I should see "Congratulations - end of lesson reached"
|
||||
|
||||
+2
-2
@@ -29,9 +29,9 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2022112807.00; // 20221128 = branching date YYYYMMDD - do not modify!
|
||||
$version = 2022112808.00; // 20221128 = branching date YYYYMMDD - do not modify!
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '4.1.7 (Build: 20231211)'; // Human-friendly version name
|
||||
$release = '4.1.8 (Build: 20231222)'; // Human-friendly version name
|
||||
$branch = '401'; // This version's branch.
|
||||
$maturity = MATURITY_STABLE; // This version's maturity level.
|
||||
|
||||
Reference in New Issue
Block a user