From 027bb199502da27ad8e0d0a09ab8f65bfea8d02a Mon Sep 17 00:00:00 2001
From: srosse <stephane.rosse@frentix.com>
Date: Mon, 18 May 2020 20:36:43 +0200
Subject: [PATCH] no-jira: selenium test for the BigBlueButton course element

---
 .../OrderAssessmentItemBuilder.java           |  19 ++
 .../BigBlueButtonEditMeetingsController.java  |   5 +
 .../ui/BigBlueButtonMeetingController.java    |   1 +
 .../ui/BigBlueButtonRunController.java        |   2 +
 .../EditBigBlueButtonMeetingController.java   |   2 +
 .../bigbluebutton/ui/_content/meeting.html    |   3 +-
 .../ui/_content/meetings_admin.html           |   2 +-
 .../bigbluebutton/ui/_content/run.html        |   2 +-
 .../bigbluebutton/ui/_content/run_admin.html  |   2 +-
 .../org/olat/selenium/CourseElementTest.java  |  60 +++++++
 .../page/course/BigBlueButtonPage.java        | 168 ++++++++++++++++++
 .../page/repository/AuthoringEnvPage.java     |   6 +-
 src/test/profile/mysql/olat.local.properties  |   4 +
 src/test/profile/oracle/olat.local.properties |   5 +-
 .../profile/postgresql/olat.local.properties  |   5 +-
 15 files changed, 279 insertions(+), 7 deletions(-)
 create mode 100644 src/test/java/org/olat/selenium/page/course/BigBlueButtonPage.java

diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/OrderAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/OrderAssessmentItemBuilder.java
index 9f52a16c16e..21e1eb305c6 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/OrderAssessmentItemBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/OrderAssessmentItemBuilder.java
@@ -1,3 +1,22 @@
+/**
+ * <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.ims.qti21.model.xml.interactions;
 
 import static org.olat.ims.qti21.model.xml.AssessmentItemFactory.appendDefaultItemBody;
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonEditMeetingsController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonEditMeetingsController.java
index d6cc3321f9c..8f294932ed8 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonEditMeetingsController.java
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonEditMeetingsController.java
@@ -116,16 +116,21 @@ public class BigBlueButtonEditMeetingsController extends FormBasicController {
 		if(!readOnly) {
 			DropdownItem addMeetingDropdown = uifactory.addDropdownMenu("add.meeting", "add.meeting", formLayout, getTranslator());
 			addMeetingDropdown.setOrientation(DropdownOrientation.right);
+			addMeetingDropdown.setElementCssClass("o_sel_bbb_meeting_add");
 			
 			addSingleMeetingLink = uifactory.addFormLink("add.single.meeting", formLayout, Link.LINK);
+			addSingleMeetingLink.setElementCssClass("o_sel_bbb_single_meeting_add");
 			addMeetingDropdown.addElement(addSingleMeetingLink);
 			if(bigBlueButtonModule.isPermanentMeetingEnabled()) {
 				addPermanentMeetingLink = uifactory.addFormLink("add.permanent.meeting", formLayout, Link.LINK);
+				addPermanentMeetingLink.setElementCssClass("o_sel_bbb_permanent_meeting_add");
 				addMeetingDropdown.addElement(addPermanentMeetingLink);
 			}
 			addDailyRecurringMeetingsLink = uifactory.addFormLink("add.daily.meeting", formLayout, Link.LINK);
+			addDailyRecurringMeetingsLink.setElementCssClass("o_sel_bbb_daily_meeting_add");
 			addMeetingDropdown.addElement(addDailyRecurringMeetingsLink);
 			addWeekyRecurringMeetingsLink = uifactory.addFormLink("add.weekly.meeting", formLayout, Link.LINK);
+			addWeekyRecurringMeetingsLink.setElementCssClass("o_sel_bbb_weekly_meeting_add");
 			addMeetingDropdown.addElement(addWeekyRecurringMeetingsLink);
 			
 			deleteButton = uifactory.addFormLink("delete", formLayout, Link.BUTTON);
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java
index fe56f6fff44..925136c6276 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonMeetingController.java
@@ -119,6 +119,7 @@ public class BigBlueButtonMeetingController extends FormBasicController implemen
 		}
 		
 		joinButton = LinkFactory.createButtonLarge("meeting.join.button", flc.getFormItemComponent(), this);
+		joinButton.setElementCssClass("o_sel_bbb_join");
 		joinButton.setTarget("_blank");
 		joinButton.setVisible(!ended);
 		joinButton.setTextReasonForDisabling(translate("warning.no.access"));
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRunController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRunController.java
index e271862830d..64f351a0e4a 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRunController.java
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/BigBlueButtonRunController.java
@@ -98,9 +98,11 @@ public class BigBlueButtonRunController extends BasicController implements Activ
 			
 			segmentView = SegmentViewFactory.createSegmentView("segments", mainVC, this);
 			meetingsLink = LinkFactory.createLink("meetings.title", mainVC, this);
+			meetingsLink.setElementCssClass("o_sel_bbb_meetings_segment");
 			segmentView.addSegment(meetingsLink, true);
 			
 			adminLink = LinkFactory.createLink("meetings.admin.title", mainVC, this);
+			adminLink.setElementCssClass("o_sel_bbb_edit_meetings_segment");
 			segmentView.addSegment(adminLink, false);
 			
 			doOpenMeetings(ureq);
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java b/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java
index e9bdb88c80f..665d573e5b0 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/EditBigBlueButtonMeetingController.java
@@ -119,6 +119,7 @@ public class EditBigBlueButtonMeetingController extends FormBasicController {
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		formLayout.setElementCssClass("o_sel_bbb_edit_meeting");
 		
 		if(!editable) {
 			setFormWarning("warning.meeting.started");
@@ -126,6 +127,7 @@ public class EditBigBlueButtonMeetingController extends FormBasicController {
 		
 		String name = meeting == null ? "" : meeting.getName();
 		nameEl = uifactory.addTextElement("meeting.name", "meeting.name", 128, name, formLayout);
+		nameEl.setElementCssClass("o_sel_bbb_edit_meeting_name");
 		nameEl.setMandatory(true);
 		nameEl.setEnabled(editable);
 		if(editable && !StringHelper.containsNonWhitespace(name)) {
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meeting.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meeting.html
index 8a37526ef54..8a2f543f7f1 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meeting.html
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meeting.html
@@ -1,3 +1,4 @@
+<div class="o_sel_bbb_meeting">
 <h3>$r.escapeHtml($title)</h3>
 #if($errorMessage && !${errorMessage.isEmpty()})
 	<div class="o_error">$errorMessage</div>
@@ -37,5 +38,5 @@
 	</fieldset>
 </div>
 #end
-
+</div>
 
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meetings_admin.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meetings_admin.html
index bbb60815b6b..bcb12fcf405 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meetings_admin.html
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/meetings_admin.html
@@ -1,4 +1,4 @@
-<div>$r.contextHelpWithWrapper("Course+element+BigBlueButton")</div>
+<div class="o_sel_bbb_edit_meetings_list">$r.contextHelpWithWrapper("Course+element+BigBlueButton")</div>
 #if($r.available("add.meeting"))
 <div class="o_button_group o_button_group_right">
 	$r.render("add.meeting")
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run.html
index 25de423a882..9c7fab80c16 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run.html
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run.html
@@ -1,4 +1,4 @@
-<div class="clearfix">
+<div class="clearfix o_sel_bbb_overview">
 #if($r.available("meeting"))
 	$r.render("backLink")
 	$r.render("meeting")
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run_admin.html b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run_admin.html
index 0dcce15ec31..2a8e4a3ade2 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run_admin.html
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_content/run_admin.html
@@ -1,4 +1,4 @@
-<div class="clearfix">
+<div class="clearfix o_sel_bbb_overview">
 #if($r.available("meeting"))
 	$r.render("backLink")
 	$r.render("meeting")
diff --git a/src/test/java/org/olat/selenium/CourseElementTest.java b/src/test/java/org/olat/selenium/CourseElementTest.java
index e673bbd968a..078f9321b91 100644
--- a/src/test/java/org/olat/selenium/CourseElementTest.java
+++ b/src/test/java/org/olat/selenium/CourseElementTest.java
@@ -42,6 +42,7 @@ import org.olat.selenium.page.User;
 import org.olat.selenium.page.core.ContactPage;
 import org.olat.selenium.page.core.FolderPage;
 import org.olat.selenium.page.core.MenuTreePageFragment;
+import org.olat.selenium.page.course.BigBlueButtonPage;
 import org.olat.selenium.page.course.ContactConfigPage;
 import org.olat.selenium.page.course.CourseEditorPageFragment;
 import org.olat.selenium.page.course.CoursePageFragment;
@@ -1978,4 +1979,63 @@ public class CourseElementTest extends Deployments {
 			.saveAndCloseSurvey()
 			.assertOnSurveyClosed();
 	}
+	
+	
+
+	/**
+	 * 
+	 * @param loginPage
+	 * @throws IOException
+	 * @throws URISyntaxException
+	 */
+	@Test
+	@RunAsClient
+	public void courseWithBigBlueButton()
+	throws IOException, URISyntaxException {
+
+		UserVO author = new UserRestClient(deploymentUrl).createAuthor();
+		LoginPage loginPage = LoginPage.load(browser, deploymentUrl);
+		loginPage.loginAs(author.getLogin(), author.getPassword());
+		
+		//create a course
+		String courseTitle = "Course-With-BBB-" + UUID.randomUUID();
+		NavigationPage navBar = NavigationPage.load(browser);
+		navBar
+			.openAuthoringEnvironment()
+			.createCourse(courseTitle, true)
+			.clickToolbarBack();
+		
+		String nodeTitle = "BBB Node-1";
+		//create a course element of type CP with the CP that we create above
+		CoursePageFragment course = CoursePageFragment.getCourse(browser);
+		CourseEditorPageFragment courseEditor = course
+			.edit();
+		courseEditor
+			.createNode("bigbluebutton")
+			.nodeTitle(nodeTitle);
+		
+		//publish the course
+		courseEditor
+			.publish()
+			.quickPublish(UserAccess.membersOnly);
+		courseEditor
+			.clickToolbarBack();
+		
+		course
+			.clickTree()
+			.selectWithTitle(nodeTitle);
+		
+		String meetingName = "Quick meeting";
+		BigBlueButtonPage bigBlueButton = new BigBlueButtonPage(browser);
+		bigBlueButton
+			.assertOnRuntime()
+			.selectEditMeetingsList()
+			.addSingleMeeting(meetingName, "Classroom")
+			.assertOnList(meetingName)
+			.selectMeetingsList()
+			.assertOnList(meetingName)
+			.selectMeeting(meetingName)
+			.assertOnMeeting(meetingName)
+			.assertOnJoin();
+	}
 }
