From 91ae1b8b1c067bdff2094ba00f4d1f53a5a577e1 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Fri, 3 Nov 2017 12:09:30 +0100 Subject: [PATCH] no-jira: add a selenium test for the FIB editor --- .../interactions/FIBEditorController.java | 2 + .../interactions/FIBScoreController.java | 2 + .../FIBTextEntrySettingsController.java | 4 + .../interactions/_content/fib_score.html | 2 +- .../ui/LectureSettingsAdminController.java | 8 +- .../java/org/olat/selenium/ImsQTI21Test.java | 172 ++++++++++++++++++ .../page/core/AdministrationPage.java | 10 + .../selenium/page/graphene/OOGraphene.java | 21 ++- .../lecture/LectureAdminSettingsPage.java | 68 +++++++ .../page/lecture/TeacherRollCallPage.java | 14 +- .../selenium/page/qti/QTI21EditorPage.java | 5 + .../page/qti/QTI21GapEntriesEditorPage.java | 140 ++++++++++++++ .../qti/QTI21GapEntriesScoreEditorPage.java | 86 +++++++++ .../org/olat/selenium/page/qti/QTI21Page.java | 21 +++ 14 files changed, 550 insertions(+), 5 deletions(-) create mode 100644 src/test/java/org/olat/selenium/page/lecture/LectureAdminSettingsPage.java create mode 100644 src/test/java/org/olat/selenium/page/qti/QTI21GapEntriesEditorPage.java create mode 100644 src/test/java/org/olat/selenium/page/qti/QTI21GapEntriesScoreEditorPage.java diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBEditorController.java index 6e7eef7aaa1..8a93302e1cb 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBEditorController.java @@ -103,6 +103,7 @@ public class FIBEditorController extends FormBasicController { textEl = uifactory.addRichTextElementForQTI21("desc", "form.imd.descr", question, 16, -1, itemContainer, formLayout, ureq.getUserSession(), getWindowControl()); textEl.addActionListener(FormEvent.ONCLICK); + textEl.setElementCssClass("o_sel_assessment_item_fib_text"); RichTextConfiguration richTextConfig = textEl.getEditorConfiguration(); richTextConfig.setReadOnly(restrictedEdit); @@ -129,6 +130,7 @@ public class FIBEditorController extends FormBasicController { // Submit Button FormLayoutContainer buttonsContainer = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + buttonsContainer.setElementCssClass("o_sel_fib_save"); buttonsContainer.setRootForm(mainForm); formLayout.add(buttonsContainer); uifactory.addFormSubmitButton("submit", buttonsContainer); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBScoreController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBScoreController.java index d8574bd6dc4..9e9722e604d 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBScoreController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBScoreController.java @@ -84,11 +84,13 @@ public class FIBScoreController extends AssessmentItemRefEditorController implem setFormContextHelp("Test editor QTI 2.1 in detail#details_testeditor_score"); super.initForm(formLayout, listener, ureq); minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, "0.0", formLayout); + minScoreEl.setElementCssClass("o_sel_assessment_item_min_score"); minScoreEl.setEnabled(false); ScoreBuilder maxScore = itemBuilder.getMaxScoreBuilder(); String maxValue = maxScore == null ? "" : (maxScore.getScore() == null ? "" : maxScore.getScore().toString()); maxScoreEl = uifactory.addTextElement("max.score", "max.score", 8, maxValue, formLayout); + maxScoreEl.setElementCssClass("o_sel_assessment_item_max_score"); maxScoreEl.setEnabled(!restrictedEdit); String[] modeValues = new String[]{ diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBTextEntrySettingsController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBTextEntrySettingsController.java index 09552b59fb1..315ac739d4f 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBTextEntrySettingsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBTextEntrySettingsController.java @@ -75,8 +75,11 @@ public class FIBTextEntrySettingsController extends FormBasicController { @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + formLayout.setElementCssClass("o_sel_gap_entry_form"); + String solution = interaction.getSolution(); solutionEl = uifactory.addTextElement("fib.solution", "fib.solution", 256, solution, formLayout); + solutionEl.setElementCssClass("o_sel_gap_entry_solution"); solutionEl.setEnabled(!restrictedEdit); if(!StringHelper.containsNonWhitespace(solution)) { solutionEl.setFocus(true); @@ -84,6 +87,7 @@ public class FIBTextEntrySettingsController extends FormBasicController { String placeholder = interaction.getPlaceholder(); placeholderEl = uifactory.addTextElement("fib.placeholder", "fib.placeholder", 256, placeholder, formLayout); + placeholderEl.setElementCssClass("o_sel_gap_entry_placeholder"); placeholderEl.setEnabled(!restrictedEdit); String alternativesPage = velocity_root + "/fib_alternatives.html"; diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/fib_score.html b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/fib_score.html index 78277a2a7af..a4979cad25e 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/fib_score.html +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/fib_score.html @@ -1,4 +1,4 @@ -<table class="table"> +<table class="table o_sel_gap_entries_scores"> <thead><tr> <th>$r.translate("form.score.answer.summary")</th> <th>$r.translate("form.score.answer.points")</th> diff --git a/src/main/java/org/olat/modules/lecture/ui/LectureSettingsAdminController.java b/src/main/java/org/olat/modules/lecture/ui/LectureSettingsAdminController.java index 5ab057b48c3..6ad9a4b2e27 100644 --- a/src/main/java/org/olat/modules/lecture/ui/LectureSettingsAdminController.java +++ b/src/main/java/org/olat/modules/lecture/ui/LectureSettingsAdminController.java @@ -112,11 +112,13 @@ public class LectureSettingsAdminController extends FormBasicController { formLayout.add("global", globalCont); partiallyDoneEnabledEl = uifactory.addCheckboxesVertical("lecture.status.partially.done.enabled", globalCont, onKeys, onValues, 1); - + partiallyDoneEnabledEl.setElementCssClass("o_sel_lecture_status_partially_done"); + String[] statusKeys = new String[]{ LectureBlockStatus.cancelled.name() }; String[] statusValues = new String[]{ translate(LectureBlockStatus.cancelled.name()) }; statusEnabledEl = uifactory.addCheckboxesVertical("lecture.status.enabled", globalCont, statusKeys, statusValues, 1); - + statusEnabledEl.setElementCssClass("o_sel_lecture_status_cancelled"); + // reminder enabled reminderEnableEl = uifactory.addCheckboxesHorizontal("lecture.reminder.enabled", globalCont, onKeys, onValues); reminderEnableEl.addActionListener(FormEvent.ONCHANGE); @@ -129,6 +131,7 @@ public class LectureSettingsAdminController extends FormBasicController { autoClosePeriodEl.setMandatory(true); authorizedAbsenceEnableEl = uifactory.addCheckboxesHorizontal("lecture.authorized.absence.enabled", globalCont, onKeys, onValues); + authorizedAbsenceEnableEl.setElementCssClass("o_sel_lecture_autorized_absence"); authorizedAbsenceEnableEl.addActionListener(FormEvent.ONCHANGE); countAuthorizedAbsenceAsAttendantEl = uifactory.addCheckboxesHorizontal("lecture.count.authorized.absence.attendant", globalCont, onKeys, onValues); absenceDefaultAuthorizedEl = uifactory.addCheckboxesHorizontal("lecture.absence.default.authorized", globalCont, onKeys, onValues); @@ -146,6 +149,7 @@ public class LectureSettingsAdminController extends FormBasicController { formLayout.add("buttonsWrapper", buttonsWrapperCont); FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); buttonsWrapperCont.add(buttonsCont); + buttonsCont.setElementCssClass("o_sel_lecture_save_settings"); uifactory.addFormSubmitButton("save", buttonsCont); } diff --git a/src/test/java/org/olat/selenium/ImsQTI21Test.java b/src/test/java/org/olat/selenium/ImsQTI21Test.java index ab302ad0d46..3755cdfca5a 100644 --- a/src/test/java/org/olat/selenium/ImsQTI21Test.java +++ b/src/test/java/org/olat/selenium/ImsQTI21Test.java @@ -47,6 +47,7 @@ import org.olat.selenium.page.course.CourseEditorPageFragment; import org.olat.selenium.page.course.CoursePageFragment; import org.olat.selenium.page.qti.QTI21ConfigurationCEPage; import org.olat.selenium.page.qti.QTI21EditorPage; +import org.olat.selenium.page.qti.QTI21GapEntriesEditorPage; import org.olat.selenium.page.qti.QTI21HotspotEditorPage; import org.olat.selenium.page.qti.QTI21KprimEditorPage; import org.olat.selenium.page.qti.QTI21LobEditorPage; @@ -1945,6 +1946,177 @@ public class ImsQTI21Test { .assertOnAssessmentTestScore(6);// 3 points from the first question, 3 from the second } + /** + * An author make a test with 2 questions using fill-in-blank, + * the first with the score set if all answers are correct, the second + * with scoring per answers.<br> + * A first user make the test, but doesn't answer all questions + * correctly, log out and a second user make the perfect test. + * + * @param authorLoginPage + * @param participantBrowser + * @throws IOException + * @throws URISyntaxException + */ + @Test + @RunAsClient + public void qti21EditorFib_text(@InitialPage LoginPage authorLoginPage, + @Drone @User WebDriver participantBrowser) + throws IOException, URISyntaxException { + + UserVO author = new UserRestClient(deploymentUrl).createAuthor(); + UserVO ryomou = new UserRestClient(deploymentUrl).createRandomUser("Ryomou"); + UserVO rei = new UserRestClient(deploymentUrl).createRandomUser("Rei"); + authorLoginPage.loginAs(author.getLogin(), author.getPassword()); + + String qtiTestTitle = "Hotspot QTI 2.1 " + UUID.randomUUID(); + navBar + .openAuthoringEnvironment() + .createQTI21Test(qtiTestTitle) + .clickToolbarBack(); + + QTI21Page qtiPage = QTI21Page + .getQTI12Page(browser); + QTI21EditorPage qtiEditor = qtiPage + .edit(); + //start a blank test + qtiEditor + .selectNode("Single choice") + .deleteNode(); + + //add a gap entry: all answers score + QTI21GapEntriesEditorPage fibEditor = qtiEditor + .addFib() + .appendContent("Usefull for circles ") + .addGapEntry("Pi", "314") + .saveGapEntry() + .editGapEntry("Ln", "lognat", 2) + .saveGapEntry() + .save(); + //set max score + fibEditor + .selectScores() + .selectAssessmentMode(ScoreEvaluation.allCorrectAnswers) + .setMaxScore("2") + .save(); + // set feedbacks + fibEditor + .selectFeedbacks() + .setHint("Hint", "This is a usefull hint") + .setCorrectSolution("Correct solution", "This is an information about the correct solution") + .setCorrectFeedback("Correct feedback", "Your answer is correct") + .setIncorrectFeedback("Incorrect", "Your answer is not correct") + .save(); + + //add a gap entry: score per anser + fibEditor = qtiEditor + .addFib() + .appendContent("European rocket ") + .addGapEntry("Ariane", "ari") + .saveGapEntry() + .editGapEntry("Falcon9", "falc", 2) + .saveGapEntry() + .save(); + //set max score + fibEditor + .selectScores() + .selectAssessmentMode(ScoreEvaluation.perAnswer) + .setMaxScore("4") + .setScore("Ariane", "3") + .setScore("Falcon9", "1") + .save(); + // set feedbacks + fibEditor + .selectFeedbacks() + .setHint("Hint", "Think to space") + .setCorrectSolution("Correct solution", "This is an information about the correct solution") + .setCorrectFeedback("Correct feedback", "Your answer is correct") + .setIncorrectFeedback("Incorrect", "Your answer is not correct") + .save(); + + qtiPage + .clickToolbarBack(); + // access to all + qtiPage + .accessConfiguration() + .setUserAccess(UserAccess.guest) + .clickToolbarBack(); + // show results + qtiPage + .options() + .showResults(Boolean.TRUE, QTI21AssessmentResultsOptions.allOptions()) + .save(); + + //a user search the content package + LoginPage userLoginPage = LoginPage.getLoginPage(participantBrowser, deploymentUrl); + userLoginPage + .loginAs(ryomou.getLogin(), ryomou.getPassword()) + .resume(); + NavigationPage userNavBar = new NavigationPage(participantBrowser); + userNavBar + .openMyCourses() + .openSearch() + .extendedSearch(qtiTestTitle) + .select(qtiTestTitle) + .start(); + + // first user make the test + QTI21Page ryomouQtiPage = QTI21Page + .getQTI12Page(participantBrowser); + ryomouQtiPage + .assertOnAssessmentItem() + .answerGapTextWithPlaceholder("Log", "314") + .answerGapTextWithPlaceholder("Sin", "lognat") + .saveAnswer() + .assertFeedback("Incorrect") + .assertCorrectSolution("Correct solution") + .hint() + .assertFeedback("Hint") + .answerGapTextWithPlaceholder("Pi", "314") + .answerGapTextWithPlaceholder("Ln", "lognat") + .saveAnswer() + .assertFeedback("Correct feedback") + .nextAnswer() + .answerGapTextWithPlaceholder("Saturn 5", "ari") + .answerGapTextWithPlaceholder("Falcon9", "falc") + .saveAnswer() + .assertCorrectSolution("Correct solution") + .assertFeedback("Incorrect") + .endTest() + .assertOnAssessmentResults() + .assertOnAssessmentTestScore(3);// 2 points from the first question, 1 from the second + + + //a second user search the content package + LoginPage reiLoginPage = LoginPage.getLoginPage(participantBrowser, deploymentUrl); + reiLoginPage + .loginAs(rei.getLogin(), rei.getPassword()) + .resume(); + NavigationPage reiNavBar = new NavigationPage(participantBrowser); + reiNavBar + .openMyCourses() + .openSearch() + .extendedSearch(qtiTestTitle) + .select(qtiTestTitle) + .start(); + + // make the test with all the correct answers + QTI21Page + .getQTI12Page(participantBrowser) + .assertOnAssessmentItem() + .answerGapTextWithPlaceholder("Pi", "314") + .answerGapTextWithPlaceholder("Ln", "lognat") + .saveAnswer() + .assertFeedback("Correct feedback") + .nextAnswer() + .answerGapTextWithPlaceholder("Ariane", "ari") + .answerGapTextWithPlaceholder("Falcon9", "falc") + .saveAnswer() + .endTest() + .assertOnAssessmentResults() + .assertOnAssessmentTestScore(6);// 2 points from the first question, 4 from the second + } + /** * An author make a test with 2 matches. A match with "multiple selection" * and score "all answers", a second with "single selection" and score diff --git a/src/test/java/org/olat/selenium/page/core/AdministrationPage.java b/src/test/java/org/olat/selenium/page/core/AdministrationPage.java index 02363a69a6b..02da1bd003e 100644 --- a/src/test/java/org/olat/selenium/page/core/AdministrationPage.java +++ b/src/test/java/org/olat/selenium/page/core/AdministrationPage.java @@ -23,6 +23,7 @@ import java.util.List; import org.junit.Assert; import org.olat.selenium.page.graphene.OOGraphene; +import org.olat.selenium.page.lecture.LectureAdminSettingsPage; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; @@ -91,6 +92,15 @@ public class AdministrationPage { return this; } + public LectureAdminSettingsPage openLecturesSettings() { + selectModules(); + + By lecturesBy = By.cssSelector(".o_sel_lectures span.o_tree_level_label_leaf>a"); + browser.findElement(lecturesBy).click(); + OOGraphene.waitBusy(browser); + return new LectureAdminSettingsPage(browser); + } + public AdministrationPage openGroupSettings() { selectModules(); diff --git a/src/test/java/org/olat/selenium/page/graphene/OOGraphene.java b/src/test/java/org/olat/selenium/page/graphene/OOGraphene.java index ec91e6a3512..4a058b65bcb 100644 --- a/src/test/java/org/olat/selenium/page/graphene/OOGraphene.java +++ b/src/test/java/org/olat/selenium/page/graphene/OOGraphene.java @@ -213,7 +213,7 @@ public class OOGraphene { /** * Scroll to the top anchor. * - * @param browser + * @param browser The browser */ public static void scrollTop(WebDriver browser) { WebElement el = browser.findElement(By.id("o_top")); @@ -242,6 +242,25 @@ public class OOGraphene { ((JavascriptExecutor)browser).executeScript("top.tinymce.editors['" + tinyId + "'].setContent('" + content + "')"); } + /** + * Insert a piece of text in TinyMCE where is the caret. + * + * @param content The text to add + * @param containerCssSelector A selector to point where the rich text editor is + * @param browser The browser + */ + public static final void tinymceInsert(String content, String containerCssSelector, WebDriver browser) { + By tinyIdBy = By.cssSelector(containerCssSelector + " div.o_richtext_mce"); + waitElement(tinyIdBy, 5, browser); + WebElement tinyIdEl = browser.findElement(tinyIdBy); + String tinyId = tinyIdEl.getAttribute("id").replace("_diw", ""); + + Graphene.waitModel(browser).withTimeout(waitTinyDuration, TimeUnit.SECONDS) + .pollingEvery(poolingDuration, TimeUnit.MILLISECONDS) + .until(new TinyMCELoadedByIdPredicate(tinyId)); + ((JavascriptExecutor)browser).executeScript("top.tinymce.editors['" + tinyId + "'].insertContent('" + content + "')"); + } + /** * * @param tabsBy The selector for the tabs bar diff --git a/src/test/java/org/olat/selenium/page/lecture/LectureAdminSettingsPage.java b/src/test/java/org/olat/selenium/page/lecture/LectureAdminSettingsPage.java new file mode 100644 index 00000000000..311036c558e --- /dev/null +++ b/src/test/java/org/olat/selenium/page/lecture/LectureAdminSettingsPage.java @@ -0,0 +1,68 @@ +/** + * <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.lecture; + +import org.olat.selenium.page.graphene.OOGraphene; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +/** + * + * Initial date: 31 oct. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class LectureAdminSettingsPage { + + private WebDriver browser; + + public LectureAdminSettingsPage(WebDriver browser) { + this.browser = browser; + } + + /** + * Set some configurations of the lecture module. + * + * @param authorizedAbsence Enable/disable authorized absence + * @return Itself + */ + public LectureAdminSettingsPage set(boolean authorizedAbsence) { + By authorizedAbsenceLabelBy = By.xpath("//label[input[@name='lecture.authorized.absence.enabled' and @value='on']]"); + By authorizedAbsenceCheckBy = By.xpath("//label/input[@name='lecture.authorized.absence.enabled' and @value='on']"); + + OOGraphene.waitElement(authorizedAbsenceLabelBy, browser); + OOGraphene.scrollTo(authorizedAbsenceLabelBy, browser); + + WebElement authorizedAbsenceLabelEl = browser.findElement(authorizedAbsenceLabelBy); + WebElement authorizedAbsenceCheckEl = browser.findElement(authorizedAbsenceCheckBy); + OOGraphene.check(authorizedAbsenceLabelEl, authorizedAbsenceCheckEl, new Boolean(authorizedAbsence)); + OOGraphene.waitBusy(browser); + return this; + } + + public LectureAdminSettingsPage save() { + By saveBy = By.cssSelector("div.o_sel_lecture_save_settings button.btn-primary"); + browser.findElement(saveBy).click(); + OOGraphene.waitBusy(browser); + return this; + } + +} diff --git a/src/test/java/org/olat/selenium/page/lecture/TeacherRollCallPage.java b/src/test/java/org/olat/selenium/page/lecture/TeacherRollCallPage.java index 986215d55a4..f7818426d79 100644 --- a/src/test/java/org/olat/selenium/page/lecture/TeacherRollCallPage.java +++ b/src/test/java/org/olat/selenium/page/lecture/TeacherRollCallPage.java @@ -19,11 +19,14 @@ */ package org.olat.selenium.page.lecture; +import java.util.List; + import org.olat.selenium.page.graphene.OOGraphene; import org.olat.user.restapi.UserVO; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.Select; /** * @@ -79,10 +82,19 @@ public class TeacherRollCallPage { } /** - * Simply confirm the clsoing of the roll call. + * Simply confirm the closing of the roll call and choose + * a reason if there is one. + * * @return Itself */ public TeacherRollCallPage confirmCloseRollCall() { + //check reasons + By reasonsBy = By.id("o_fioeffective_reason_SELBOX"); + List<WebElement> reasonsEls = browser.findElements(reasonsBy); + if(reasonsEls.size() > 0) { + new Select(reasonsEls.get(0)).selectByIndex(1); + } + By confirmCloseBy = By.cssSelector("fieldset.o_sel_lecture_confirm_close_form button.btn-primary"); browser.findElement(confirmCloseBy).click(); OOGraphene.waitBusy(browser); diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21EditorPage.java b/src/test/java/org/olat/selenium/page/qti/QTI21EditorPage.java index 70a1e3d7207..da88b562678 100644 --- a/src/test/java/org/olat/selenium/page/qti/QTI21EditorPage.java +++ b/src/test/java/org/olat/selenium/page/qti/QTI21EditorPage.java @@ -142,6 +142,11 @@ public class QTI21EditorPage { return new QTI21HotspotEditorPage(browser); } + public QTI21GapEntriesEditorPage addFib() { + addQuestion(QTI21QuestionType.fib); + return new QTI21GapEntriesEditorPage(browser); + } + private QTI21EditorPage addQuestion(QTI21QuestionType type) { openElementsMenu(); diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21GapEntriesEditorPage.java b/src/test/java/org/olat/selenium/page/qti/QTI21GapEntriesEditorPage.java new file mode 100644 index 00000000000..8213ce301d1 --- /dev/null +++ b/src/test/java/org/olat/selenium/page/qti/QTI21GapEntriesEditorPage.java @@ -0,0 +1,140 @@ +/** + * <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.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +/** + * + * Initial date: 2 nov. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class QTI21GapEntriesEditorPage extends QTI21AssessmentItemEditorPage { + + public QTI21GapEntriesEditorPage(WebDriver browser) { + super(browser); + } + + public QTI21GapEntriesEditorPage appendContent(String text) { + String textSelector = ".o_sel_assessment_item_fib_text"; + OOGraphene.tinymceInsert(text, textSelector, browser); + return this; + } + + /** + * Add a new gap entry of type text. Use the placeholder to locate + * the gap during the test. + * + * @param solution The solution + * @param placeholder The placeholder + * @return Itself + */ + public QTI21GapEntriesEditorPage addGapEntry(String solution, String placeholder) { + By addGapBy = By.xpath("//div[contains(@class,'o_sel_assessment_item_fib_text')]//button[i[contains(@class,'mce-i-gaptext')]]"); + browser.findElement(addGapBy).click(); + OOGraphene.waitModalDialog(browser); + + By solutionBy = By.cssSelector("fieldset.o_sel_gap_entry_form div.o_sel_gap_entry_solution input[type=text]"); + OOGraphene.waitElement(solutionBy, browser); + browser.findElement(solutionBy).sendKeys(solution); + + By placeholderBy = By.cssSelector("fieldset.o_sel_gap_entry_form div.o_sel_gap_entry_placeholder input[type=text]"); + browser.findElement(placeholderBy).sendKeys(placeholder); + return this; + } + + /** + * Edit an existing gap entry of type text. Use the placeholder to locate + * the gap during the test. + * + * @param solution The solution + * @param placeholder The placeholder + * @param index The index of the entry in the paragraph + * @return Itself + */ + public QTI21GapEntriesEditorPage editGapEntry(String solution, String placeholder, int index) { + By frameBy = By.cssSelector("div.o_sel_assessment_item_fib_text div.mce-edit-area iframe"); + WebElement frameEl = browser.findElement(frameBy); + browser.switchTo().frame(frameEl); + + By gapEntryBy = By.xpath("//p/span[@class='textentryinteraction'][" + index + "]/a"); + browser.findElement(gapEntryBy).click(); + + browser.switchTo().defaultContent(); + OOGraphene.waitModalDialog(browser); + + By solutionBy = By.cssSelector("fieldset.o_sel_gap_entry_form div.o_sel_gap_entry_solution input[type=text]"); + WebElement solutionEl = browser.findElement(solutionBy); + solutionEl.clear(); + solutionEl.sendKeys(solution); + + By placeholderBy = By.cssSelector("fieldset.o_sel_gap_entry_form div.o_sel_gap_entry_placeholder input[type=text]"); + browser.findElement(placeholderBy).sendKeys(placeholder); + return this; + } + + /** + * Save and close the modal dialog to edit the gap entry. + * + * @return Itself + */ + public QTI21GapEntriesEditorPage saveGapEntry() { + By saveBy = By.cssSelector(".o_sel_gap_entry_form button.btn-primary"); + browser.findElement(saveBy).click(); + OOGraphene.waitBusy(browser); + return this; + } + + /** + * Save the whole interaction. + * + * @return Itself + */ + public QTI21GapEntriesEditorPage save() { + By saveBy = By.cssSelector("div.o_sel_fib_save button.btn.btn-primary"); + OOGraphene.click(saveBy, browser); + OOGraphene.waitBusy(browser); + return this; + } + + /** + * Select the tab to edit the scores + * + * @return The score page + */ + public QTI21GapEntriesScoreEditorPage selectScores() { + selectTab(By.className("o_sel_assessment_item_options")); + return new QTI21GapEntriesScoreEditorPage(browser); + } + + /** + * Select the tab to edit the feedbacks + * + * @return the feedback page + */ + public QTI21FeedbacksEditorPage selectFeedbacks() { + selectTab(By.className("o_sel_assessment_item_feedbacks")); + return new QTI21FeedbacksEditorPage(browser); + } +} diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21GapEntriesScoreEditorPage.java b/src/test/java/org/olat/selenium/page/qti/QTI21GapEntriesScoreEditorPage.java new file mode 100644 index 00000000000..d534738e9bb --- /dev/null +++ b/src/test/java/org/olat/selenium/page/qti/QTI21GapEntriesScoreEditorPage.java @@ -0,0 +1,86 @@ +/** + * <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.ims.qti21.model.xml.interactions.SimpleChoiceAssessmentItemBuilder.ScoreEvaluation; +import org.olat.selenium.page.graphene.OOGraphene; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +/** + * + * Initial date: 3 nov. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class QTI21GapEntriesScoreEditorPage { + + private static final By choiceScoreTable = By.className("o_sel_gap_entries_scores"); + + private final WebDriver browser; + + public QTI21GapEntriesScoreEditorPage(WebDriver browser) { + this.browser = browser; + } + + public QTI21GapEntriesScoreEditorPage selectAssessmentMode(ScoreEvaluation mode) { + By modeBy = By.cssSelector("#o_coassessment_mode input[value='" + mode.name() + "']"); + browser.findElement(modeBy).click(); + OOGraphene.waitBusy(browser); + if(mode == ScoreEvaluation.allCorrectAnswers) { + OOGraphene.waitElementDisappears(choiceScoreTable, 5, browser); + } else if (mode == ScoreEvaluation.perAnswer) { + OOGraphene.waitElement(choiceScoreTable, 5, browser); + } + return this; + } + + public QTI21GapEntriesScoreEditorPage setScore(String answer, String score) { + By scoreBy = By.xpath("//table[contains(@class,'o_sel_gap_entries_scores')]//tr[td[contains(text(),'" + answer + "')]]/td/div/input[@type='text']"); + WebElement scoreEl = browser.findElement(scoreBy); + scoreEl.clear(); + scoreEl.sendKeys(score); + return this; + } + + public QTI21GapEntriesScoreEditorPage setMaxScore(String maxScore) { + By maxScoreBy = By.cssSelector("div.o_sel_assessment_item_max_score input[type='text']"); + WebElement maxScoreEl = browser.findElement(maxScoreBy); + maxScoreEl.clear(); + maxScoreEl.sendKeys(maxScore); + return this; + } + + public QTI21GapEntriesScoreEditorPage setMinScore(String minScore) { + By minScoreBy = By.cssSelector("div.o_sel_assessment_item_min_score input[type='text']"); + WebElement minScoreEl = browser.findElement(minScoreBy); + minScoreEl.clear(); + minScoreEl.sendKeys(minScore); + return this; + } + + public QTI21GapEntriesScoreEditorPage save() { + By saveBy = By.cssSelector("fieldset.o_sel_assessment_item_options button.btn.btn-primary"); + browser.findElement(saveBy).click(); + OOGraphene.waitBusy(browser); + return this; + } +} diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21Page.java b/src/test/java/org/olat/selenium/page/qti/QTI21Page.java index e4636d51693..95b0b83efe9 100644 --- a/src/test/java/org/olat/selenium/page/qti/QTI21Page.java +++ b/src/test/java/org/olat/selenium/page/qti/QTI21Page.java @@ -160,6 +160,13 @@ public class QTI21Page { return this; } + /** + * Fill the gap entry based on its response id. + * + * @param text The answer + * @param responseId The identifier of the text entry + * @return Itself + */ public QTI21Page answerGapText(String text, String responseId) { By gapBy = By.xpath("//span[contains(@class,'textEntryInteraction')]/input[@type='text'][contains(@name,'" + responseId + "')]"); WebElement gapEl = browser.findElement(gapBy); @@ -168,6 +175,20 @@ public class QTI21Page { return this; } + /** + * + * @param text The answer + * @param placeholder The placeholder to found the right gap + * @return Itself + */ + public QTI21Page answerGapTextWithPlaceholder(String text, String placeholder) { + By gapBy = By.xpath("//span[contains(@class,'textEntryInteraction')]/input[@type='text'][@placeholder='" + placeholder + "']"); + WebElement gapEl = browser.findElement(gapBy); + gapEl.clear(); + gapEl.sendKeys(text); + return this; + } + public QTI21Page answerMatch(String source, String target, boolean match) { By matchBy = By.xpath("//div[contains(@class,'matchInteraction')]/table//tr[th/p[contains(text(),'" + source + "')]]/td[count(//div[contains(@class,'matchInteraction')]/table//tr/th[p[contains(text(),'" + target + "')]]/preceding-sibling::th)]/input"); WebElement matchEl = browser.findElement(matchBy); -- GitLab