diff --git a/src/main/java/org/olat/modules/grading/ui/GradingAssignmentsListController.java b/src/main/java/org/olat/modules/grading/ui/GradingAssignmentsListController.java index 8033bc373f87a9fd8cef128a39c68ada84585319..2d94bf8b4372b14ad7d251d57154628feb0c01ca 100644 --- a/src/main/java/org/olat/modules/grading/ui/GradingAssignmentsListController.java +++ b/src/main/java/org/olat/modules/grading/ui/GradingAssignmentsListController.java @@ -325,6 +325,7 @@ public class GradingAssignmentsListController extends FormBasicController implem userPropertyHandlers, assessedUserPropertyHandlers, getTranslator(), getLocale()); tableEl = uifactory.addTableElement(getWindowControl(), "assignments", tableModel, 24, false, getTranslator(), formLayout); tableEl.setEmptyTableSettings("table.assignments.empty", true); + tableEl.setElementCssClass("o_sel_grading_assignments_list"); tableEl.setExportEnabled(true); String id = "grading-assignments-list-" + (testEntry == null ? "coaching" : testEntry.getKey()); tableEl.setAndLoadPersistedPreferences(ureq, id); diff --git a/src/test/java/org/olat/selenium/ImsQTI21Test.java b/src/test/java/org/olat/selenium/ImsQTI21Test.java index 836520906f27835520003621112807450b904472..d05353f1902537f663a6ee91b2517c1770cff98d 100644 --- a/src/test/java/org/olat/selenium/ImsQTI21Test.java +++ b/src/test/java/org/olat/selenium/ImsQTI21Test.java @@ -30,7 +30,6 @@ import org.jboss.arquillian.drone.api.annotation.Drone; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.olat.ims.qti21.QTI21AssessmentResultsOptions; @@ -1156,7 +1155,7 @@ public class ImsQTI21Test extends Deployments { .assertOnCourseAssessmentTestScore(2); } - + /** * An author create a test with essay and single choice, configures it * with grading, add a grader then makes a course. A user play the test, @@ -1169,9 +1168,9 @@ public class ImsQTI21Test extends Deployments { * @throws URISyntaxException */ @Test - @Ignore @RunAsClient - public void qti21CourseTestGradingWorkflow(@Drone @Participant WebDriver participantBrowser, @Drone @User WebDriver graderBrowser) + public void qti21CourseTestGradingWorkflow(@Drone @Participant WebDriver participantBrowser, + @Drone @User WebDriver graderBrowser) throws IOException, URISyntaxException { UserVO author = new UserRestClient(deploymentUrl).createRandomAuthor(); @@ -1337,6 +1336,196 @@ public class ImsQTI21Test extends Deployments { .assertOnCourseAssessmentTestPassed(); } + + /** + * An author create a test with essay and single choice, configures it + * with grading, forget to add the grader, then makes a course. A user + * play the test, the author add a grader to the assignment and the grader + * correct it, the author set the test as passed and free the results. + * The user reloads the results to see passed! + * + * @param participantBrowser The participant browser + * @param graderBrowser The grader browser + * @throws IOException + * @throws URISyntaxException + */ + @Test + @RunAsClient + public void qti21CourseTestGradingAfterwardsAssignment(@Drone @Participant WebDriver participantBrowser, + @Drone @User WebDriver graderBrowser) + throws IOException, URISyntaxException { + + UserVO author = new UserRestClient(deploymentUrl).createRandomAuthor(); + UserVO participant = new UserRestClient(deploymentUrl).createRandomUser("Hakufu"); + UserVO grader = new UserRestClient(deploymentUrl).createRandomUser("Hakufu"); + + LoginPage loginPage = LoginPage.load(browser, deploymentUrl); + loginPage.loginAs(author.getLogin(), author.getPassword()); + + //upload a test and prepare the grading configuration + String qtiTestTitle = "Correction 2.1 " + UUID.randomUUID(); + URL qtiTestUrl = JunitTestHelper.class.getResource("file_resources/qti21/test_sc_essay_mc.zip"); + File qtiTestFile = new File(qtiTestUrl.toURI()); + NavigationPage navBar = NavigationPage.load(browser); + navBar + .openAuthoringEnvironment() + .uploadResource(qtiTestTitle, qtiTestFile) + .clickToolbarRootCrumb(); + + QTI21Page qtiPage = QTI21Page + .getQTI21Page(browser); + QTI21GradingPage gradingPage = qtiPage + .grading(); + gradingPage + .settings() + .enable() + .setPeriods(9, 5, 7) + .selectMailTemplate() + .save(); + + qtiPage + .clickToolbarBack(); + + //create a course + String courseTitle = "Grading QTI 2.1 " + UUID.randomUUID(); + navBar + .openAuthoringEnvironment() + .createCourse(courseTitle) + .clickToolbarBack(); + + String testNodeTitle = "QTI21Grading-1"; + + //create a course element of type CP with the CP that we create above + CourseEditorPageFragment courseEditor = CoursePageFragment.getCourse(browser) + .edit(); + courseEditor + .createNode("iqtest") + .nodeTitle(testNodeTitle) + .selectTabLearnContent() + .chooseTest(qtiTestTitle); + OOGraphene.closeWarningBox(browser);//close the warning + + QTI21ConfigurationCEPage configPage = new QTI21ConfigurationCEPage(browser); + configPage + .selectConfiguration() + .setCorrectionMode("grading") + .saveConfiguration(); + + courseEditor + .autoPublish() + .settings() + .accessConfiguration() + .setUserAccess(UserAccess.membersOnly) + .save(); + + //add a participant + CoursePageFragment courseRuntime = courseEditor + .clickToolbarBack(); + courseRuntime + .publish() + .members() + .quickAdd(participant); + + //a user search the course and make the test + LoginPage userLoginPage = LoginPage.load(participantBrowser, deploymentUrl); + userLoginPage + .loginAs(participant.getLogin(), participant.getPassword()) + .resume(); + NavigationPage userNavBar = NavigationPage.load(participantBrowser); + userNavBar + .openMyCourses() + .openSearch() + .extendedSearch(courseTitle) + .select(courseTitle); + + // open the course and see the test + CoursePageFragment course = CoursePageFragment.getCourse(participantBrowser); + course + .clickTree() + .selectWithTitle(testNodeTitle); + QTI21Page participantQtiPage = QTI21Page + .getQTI21Page(participantBrowser); + participantQtiPage + .assertOnStart() + .start() + .assertOnAssessmentItem() + .answerSingleChoiceWithParagraph("Correct answer") + .saveAnswer() + .assertOnAssessmentItem("Essay") + .answerEssay("Bla bla bla") + .saveAnswer() + .answerMultipleChoice("Good choice", "Bad choice") + .saveAnswer() + .endTest() + .assertOnCourseAssessmentTestWaitingCorrection(); + + // author returns to test and assign the test to a new grader + navBar + .openAuthoringEnvironment() + .selectResource(qtiTestTitle); + + gradingPage = qtiPage + .grading(); + gradingPage + .assignments() + .assertAssignmentUnassigned(testNodeTitle) + .openAssignmentUnassignedTool(testNodeTitle) + .addGrader(grader); + + // grader assignment + LoginPage graderLoginPage = LoginPage.load(graderBrowser, deploymentUrl); + graderLoginPage + .loginAs(grader.getLogin(), grader.getPassword()) + .resume(); + + NavigationPage graderNavBar = NavigationPage.load(graderBrowser); + graderNavBar + .openCoaching() + .assertOnGrading() + .startGrading(testNodeTitle) + .assertOnAssessmentItemNotCorrected("Essay", 0) + .selectAssessmentItem("Essay") + .setScore("1.0") + .save() + .assertOnStatusOk() + .back() + .publish() + .confirmDialog(); + + // author check the assignment + navBar + .openAuthoringEnvironment() + .selectResource(qtiTestTitle); + qtiPage + .grading() + .graders() + .assertGraderAssignmentsDone(grader, 1); + // go in assessment tool to free the results + navBar + .openAuthoringEnvironment() + .selectResource(courseTitle); + + courseRuntime + .assessmentTool() + .users() + .assertOnUsers(participant) + .selectUser(participant) + .selectUsersCourseNode(testNodeTitle) + .reopenAssessment() + .setAssessmentPassed(Boolean.TRUE) + .setAssessmentVisibility(true) + .closeAssessment() + .assertUserPassedCourseNode(testNodeTitle); + + // participant checks its result + course + .clickTree() + .selectWithTitle(testNodeTitle); + participantQtiPage + .assertOnCourseAssessmentTestScore(2) + .assertOnCourseAssessmentTestPassed(); + } + /** * An author create a course with a course element * of type self test. It add a participant. The diff --git a/src/test/java/org/olat/selenium/page/group/MembersWizardPage.java b/src/test/java/org/olat/selenium/page/group/MembersWizardPage.java index 1e8035582fefa887bd1f7c7628d53be39e032ad3..28b94b238b83350dc8880605d5330f8801eed479 100644 --- a/src/test/java/org/olat/selenium/page/group/MembersWizardPage.java +++ b/src/test/java/org/olat/selenium/page/group/MembersWizardPage.java @@ -75,11 +75,49 @@ public class MembersWizardPage { } /** - * Search member and select them - * @param user - * @return + * Search member and select them, flow to select several + * identities at once. + * + * @param user The user to search for + * @param admin If administrator search with user name + * @return Itself */ public MembersWizardPage searchMember(UserVO user, boolean admin) { + searchMemberForm(user, admin); + + // select all + By selectAll = By.xpath("//div[contains(@class,'modal')]//div[contains(@class,'o_table_checkall')]/a[i[contains(@class,'o_icon_check_on')]]"); + OOGraphene.waitElement(selectAll, browser); + if(browser instanceof FirefoxDriver) { + OOGraphene.waitingALittleLonger();// link is obscured by the scroll bar + } + browser.findElement(selectAll).click(); + OOGraphene.waitBusy(browser); + return this; + } + + /** + * Search and select a member, flow to select a single identity. + * + * @param user The user to search for + * @param admin If administrator search with user name + * @return Itself + */ + public MembersWizardPage searchOneMember(UserVO user, boolean admin) { + searchMemberForm(user, admin); + + // select the identity + By selectAll = By.xpath("//div[contains(@class,'modal')]//div[contains(@class,'o_table_flexi')]/table//tr[td[text()[contains(.,'" + user.getFirstName() + "')]]]/td/a[contains(@onclick,'select')]"); + OOGraphene.waitElement(selectAll, browser); + if(browser instanceof FirefoxDriver) { + OOGraphene.waitingALittleLonger();// link is obscured by the scroll bar + } + browser.findElement(selectAll).click(); + OOGraphene.waitBusy(browser); + return this; + } + + private MembersWizardPage searchMemberForm(UserVO user, boolean admin) { //Search by username By usernameBy = By.cssSelector(".o_sel_usersearch_searchform input[type='text']"); OOGraphene.waitElement(usernameBy, browser); @@ -96,18 +134,10 @@ public class MembersWizardPage { OOGraphene.takeScreenshot("Search member", browser); throw e; } - - // select all - By selectAll = By.xpath("//div[contains(@class,'modal')]//div[contains(@class,'o_table_checkall')]/a[i[contains(@class,'o_icon_check_on')]]"); - OOGraphene.waitElement(selectAll, browser); - if(browser instanceof FirefoxDriver) { - OOGraphene.waitingALittleLonger();// link is obscured by the scroll bar - } - browser.findElement(selectAll).click(); - OOGraphene.waitBusy(browser); return this; } + public MembersWizardPage setMembers(UserVO... users) { StringBuilder sb = new StringBuilder(); for(UserVO user:users) { diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21GradingAssignmentsPage.java b/src/test/java/org/olat/selenium/page/qti/QTI21GradingAssignmentsPage.java new file mode 100644 index 0000000000000000000000000000000000000000..06deec97ce9fb1fcaacb0cdd8e5fb57d5567bd4e --- /dev/null +++ b/src/test/java/org/olat/selenium/page/qti/QTI21GradingAssignmentsPage.java @@ -0,0 +1,74 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.selenium.page.qti; + +import org.olat.selenium.page.graphene.OOGraphene; +import org.olat.selenium.page.group.MembersWizardPage; +import org.olat.user.restapi.UserVO; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; + +/** + * + * Initial date: 12 juin 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class QTI21GradingAssignmentsPage { + + private final WebDriver browser; + + public QTI21GradingAssignmentsPage(WebDriver browser) { + this.browser = browser; + } + + public QTI21GradingAssignmentsPage assertAssignmentUnassigned(String testNodeTitle) { + By graderBy = By.xpath("//div[contains(@class,'o_sel_grading_assignments_list')]//table//tr[td/span/i[contains(@class,'o_grad_assignment_unassigned')]]/td/a[text()[contains(.,'" + testNodeTitle + "')]]"); + OOGraphene.waitElement(graderBy, browser); + return this; + } + + public QTI21GradingAssignmentsPage openAssignmentUnassignedTool(String testNodeTitle) { + By toolBy = By.xpath("//div[contains(@class,'o_sel_grading_assignments_list')]//table//tr[td/span/i[contains(@class,'o_grad_assignment_unassigned')]][td/a[text()[contains(.,'" + testNodeTitle + "')]]]/td/a[i[contains(@class,'o_icon_actions')]]"); + OOGraphene.waitElement(toolBy, browser); + OOGraphene.scrollTo(toolBy, browser); + browser.findElement(toolBy).click(); + OOGraphene.waitBusy(browser); + OOGraphene.waitCallout(browser); + return this; + } + + public QTI21GradingAssignmentsPage addGrader(UserVO user) { + By graderBy = By.xpath("//ul[contains(@class,'o_dropdown')]/li/a[contains(@onclick,'assign_grader')]"); + OOGraphene.waitElement(graderBy, browser); + browser.findElement(graderBy).click(); + OOGraphene.waitBusy(browser); + OOGraphene.waitModalWizard(browser); + + MembersWizardPage wizard = new MembersWizardPage(browser) + .searchOneMember(user, true); + + OOGraphene.nextStep(browser); + wizard.finish(); + return this; + } + + +} diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21GradingGradersPage.java b/src/test/java/org/olat/selenium/page/qti/QTI21GradingGradersPage.java index 958e035a49bb0512aa9fdded4ac3f7b789182659..d4240ed8c7e46a047add0b0b6f331a1d2668ebe4 100644 --- a/src/test/java/org/olat/selenium/page/qti/QTI21GradingGradersPage.java +++ b/src/test/java/org/olat/selenium/page/qti/QTI21GradingGradersPage.java @@ -43,6 +43,8 @@ public class QTI21GradingGradersPage { By addGraderBy = By.cssSelector("div.o_button_group a.o_sel_repo_grading_add_graders"); OOGraphene.waitElement(addGraderBy, browser); browser.findElement(addGraderBy).click(); + OOGraphene.waitBusy(browser); + OOGraphene.waitModalWizard(browser); MembersWizardPage wizard = new MembersWizardPage(browser) .searchMember(user, true); @@ -74,6 +76,5 @@ public class QTI21GradingGradersPage { OOGraphene.waitElement(graderBy, browser); return this; } - } diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21GradingPage.java b/src/test/java/org/olat/selenium/page/qti/QTI21GradingPage.java index 034b712d112573e81ebf5069c36069ae056dfd6c..1fcec4a6375abb0c1191c78c15b0655450033d20 100644 --- a/src/test/java/org/olat/selenium/page/qti/QTI21GradingPage.java +++ b/src/test/java/org/olat/selenium/page/qti/QTI21GradingPage.java @@ -47,6 +47,11 @@ public class QTI21GradingPage { return new QTI21GradingGradersPage(browser); } + public QTI21GradingAssignmentsPage assignments() { + selectSegment("repository.assignments"); + return new QTI21GradingAssignmentsPage(browser); + } + private void selectSegment(String action) { By segmentBy = By.xpath("//div[contains(@class,'o_segments')]/a[contains(@onclick,'" + action + "')]"); OOGraphene.waitElement(segmentBy, browser);