diff --git a/src/test/java/org/olat/selenium/page/course/BigBlueButtonPage.java b/src/test/java/org/olat/selenium/page/course/BigBlueButtonPage.java
new file mode 100644
index 00000000000..2ccac91d7ad
--- /dev/null
+++ b/src/test/java/org/olat/selenium/page/course/BigBlueButtonPage.java
@@ -0,0 +1,168 @@
+/**
+ * <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.course;
+
+import org.olat.selenium.page.graphene.OOGraphene;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * 
+ * Initial date: 18 mai 2020<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class BigBlueButtonPage {
+
+	private final WebDriver browser;
+	
+	public BigBlueButtonPage(WebDriver browser) {
+		this.browser = browser;
+	}
+	
+	/**
+	 * Assert on the wrapper of the meetings lists.
+	 * 
+	 * @return Itself
+	 */
+	public BigBlueButtonPage assertOnRuntime() {
+		By runtimeBy = By.className("o_sel_bbb_overview");
+		OOGraphene.waitElement(runtimeBy, browser);
+		return this;
+	}
+	
+	/**
+	 * The list of meetings to configure.
+	 * 
+	 * @return Itself
+	 */
+	public BigBlueButtonPage selectEditMeetingsList() {
+		By editListBy = By.cssSelector("a.o_sel_bbb_edit_meetings_segment");
+		OOGraphene.waitElement(editListBy, browser);
+		browser.findElement(editListBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitElement(By.className("o_sel_bbb_edit_meetings_list"), browser);
+		return this;
+	}	
+	
+	/**
+	 * Add a single meeting.
+	 * 
+	 * @param name The name of the meeting
+	 * @param template The name of the template
+	 * @return Itself
+	 */
+	public BigBlueButtonPage addSingleMeeting(String name, String template) {
+		openCreateDropDown();
+		
+		By addSingleMeetingBy = By.cssSelector("a.o_sel_bbb_single_meeting_add");
+		OOGraphene.waitElement(addSingleMeetingBy, browser);
+		browser.findElement(addSingleMeetingBy).click();
+		OOGraphene.waitModalDialog(browser);
+		
+		By nameBy = By.cssSelector(".o_sel_bbb_edit_meeting_name input[type='text']");
+		OOGraphene.waitElement(nameBy, browser);
+		browser.findElement(nameBy).sendKeys(name);
+		
+		By templateBy = By.cssSelector(".o_sel_bbb_edit_meeting select#o_fiomeeting_template_SELBOX");
+		new Select(browser.findElement(templateBy)).selectByVisibleText(template);
+		OOGraphene.waitBusy(browser);
+		
+		By saveBy = By.cssSelector("fieldset.o_sel_bbb_edit_meeting button.btn.btn-primary");
+		browser.findElement(saveBy).click();
+		OOGraphene.waitBusy(browser);
+		
+		OOGraphene.waitModalDialogDisappears(browser);
+		return this;
+	}
+	
+	private BigBlueButtonPage openCreateDropDown() {
+		By addMenuCaretBy = By.cssSelector("button.o_sel_bbb_meeting_add");
+		OOGraphene.waitElement(addMenuCaretBy, browser);
+		browser.findElement(addMenuCaretBy).click();
+		
+		By addMenuBy = By.cssSelector("ul.o_sel_bbb_meeting_add");
+		OOGraphene.waitElement(addMenuBy, browser);
+		return this;
+	}
+	
+	/**
+	 * The list of meetings for the end users.
+	 * 
+	 * @return Itself
+	 */
+	public BigBlueButtonPage selectMeetingsList() {
+		By editListBy = By.cssSelector("a.o_sel_bbb_meetings_segment");
+		OOGraphene.waitElement(editListBy, browser);
+		browser.findElement(editListBy).click();
+		OOGraphene.waitBusy(browser);
+		return this;
+	}
+	
+	/**
+	 * Check if the meeting with the specified name is in the list (edit or run);
+	 * @param meetingName The name of the meeting
+	 * @return Itself
+	 */
+	public BigBlueButtonPage assertOnList(String meetingName) {
+		By meetingBy = By.xpath("//div[contains(@class,'o_table_flexi')]//table//td[contains(@class,'o_dnd_label')][text()[contains(.,'" + meetingName + "')]]");
+		OOGraphene.waitElement(meetingBy, browser);
+		return this;
+	}
+	
+	/**
+	 * Select a meeting in the run list.
+	 * 
+	 * @param meetingName The name of the meeting to select
+	 * @return Itself
+	 */
+	public BigBlueButtonPage selectMeeting(String meetingName) {
+		By selectBy = By.xpath("//div[contains(@class,'o_table_flexi')]//table//tr[td[text()[contains(.,'" + meetingName + "')]]]/td/a[contains(@onclick,'select')]");
+		OOGraphene.waitElement(selectBy, browser);
+		browser.findElement(selectBy).click();
+		OOGraphene.waitBusy(browser);
+		return this;
+	}
+	
+	/**
+	 * Check that the meeting with the specified is on the start page.
+	 * 
+	 * @param meetingName The meeting name
+	 * @return Itself
+	 */
+	public BigBlueButtonPage assertOnMeeting(String meetingName) {
+		By titleBy = By.xpath("//div[@class='o_sel_bbb_meeting']//h3[text()[contains(.,'" + meetingName + "')]]");
+		OOGraphene.waitElement(titleBy, browser);
+		return this;
+	}
+	
+	/**
+	 * Check that the join button is visible.
+	 * 
+	 * @return Itself
+	 */
+	public BigBlueButtonPage assertOnJoin() {
+		By joinBy = By.cssSelector("div.o_sel_bbb_meeting a.btn.o_sel_bbb_join");
+		OOGraphene.waitElement(joinBy, browser);
+		return this;
+	}
+
+}
diff --git a/src/test/java/org/olat/selenium/page/repository/AuthoringEnvPage.java b/src/test/java/org/olat/selenium/page/repository/AuthoringEnvPage.java
index 8ecb60a1179..62bd8f4344e 100644
--- a/src/test/java/org/olat/selenium/page/repository/AuthoringEnvPage.java
+++ b/src/test/java/org/olat/selenium/page/repository/AuthoringEnvPage.java
@@ -82,9 +82,13 @@ public class AuthoringEnvPage {
 	}
 	
 	public CourseSettingsPage createCourse(String title) {
+		return createCourse(title, false);
+	}
+	
+	public CourseSettingsPage createCourse(String title, boolean learnPath) {
 		openCreateDropDown()
 			.clickCreate(ResourceType.course)
-			.fillCreateCourseForm(title, false)
+			.fillCreateCourseForm(title, learnPath)
 			.assertOnInfos();
 		return new CourseSettingsPage(browser);
 	}
