diff --git a/src/main/java/org/olat/course/certificate/ui/CertificateAndEfficiencyStatementController.java b/src/main/java/org/olat/course/certificate/ui/CertificateAndEfficiencyStatementController.java index 784f7425cc664b5a004f570f5a28653353aa2a17..262bfe2feb60df33a305bbbd9d06572119d935a5 100644 --- a/src/main/java/org/olat/course/certificate/ui/CertificateAndEfficiencyStatementController.java +++ b/src/main/java/org/olat/course/certificate/ui/CertificateAndEfficiencyStatementController.java @@ -171,10 +171,12 @@ public class CertificateAndEfficiencyStatementController extends BasicController if(efficiencyStatement != null && certificate != null) { segmentView = SegmentViewFactory.createSegmentView("segments", mainVC, this); certificateLink = LinkFactory.createLink("details.certificate", mainVC, this); + certificateLink.setElementCssClass("o_select_certificate_segement"); segmentView.addSegment(certificateLink, true); selectCertificate(ureq); courseDetailsLink = LinkFactory.createLink("details.course.infos", mainVC, this); + courseDetailsLink.setElementCssClass("o_select_statement_segment"); segmentView.addSegment(courseDetailsLink, false); } else if(efficiencyStatement != null) { selectCourseInfos(ureq); diff --git a/src/main/java/org/olat/course/nodes/st/EditScoreCalculationEasyForm.java b/src/main/java/org/olat/course/nodes/st/EditScoreCalculationEasyForm.java index 1aa81717dc949911dd734ce348560fc70752ffe4..f1c2ba2f50645aa6e395a9c51e528371a0a9f48d 100644 --- a/src/main/java/org/olat/course/nodes/st/EditScoreCalculationEasyForm.java +++ b/src/main/java/org/olat/course/nodes/st/EditScoreCalculationEasyForm.java @@ -284,6 +284,7 @@ public class EditScoreCalculationEasyForm extends FormBasicController { hasScore = uifactory.addCheckboxesHorizontal("scform.hasScore", formLayout, new String[]{"xx"}, new String[]{null}); hasScore.select("xx", sc != null && sc.getSumOfScoreNodes() != null && sc.getSumOfScoreNodes().size() > 0); hasScore.addActionListener(FormEvent.ONCLICK); // Radios/Checkboxes need onclick because of IE bug OLAT-5753 + hasScore.setElementCssClass("o_sel_has_score"); scoreNodeIdents = initNodeSelectionElement( formLayout, "scform.scoreNodeIndents", sc, (sc == null ? null : sc.getSumOfScoreNodes()), nodeIdentList @@ -295,6 +296,7 @@ public class EditScoreCalculationEasyForm extends FormBasicController { hasPassed = uifactory.addCheckboxesHorizontal("scform.passedtype", formLayout, new String[]{"xx"}, new String[]{null}); hasPassed.select("xx", sc != null && sc.getPassedType() != null && !sc.getPassedType().equals(ScoreCalculator.PASSED_TYPE_NONE)); hasPassed.addActionListener(FormEvent.ONCLICK); // Radios/Checkboxes need onclick because of IE bug OLAT-5753 + hasPassed.setElementCssClass("o_sel_has_passed"); String[] passedTypeKeys = new String[] { ScoreCalculator.PASSED_TYPE_CUTVALUE, diff --git a/src/main/java/org/olat/course/nodes/st/_content/scoreedit.html b/src/main/java/org/olat/course/nodes/st/_content/scoreedit.html index 76d154c1cada4fa7a65ee721607ee73b35cb038b..491953832d65b873110f0ef03d7ae9647d51a677 100644 --- a/src/main/java/org/olat/course/nodes/st/_content/scoreedit.html +++ b/src/main/java/org/olat/course/nodes/st/_content/scoreedit.html @@ -1,4 +1,4 @@ -<fieldset> +<fieldset class="o_sel_structure_score"> <legend>$r.contextHelpWithWrapper("org.olat.course.nodes.st","ced-st-score.html","help.st") $r.translate("score.fieldset.title")</legend> diff --git a/src/test/java/org/olat/selenium/AssessmentTest.java b/src/test/java/org/olat/selenium/AssessmentTest.java index 7e574aeb7217bc87efa3ea0ed97b1eff640d966c..402525721a5c76555a758d6a02750d94ee194c68 100644 --- a/src/test/java/org/olat/selenium/AssessmentTest.java +++ b/src/test/java/org/olat/selenium/AssessmentTest.java @@ -57,6 +57,7 @@ import org.olat.selenium.page.repository.RepositoryAccessPage.UserAccess; import org.olat.selenium.page.user.UserToolsPage; import org.olat.test.ArquillianDeployments; import org.olat.test.JunitTestHelper; +import org.olat.test.rest.RepositoryRestClient; import org.olat.test.rest.UserRestClient; import org.olat.user.restapi.UserVO; import org.openqa.selenium.By; @@ -656,5 +657,102 @@ public class AssessmentTest { .assertOnEfficiencyStatmentPage() .assertOnCertificate(courseTitle); } + + /** + * An author create a course, set up the root node to make efficiency statement, + * add a test, publish it and add a participant. It set the certificate.<br> + * + * The participant logs in, make the test and look at its wonderful certificate + * and the details of its performance. + * + * @param authorLoginPage + * @param reiBrowser + * @throws IOException + * @throws URISyntaxException + */ + @Test + @RunAsClient + public void certificatesGeneratedByTest(@InitialPage LoginPage authorLoginPage, + @Drone @User WebDriver reiBrowser) + throws IOException, URISyntaxException { + //create an author and a participant + UserVO author = new UserRestClient(deploymentUrl).createAuthor(); + UserVO rei = new UserRestClient(deploymentUrl).createRandomUser("Rei"); + //deploy the test + URL testUrl = ArquillianDeployments.class.getResource("file_resources/e4_test.zip"); + String testTitle = "E4Test-" + UUID.randomUUID(); + new RepositoryRestClient(deploymentUrl, author).deployResource(new File(testUrl.toURI()), "-", testTitle); + + authorLoginPage.loginAs(author.getLogin(), author.getPassword()); + //create a course + String courseTitle = "Certif-" + UUID.randomUUID(); + navBar + .openAuthoringEnvironment() + .createCourse(courseTitle) + .clickToolbarBack(); + + //create a course element of type CP with the CP that we create above + String testNodeTitle = "Test-QTI-1.2"; + CoursePageFragment courseRuntime = CoursePageFragment.getCourse(browser); + courseRuntime + .edit() + .createNode("iqtest") + .nodeTitle(testNodeTitle) + .selectTabLearnContent() + .chooseTest(testTitle) + .selectRoot() + .selectTabScore() + .enableRootScoreByNodes() + .autoPublish() + .accessConfiguration() + .setUserAccess(UserAccess.registred) + .clickToolbarBack(); + + //add a participant to the course + MembersPage members = courseRuntime + .members(); + members + .addMember() + .searchMember(rei, true) + .next().next().next().finish(); + // return to course + courseRuntime = members + .clickToolbarBack() + .efficiencyStatementConfiguration() + .enableCertificates(true) + .enableRecertification() + .save() + .clickToolbarBack(); + + //Participant log in + LoginPage reiLoginPage = LoginPage.getLoginPage(reiBrowser, deploymentUrl); + reiLoginPage + .loginAs(rei.getLogin(), rei.getPassword()) + .resume(); + + //open the course + NavigationPage reiNavBar = new NavigationPage(reiBrowser); + reiNavBar + .openMyCourses() + .select(courseTitle); + + //go to the test + CoursePageFragment reiTestCourse = new CoursePageFragment(reiBrowser); + reiTestCourse + .clickTree() + .selectWithTitle(testNodeTitle); + //pass the test + QTI12Page.getQTI12Page(reiBrowser).passE4(rei); + + //open the efficiency statements + UserToolsPage reiUserTools = new UserToolsPage(reiBrowser); + reiUserTools + .openUserToolsMenu() + .openMyEfficiencyStatement() + .assertOnEfficiencyStatmentPage() + .assertOnCertificateAndStatements(courseTitle) + .selectStatement(courseTitle).selectStatementSegment() + .assertOnCourseDetails(testNodeTitle, true); + } } diff --git a/src/test/java/org/olat/selenium/page/course/CourseEditorPageFragment.java b/src/test/java/org/olat/selenium/page/course/CourseEditorPageFragment.java index ac42ca0ae1f4cdbe73026feb371dad190cae4ea0..82ab18128ba14935eeb633430a2a1e1803a5efa2 100644 --- a/src/test/java/org/olat/selenium/page/course/CourseEditorPageFragment.java +++ b/src/test/java/org/olat/selenium/page/course/CourseEditorPageFragment.java @@ -86,6 +86,82 @@ public class CourseEditorPageFragment { return this; } + /** + * Select the root course element. + */ + public CourseEditorPageFragment selectRoot() { + By rootNodeBy = By.cssSelector("span.o_tree_link.o_tree_l0>a"); + browser.findElement(rootNodeBy).click(); + OOGraphene.waitBusy(browser); + return this; + } + + /** + * Select the tab score in a structure node. + * + */ + public CourseEditorPageFragment selectTabScore() { + By scoreTabBy = By.cssSelector("fieldset.o_sel_structure_score"); + return selectTab(scoreTabBy); + } + + private CourseEditorPageFragment selectTab(By tabBy) { + List<WebElement> tabLinks = browser.findElements(navBarNodeConfiguration); + + boolean found = false; + a_a: + for(WebElement tabLink:tabLinks) { + tabLink.click(); + OOGraphene.waitBusy(browser); + List<WebElement> chooseRepoEntry = browser.findElements(tabBy); + if(chooseRepoEntry.size() > 0) { + found = true; + break a_a; + } + } + + Assert.assertTrue("Found the tab", found); + return this; + } + + /** + * Enable passed and points by nodes + * @return + */ + public CourseEditorPageFragment enableRootScoreByNodes() { + By enablePointBy = By.cssSelector("fieldset.o_sel_structure_score .o_sel_has_score input[type='checkbox']"); + browser.findElement(enablePointBy).click(); + OOGraphene.waitBusy(browser); //scform.scoreNodeIndents + + By enablePointNodesBy = By.cssSelector("fieldset.o_sel_structure_score input[type='checkbox'][name='scform.scoreNodeIndents']"); + List<WebElement> pointNodeEls = browser.findElements(enablePointNodesBy); + for(WebElement pointNodeEl:pointNodeEls) { + pointNodeEl.click(); + OOGraphene.waitBusy(browser); + } + + By enablePassedBy = By.cssSelector("fieldset.o_sel_structure_score .o_sel_has_passed input[type='checkbox']"); + browser.findElement(enablePassedBy).click(); + OOGraphene.waitBusy(browser); + + By passedInheritBy = By.cssSelector("fieldset.o_sel_structure_score input[type='radio'][name='passedType'][value='inherit']"); + browser.findElement(passedInheritBy).click(); + OOGraphene.waitBusy(browser); + + By enablePassedNodesBy = By.cssSelector("fieldset.o_sel_structure_score input[type='checkbox'][name='scform.passedNodeIndents']"); + List<WebElement> enablePassedNodeEls = browser.findElements(enablePassedNodesBy); + for(WebElement enablePassedNodeEl:enablePassedNodeEls) { + enablePassedNodeEl.click(); + OOGraphene.waitBusy(browser); + } + + //save + By submitBy = By.cssSelector("fieldset.o_sel_structure_score button.btn.btn-primary"); + browser.findElement(submitBy).click(); + OOGraphene.waitBusy(browser); + return this; + } + /** * Create a new course element * @param nodeAlias The type of the course element diff --git a/src/test/java/org/olat/selenium/page/user/EfficiencyStatementPage.java b/src/test/java/org/olat/selenium/page/user/EfficiencyStatementPage.java index b20a5a597ea403c27c2692ce852530c832d74370..8a0004db54fc204be933a0306f607589ac01a5f7 100644 --- a/src/test/java/org/olat/selenium/page/user/EfficiencyStatementPage.java +++ b/src/test/java/org/olat/selenium/page/user/EfficiencyStatementPage.java @@ -22,6 +22,7 @@ package org.olat.selenium.page.user; import java.util.List; import org.junit.Assert; +import org.olat.selenium.page.graphene.OOGraphene; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; @@ -59,5 +60,95 @@ public class EfficiencyStatementPage { Assert.assertFalse(certificateDownloadEls.isEmpty()); return this; } + + /** + * + * Statement cut the title, be aware of it + * + * @param courseTitle + * @return + */ + public EfficiencyStatementPage assertOnCertificateAndStatements(String courseTitle) { + WebElement rowToAssert = getStatementRow(courseTitle); + boolean found = false; + for(int i=0; i<20; i++) { + By certificateDownloadBy = By.cssSelector("a i.o_icon.o_filetype_pdf"); + List<WebElement> certificateDownloadEls = rowToAssert.findElements(certificateDownloadBy); + if(certificateDownloadEls.size() > 0) { + found = true; + break; + } + OOGraphene.waitingALittleLonger(); + } + Assert.assertTrue(found); + return this; + } + + /** + * In the efficiency statement page / course details, check that + * the node is in the table and the node is passe / failed. + * + * @param testNodeTitle + * @param passed + * @return + */ + public EfficiencyStatementPage assertOnCourseDetails(String testNodeTitle, boolean passed) { + By courseCertificateBy = By.xpath("//div[contains(@class,'o_efficiencystatement')]//table//tr[td[contains(text(),'" + testNodeTitle + "')]]"); + List<WebElement> certifiatesTable = browser.findElements(courseCertificateBy); + Assert.assertFalse(certifiatesTable.isEmpty()); + + By by; + if(passed) { + by = By.cssSelector("td.text-left span.o_state.o_passed"); + } else { + by = By.cssSelector("td.text-left span.o_state.o_failed"); + } + List<WebElement> passedEl = certifiatesTable.get(0).findElements(by); + Assert.assertFalse(passedEl.isEmpty()); + Assert.assertTrue(passedEl.get(0).isDisplayed()); + return this; + } + + /** + * On the page with the certificate and the efficiency statement, + * select the efficiency statement tab where there are the course + * details. + * + * @param courseTitle + * @return + */ + public EfficiencyStatementPage selectStatement(String courseTitle) { + WebElement rowToAssert = getStatementRow(courseTitle); + By courseCertificateBy = By.xpath("//td//a[contains(@href,'cmd.show')]"); + rowToAssert.findElement(courseCertificateBy).click(); + OOGraphene.waitBusy(browser); + return this; + } + + private WebElement getStatementRow(String courseTitle) { + By courseCertificateBy = By.xpath("//div[contains(@class,'o_sel_certificates_table')]//table//tr"); + + WebElement rowToAssert = null; + List<WebElement> rowsEl = browser.findElements(courseCertificateBy); + a_a: + for(WebElement rowEl:rowsEl) { + for(WebElement col:rowEl.findElements(By.tagName("td"))) { + String text = col.getText(); + if(courseTitle.contains(text) || text.contains(courseTitle)) { + rowToAssert = rowEl; + break a_a; + } + } + } + + Assert.assertNotNull(rowToAssert); + return rowToAssert; + } + public EfficiencyStatementPage selectStatementSegment() { + By courseDetailsBy = By.className("o_select_statement_segment"); + browser.findElement(courseDetailsBy).click(); + OOGraphene.waitBusy(browser); + return this; + } } diff --git a/src/test/java/org/olat/test/rest/RepositoryRestClient.java b/src/test/java/org/olat/test/rest/RepositoryRestClient.java index 0a4c2b1f0b1afc36eea6c6dd5badba19e1df6091..62283f1a2610d49bb76f5a92bcfbf65939f1d42e 100644 --- a/src/test/java/org/olat/test/rest/RepositoryRestClient.java +++ b/src/test/java/org/olat/test/rest/RepositoryRestClient.java @@ -35,12 +35,15 @@ import javax.ws.rs.core.UriBuilder; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.olat.restapi.RestConnection; import org.olat.restapi.support.vo.CourseVO; +import org.olat.restapi.support.vo.RepositoryEntryVO; import org.olat.test.ArquillianDeployments; +import org.olat.user.restapi.UserVO; /** * @@ -59,6 +62,12 @@ public class RepositoryRestClient { this(deploymentUrl, "administrator", "openolat"); } + public RepositoryRestClient(URL deploymentUrl, UserVO author) { + this.deploymentUrl = deploymentUrl; + this.username = author.getLogin(); + this.password = author.getPassword(); + } + public RepositoryRestClient(URL deploymentUrl, String username, String password) { this.deploymentUrl = deploymentUrl; this.username = username; @@ -74,6 +83,35 @@ public class RepositoryRestClient { return deployCourse(archive, "-", displayname); } + public RepositoryEntryVO deployResource(File archive, String resourcename, String displayname) + throws URISyntaxException, IOException { + RestConnection conn = new RestConnection(deploymentUrl); + assertTrue(conn.login(username, password)); + + URI request = UriBuilder.fromUri(deploymentUrl.toURI()).path("restapi").path("repo").path("entries").build(); + HttpPut method = conn.createPut(request, MediaType.APPLICATION_JSON, true); + String softKey = UUID.randomUUID().toString(); + HttpEntity entity = MultipartEntityBuilder.create() + .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) + .addBinaryBody("file", archive, ContentType.APPLICATION_OCTET_STREAM, archive.getName()) + .addTextBody("filename", archive.getName()) + .addTextBody("resourcename", resourcename) + .addTextBody("displayname", displayname) + .addTextBody("access", "3") + .addTextBody("softkey", softKey) + .build(); + method.setEntity(entity); + + HttpResponse response = conn.execute(method); + assertTrue(response.getStatusLine().getStatusCode() == 200 || response.getStatusLine().getStatusCode() == 201); + + RepositoryEntryVO vo = conn.parse(response, RepositoryEntryVO.class); + assertNotNull(vo); + assertNotNull(vo.getDisplayname()); + assertNotNull(vo.getKey()); + return vo; + } + public CourseVO deployCourse(File archive, String resourcename, String displayname) throws URISyntaxException, IOException { diff --git a/src/test/java/org/olat/test/rest/UserRestClient.java b/src/test/java/org/olat/test/rest/UserRestClient.java index 547a29cd2fffb526c52a870a5d1ed72f8949bc37..6cd270ad104bb3eb03d11f5576fa55ae652615f8 100644 --- a/src/test/java/org/olat/test/rest/UserRestClient.java +++ b/src/test/java/org/olat/test/rest/UserRestClient.java @@ -67,8 +67,6 @@ public class UserRestClient { this.password = password; } - - public UserVO createRandomUser() throws IOException, URISyntaxException { return createRandomUser("Selena");