diff --git a/src/test/profile/mysql/olat.local.properties b/src/test/profile/mysql/olat.local.properties
index ba0b28cb6bc..b32c1bd4d67 100644
--- a/src/test/profile/mysql/olat.local.properties
+++ b/src/test/profile/mysql/olat.local.properties
@@ -105,3 +105,7 @@ base.security.contentSecurityPolicy.frameSrc=http://lti.frentix.com
 
 #access control
 method.paypal.checkout.enabled=true
+
+#virtual class rooms
+vc.bigbluebutton.enabled=true
+
diff --git a/src/test/profile/oracle/olat.local.properties b/src/test/profile/oracle/olat.local.properties
index 4f4b9bc35aa..ce397f135ac 100644
--- a/src/test/profile/oracle/olat.local.properties
+++ b/src/test/profile/oracle/olat.local.properties
@@ -109,4 +109,7 @@ base.security.contentSecurityPolicy=enabled
 base.security.contentSecurityPolicy.frameSrc=http://lti.frentix.com
 
 #access control
-method.paypal.checkout.enabled=true
\ No newline at end of file
+method.paypal.checkout.enabled=true
+
+#virtual class rooms
+vc.bigbluebutton.enabled=true
diff --git a/src/test/profile/postgresql/olat.local.properties b/src/test/profile/postgresql/olat.local.properties
index 9ea8f403e16..07c42d88ea1 100644
--- a/src/test/profile/postgresql/olat.local.properties
+++ b/src/test/profile/postgresql/olat.local.properties
@@ -104,4 +104,7 @@ base.security.contentSecurityPolicy=enabled
 base.security.contentSecurityPolicy.frameSrc=http://lti.frentix.com
 
 #access control
-method.paypal.checkout.enabled=true
\ No newline at end of file
+method.paypal.checkout.enabled=true
+
+#virtual class rooms
+vc.bigbluebutton.enabled=true
-- 
GitLab