From 41f4f22a03053caa992d5ba757fc9ad95760bea8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Kr=C3=A4hemann?= <joel.kraehemann@frentix.com>
Date: Mon, 25 Feb 2013 11:45:32 +0100
Subject: [PATCH] FXOLAT-208,OO-425: fixed changed location of
 tree-node-loading class and changed return value open method within
 FunctionalCourseUtil. Implemented some utility methods.

---
 .../org/olat/course/FunctionalCourseTest.java |   2 +-
 .../course/nodes/cp/FunctionalCPTest.java     |   2 +-
 .../course/nodes/feed/FunctionalBlogTest.java |   6 +-
 .../nodes/feed/FunctionalPodcastTest.java     |   4 +-
 .../course/nodes/iq/FunctionalIQTestTest.java |   2 +-
 .../portfolio/FunctionalPortfolioTest.java    |   2 +-
 .../course/nodes/wiki/FunctionalWikiTest.java |   2 +-
 .../repository/FunctionalRepositoryTest.java  | 231 +++++++++++++++++-
 .../org/olat/util/FunctionalCourseUtil.java   | 148 ++++++++++-
 .../olat/util/FunctionalEPortfolioUtil.java   |   4 +-
 .../olat/util/FunctionalGroupsSiteUtil.java   |   5 +
 .../util/FunctionalRepositorySiteUtil.java    | 134 +++++++++-
 .../java/org/olat/util/FunctionalVOUtil.java  |  12 +
 13 files changed, 512 insertions(+), 42 deletions(-)

diff --git a/src/test/java/org/olat/course/FunctionalCourseTest.java b/src/test/java/org/olat/course/FunctionalCourseTest.java
index 7ed8babd719..e319d809cb0 100644
--- a/src/test/java/org/olat/course/FunctionalCourseTest.java
+++ b/src/test/java/org/olat/course/FunctionalCourseTest.java
@@ -285,7 +285,7 @@ public class FunctionalCourseTest {
 		Assert.assertTrue(functionalRepositorySiteUtil.createCourseUsingWizard(browser, WIZARD_COURSE_TITLE, WIZARD_COURSE_DESCRIPTION,
 				elementArray, null, true, CourseWizardAccess.USERS));
 		
-		Assert.assertTrue(functionalCourseUtil.open(browser, 1));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, 1));
 		
 		for(File currentFile: largeFile){
 			Assert.assertTrue(functionalCourseUtil.uploadFileToCourseFolder(browser, currentFile.toURI()));
diff --git a/src/test/java/org/olat/course/nodes/cp/FunctionalCPTest.java b/src/test/java/org/olat/course/nodes/cp/FunctionalCPTest.java
index e17e8aefc93..f09ea4ed273 100644
--- a/src/test/java/org/olat/course/nodes/cp/FunctionalCPTest.java
+++ b/src/test/java/org/olat/course/nodes/cp/FunctionalCPTest.java
@@ -103,7 +103,7 @@ public class FunctionalCPTest {
 		
 		Assert.assertTrue(functionalCourseUtil.publishEntireCourse(browser, null, null));
 		
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 	}
 
 }
diff --git a/src/test/java/org/olat/course/nodes/feed/FunctionalBlogTest.java b/src/test/java/org/olat/course/nodes/feed/FunctionalBlogTest.java
index 750323b44f8..45950c8c965 100644
--- a/src/test/java/org/olat/course/nodes/feed/FunctionalBlogTest.java
+++ b/src/test/java/org/olat/course/nodes/feed/FunctionalBlogTest.java
@@ -146,11 +146,11 @@ public class FunctionalBlogTest {
 		Assert.assertTrue(functionalCourseUtil.publishEntireCourse(browser, null, null));
 		
 		/* import empty feed */
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 		Assert.assertTrue(functionalCourseUtil.importBlogFeed(browser, BLOG_FEED));
 		
 		/* blog should be accessible */
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 		
 
 		Assert.assertTrue(functionalUtil.logout(browser));
@@ -348,7 +348,7 @@ public class FunctionalBlogTest {
 		Assert.assertTrue(functionalCourseUtil.publishEntireCourse(browser, null, null));
 		
 		/* create content */
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 		Assert.assertTrue(functionalCourseUtil.createBlogEntry(browser, course.getRepoEntryKey(), 0,
 				CONCURRENT_RW_BLOG_SHORT_TITLE, CONCURRENT_RW_BLOG_DESCRIPTION, CONCURRENT_RW_BLOG_CONTENT));
 		
diff --git a/src/test/java/org/olat/course/nodes/feed/FunctionalPodcastTest.java b/src/test/java/org/olat/course/nodes/feed/FunctionalPodcastTest.java
index b4ccb171400..070d1d9e00f 100644
--- a/src/test/java/org/olat/course/nodes/feed/FunctionalPodcastTest.java
+++ b/src/test/java/org/olat/course/nodes/feed/FunctionalPodcastTest.java
@@ -105,9 +105,9 @@ public class FunctionalPodcastTest {
 		
 		Assert.assertTrue(functionalCourseUtil.publishEntireCourse(browser, null, null));
 		
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 		Assert.assertTrue(functionalCourseUtil.importPodcastFeed(browser, PODCAST_FEED));
 		
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 	}
 }
diff --git a/src/test/java/org/olat/course/nodes/iq/FunctionalIQTestTest.java b/src/test/java/org/olat/course/nodes/iq/FunctionalIQTestTest.java
index 27b91c554fc..cf180a81913 100644
--- a/src/test/java/org/olat/course/nodes/iq/FunctionalIQTestTest.java
+++ b/src/test/java/org/olat/course/nodes/iq/FunctionalIQTestTest.java
@@ -110,6 +110,6 @@ public class FunctionalIQTestTest {
 		/* close editor, open course and click nth course node */
 		Assert.assertTrue(functionalCourseUtil.closeActiveTab(browser));
 		Assert.assertTrue(functionalRepositorySiteUtil.openCourse(browser, course.getRepoEntryKey()));
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 	}
 }
diff --git a/src/test/java/org/olat/course/nodes/portfolio/FunctionalPortfolioTest.java b/src/test/java/org/olat/course/nodes/portfolio/FunctionalPortfolioTest.java
index 92ab5b2efcf..1cb5a466e3c 100644
--- a/src/test/java/org/olat/course/nodes/portfolio/FunctionalPortfolioTest.java
+++ b/src/test/java/org/olat/course/nodes/portfolio/FunctionalPortfolioTest.java
@@ -125,6 +125,6 @@ public class FunctionalPortfolioTest {
 		Assert.assertTrue(functionalCourseUtil.openCourseEditor(browser));
 		Assert.assertTrue(functionalCourseUtil.publishEntireCourse(browser, null, null));
 		
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 	}
 }
diff --git a/src/test/java/org/olat/course/nodes/wiki/FunctionalWikiTest.java b/src/test/java/org/olat/course/nodes/wiki/FunctionalWikiTest.java
index d7021cd6267..06e7a8fb83c 100644
--- a/src/test/java/org/olat/course/nodes/wiki/FunctionalWikiTest.java
+++ b/src/test/java/org/olat/course/nodes/wiki/FunctionalWikiTest.java
@@ -104,6 +104,6 @@ public class FunctionalWikiTest {
 		
 		Assert.assertTrue(functionalCourseUtil.publishEntireCourse(browser, null, null));
 		
-		Assert.assertTrue(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
+		Assert.assertNotNull(functionalCourseUtil.open(browser, course.getRepoEntryKey(), 0));
 	}
 }
diff --git a/src/test/java/org/olat/repository/FunctionalRepositoryTest.java b/src/test/java/org/olat/repository/FunctionalRepositoryTest.java
index bc703be17a0..8dc8c16d960 100644
--- a/src/test/java/org/olat/repository/FunctionalRepositoryTest.java
+++ b/src/test/java/org/olat/repository/FunctionalRepositoryTest.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.List;
+import java.util.UUID;
 
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.container.test.api.RunAsClient;
@@ -39,9 +40,18 @@ import org.olat.restapi.support.vo.GroupVO;
 import org.olat.restapi.support.vo.RepositoryEntryVO;
 import org.olat.test.ArquillianDeployments;
 import org.olat.user.restapi.UserVO;
+import org.olat.util.FunctionalCourseUtil;
+import org.olat.util.FunctionalCourseUtil.CourseEditorBlogTab;
+import org.olat.util.FunctionalCourseUtil.CourseEditorCpTab;
+import org.olat.util.FunctionalCourseUtil.CourseEditorIQTestTab;
+import org.olat.util.FunctionalCourseUtil.CourseEditorPodcastTab;
+import org.olat.util.FunctionalCourseUtil.CourseEditorPortfolioTaskTab;
+import org.olat.util.FunctionalCourseUtil.CourseEditorWikiTab;
+import org.olat.util.FunctionalCourseUtil.CourseNodeAlias;
 import org.olat.util.FunctionalRepositorySiteUtil;
 import org.olat.util.FunctionalUtil;
 import org.olat.util.FunctionalVOUtil;
+import org.olat.util.browser.Tutor1;
 
 import com.thoughtworks.selenium.DefaultSelenium;
 
@@ -66,7 +76,8 @@ public class FunctionalRepositoryTest {
 
 	static FunctionalUtil functionalUtil;
 	static FunctionalRepositorySiteUtil functionalRepositorySiteUtil;
-
+	static FunctionalCourseUtil functionalCourseUtil;
+	
 	static FunctionalVOUtil functionalVOUtil;
 	
 	static boolean initialized = false;
@@ -79,6 +90,8 @@ public class FunctionalRepositoryTest {
 
 			functionalRepositorySiteUtil = functionalUtil.getFunctionalRepositorySiteUtil();
 			
+			functionalCourseUtil = new FunctionalCourseUtil(functionalUtil, functionalRepositorySiteUtil);
+			
 			functionalVOUtil = new FunctionalVOUtil(functionalUtil.getUsername(), functionalUtil.getPassword());
 			
 			initialized = true;
@@ -88,21 +101,25 @@ public class FunctionalRepositoryTest {
 	@Ignore
 	@Test
 	@RunAsClient
-	public void checkCopyCourse() throws URISyntaxException, IOException{
+	public void checkCopyCourse(@Drone @Tutor1 DefaultSelenium tutor0) throws URISyntaxException, IOException{
 		/*
 		 * prerequisites for test created via REST
 		 */
 		/* create tutor */
 		UserVO tutorVO = functionalVOUtil.createTestAuthors(deploymentUrl, 1).get(0);
 		
+		/* create groups */
+		List<GroupVO> groupVO = functionalVOUtil.createTestCourseGroups(deploymentUrl, 3);
+		
 		/* import course */
-		CourseVO courseVO = functionalVOUtil.importAllElementsCourse(deploymentUrl);
+		String courseName = UUID.randomUUID().toString();
+		CourseVO courseVO = functionalVOUtil.importAllElementsCourse(deploymentUrl, courseName, courseName);
 		
 		RepositoryEntryVO repoEntryVO = functionalVOUtil.getRepositoryEntryByKey(deploymentUrl, courseVO.getRepoEntryKey());
 		functionalVOUtil.addOwnerToRepositoryEntry(deploymentUrl, repoEntryVO, tutorVO);
 		
-		/* create groups */
-		List<GroupVO> groupVO = functionalVOUtil.createTestCourseGroups(deploymentUrl, 3);
+		/* set rights */
+		//TODO:JK: implement me
 		
 		/* create learning areas */
 		//TODO:JK: implement me
@@ -110,12 +127,212 @@ public class FunctionalRepositoryTest {
 		/*
 		 * create or configure content
 		 */
-		//TODO:JK: implement me
+		/* login */
+		functionalUtil.login(tutor0, tutorVO.getLogin(), tutorVO.getPassword(), true);
+		
+		/* open detailed view of course */
+		functionalRepositorySiteUtil.openDetailedView(tutor0, courseName, 1);
+		
+		/* copy resource */
+		String newCourseName = "Copy " + courseName;
+		functionalRepositorySiteUtil.copyRepositoryEntry(tutor0, newCourseName, courseName);
 		
 		/*
 		 * verify content
 		 */
-		//TODO:JK: implement me
+		/* open copied course */
+		functionalRepositorySiteUtil.openCourse(tutor0, newCourseName, 0);
+		
+		/* open course editor */
+		functionalCourseUtil.openCourseEditor(tutor0);
+		
+		/* click all course nodes */
+		int count = functionalCourseUtil.count(tutor0, true, true, false);
 		
+		for(int i = 0; i < count; i++){
+			/* open course node */
+			CourseNodeAlias alias = functionalCourseUtil.open(tutor0, i);
+			
+			/**/
+			switch(alias){
+			case CP:
+			{
+				
+				
+				/* verify visibility */
+				//TODO:JK: implement me
+				
+				/* verify access */
+				//TODO:JK: implement me
+				
+				/* verify rules */
+				//TODO:JK: implement me
+			}
+			break;
+			case BLOG:
+			{
+				/* verify visibility */
+				//TODO:JK: implement me
+				
+				/* verify access */
+				//TODO:JK: implement me
+				
+				/* verify rules */
+				//TODO:JK: implement me
+			}
+			break;
+			case PODCAST:
+			{
+				/* verify visibility */
+				//TODO:JK: implement me
+				
+				/* verify access */
+				//TODO:JK: implement me
+				
+				/* verify rules */
+				//TODO:JK: implement me
+			}
+			break;
+			case WIKI:
+			{
+				/* verify visibility */
+				//TODO:JK: implement me
+				
+				/* verify access */
+				//TODO:JK: implement me
+				
+				/* verify rules */
+				//TODO:JK: implement me
+			}
+			break;
+			case PORTFOLIO_TASK:
+			{
+				/* verify visibility */
+				//TODO:JK: implement me
+				
+				/* verify access */
+				//TODO:JK: implement me
+				
+				/* verify rules */
+				//TODO:JK: implement me
+			}
+			break;
+			case IQ_TEST:
+			{
+				/* verify visibility */
+				//TODO:JK: implement me
+				
+				/* verify access */
+				//TODO:JK: implement me
+				
+				/* verify rules */
+				//TODO:JK: implement me
+			}
+			break;
+			case IQ_SELFTEST:
+			{
+				/* verify visibility */
+				//TODO:JK: implement me
+				
+				/* verify access */
+				//TODO:JK: implement me
+				
+				/* verify rules */
+				//TODO:JK: implement me
+			}
+			break;
+			case IQ_QUESTIONAIRE:
+			{
+				/* verify visibility */
+				//TODO:JK: implement me
+				
+				/* verify access */
+				//TODO:JK: implement me
+				
+				/* verify rules */
+				//TODO:JK: implement me
+			}
+			break;
+			}
+			
+		}
+		
+		/* click all learning resources */
+		count = functionalCourseUtil.count(tutor0, true, false, false);
+		
+		for(int i = 0; i < count; i++){
+			/* open learning resource */
+			CourseNodeAlias alias = functionalCourseUtil.openLearningResource(tutor0, i);
+			
+			/* open learning content tab */
+			switch(alias){
+			case CP:
+			{
+				functionalCourseUtil.openCourseEditorCpTab(tutor0, CourseEditorCpTab.LEARNING_CONTENT);
+				
+				/* verify resource IDs */
+				//TODO:JK: implement me
+			}
+			break;
+			case BLOG:
+			{
+				functionalCourseUtil.openCourseEditorBlogTab(tutor0, CourseEditorBlogTab.LEARNING_CONTENT);
+				
+				/* verify resource IDs */
+				//TODO:JK: implement me
+			}
+			break;
+			case PODCAST:
+			{
+				functionalCourseUtil.openCourseEditorPodcastTab(tutor0, CourseEditorPodcastTab.LEARNING_CONTENT);
+				
+				/* verify resource IDs */
+				//TODO:JK: implement me
+			}
+			break;
+			case WIKI:
+			{
+				functionalCourseUtil.openCourseEditorWikiTab(tutor0, CourseEditorWikiTab.LEARNING_CONTENT);
+
+				/* verify resource IDs */
+				//TODO:JK: implement me
+			}
+			break;
+			case PORTFOLIO_TASK:
+			{
+				functionalCourseUtil.openCourseEditorPortfolioTaskTab(tutor0, CourseEditorPortfolioTaskTab.LEARNING_CONTENT);
+
+				/* verify resource IDs */
+				//TODO:JK: implement me
+			}
+			break;
+			case IQ_TEST:
+			{
+				functionalCourseUtil.openCourseEditorIQTestTab(tutor0, CourseEditorIQTestTab.TEST_CONFIGURATION);
+
+				/* verify resource IDs */
+				//TODO:JK: implement me
+			}
+			break;
+			case IQ_SELFTEST:
+			{
+				functionalCourseUtil.openCourseEditorIQTestTab(tutor0, CourseEditorIQTestTab.TEST_CONFIGURATION);
+
+				/* verify resource IDs */
+				//TODO:JK: implement me
+			}
+			break;
+			case IQ_QUESTIONAIRE:
+			{
+				functionalCourseUtil.openCourseEditorIQTestTab(tutor0, CourseEditorIQTestTab.TEST_CONFIGURATION);
+
+				/* verify resource IDs */
+				//TODO:JK: implement me
+			}
+			break;
+			}
+		}
+		
+		//TODO:JK: implement me
 	}
 }
diff --git a/src/test/java/org/olat/util/FunctionalCourseUtil.java b/src/test/java/org/olat/util/FunctionalCourseUtil.java
index bb4d46d4805..798cde38450 100644
--- a/src/test/java/org/olat/util/FunctionalCourseUtil.java
+++ b/src/test/java/org/olat/util/FunctionalCourseUtil.java
@@ -20,6 +20,7 @@
 package org.olat.util;
 
 import java.io.IOException;
+import java.io.StringWriter;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.util.ArrayList;
@@ -27,6 +28,11 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.commons.lang.ArrayUtils;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 
@@ -192,14 +198,29 @@ public class FunctionalCourseUtil {
 	}
 	
 	public enum CourseNodeAlias {
-		CP("o_cp_icon"),
+		STRUCTURE("o_st_icon"), // NOT really a course node
+		SP("o_sp_icon"),
+		FORUM("o_fo_icon"),
+		WIKI("o_wiki_icon"),
 		BLOG("o_blog_icon"),
+		CP("o_cp_icon"),
+		SCORM("o_scorm_icon"),
+		BC("o_bc_icon"),
 		PODCAST("o_podcast_icon"),
-		WIKI("o_wiki_icon"),
+		DIALOG("o_dialog_icon"),
+		ASSESSMENT("o_ms_icon"),
+		TASK("o_ta_icon"),
 		PORTFOLIO_TASK("o_ep_icon"),
 		IQ_TEST("o_iqtest_icon"),
 		IQ_SELFTEST("o_iqself_icon"),
-		IQ_QUESTIONAIRE("o_iqsurv_icon");
+		IQ_QUESTIONAIRE("o_iqsurv_icon"),
+		ENROLMENT("o_en_icon"),
+		PROJECT_BROKER("o_projectbroker_icon"),
+		NOTIFICATIONS("o_infomsg_icon"),
+		MEMBERS("o_cmembers_icon"),
+		CALENDAR("o_cal_icon"),
+		LINK_LIST("o_ll_icon"),
+		CONTACT("o_co_icon");
 		
 		private String iconCss;
 		
@@ -443,37 +464,126 @@ public class FunctionalCourseUtil {
 	 * @param nth
 	 * @return true on success otherwise false
 	 */
-	public boolean open(Selenium browser, long courseId, int nth){
+	public CourseNodeAlias open(Selenium browser, long courseId, int nth){
 		if(!functionalRepositorySiteUtil.openCourse(browser, courseId))
-			return(false);
+			return(null);
 		
 		return(open(browser, nth));
 	}
+
+	/**
+	 * Counts the course nodes.
+	 * 
+	 * @param browser
+	 * @param learningResources
+	 * @param courseNodes
+	 * @param structuralNodes
+	 * @return
+	 */
+	public int count(Selenium browser, boolean learningResources, boolean courseNodes, boolean structuralNodes){
+		int count = -1;
+		
+		//TODO:JK: implement me
+		
+		return(count);
+	}
+	
+	/**
+	 * Returns the matching CourseNodeAlias of corresponding iconCss String.
+	 * 
+	 * @param iconCss
+	 * @return
+	 */
+	public static CourseNodeAlias findCourseNodeAliasOfIconCss(String iconCss){
+
+		if(iconCss == null){
+			return (null);
+		}
+		
+		CourseNodeAlias alias = null;
+		
+		if(iconCss.contains(CourseNodeAlias.SP.getIconCss())){
+			alias = CourseNodeAlias.SP;
+		}else if(iconCss.contains(CourseNodeAlias.FORUM.getIconCss())){
+			alias = CourseNodeAlias.FORUM;
+		}else if(iconCss.contains(CourseNodeAlias.WIKI.getIconCss())){
+			alias = CourseNodeAlias.WIKI;
+		}else if(iconCss.contains(CourseNodeAlias.BLOG.getIconCss())){
+			alias = CourseNodeAlias.BLOG;
+		}else if(iconCss.contains(CourseNodeAlias.CP.getIconCss())){
+			alias = CourseNodeAlias.CP;
+		}else if(iconCss.contains(CourseNodeAlias.SCORM.getIconCss())){
+			alias = CourseNodeAlias.SCORM;
+		}else if(iconCss.contains(CourseNodeAlias.BC.getIconCss())){
+			alias = CourseNodeAlias.BC;
+		}else if(iconCss.contains(CourseNodeAlias.PODCAST.getIconCss())){
+			alias = CourseNodeAlias.PODCAST;
+		}else if(iconCss.contains(CourseNodeAlias.DIALOG.getIconCss())){
+			alias = CourseNodeAlias.DIALOG;
+		}else if(iconCss.contains(CourseNodeAlias.ASSESSMENT.getIconCss())){
+			alias = CourseNodeAlias.ASSESSMENT;
+		}else if(iconCss.contains(CourseNodeAlias.TASK.getIconCss())){
+			alias = CourseNodeAlias.TASK;
+		}else if(iconCss.contains(CourseNodeAlias.PORTFOLIO_TASK.getIconCss())){
+			alias = CourseNodeAlias.PORTFOLIO_TASK;
+		}else if(iconCss.contains(CourseNodeAlias.IQ_TEST.getIconCss())){
+			alias = CourseNodeAlias.IQ_TEST;
+		}else if(iconCss.contains(CourseNodeAlias.IQ_SELFTEST.getIconCss())){
+			alias = CourseNodeAlias.IQ_SELFTEST;
+		}else if(iconCss.contains(CourseNodeAlias.IQ_QUESTIONAIRE.getIconCss())){
+			alias = CourseNodeAlias.IQ_QUESTIONAIRE;
+		}else if(iconCss.contains(CourseNodeAlias.ENROLMENT.getIconCss())){
+			alias = CourseNodeAlias.ENROLMENT;
+		}else if(iconCss.contains(CourseNodeAlias.PROJECT_BROKER.getIconCss())){
+			alias = CourseNodeAlias.PROJECT_BROKER;
+		}else if(iconCss.contains(CourseNodeAlias.NOTIFICATIONS.getIconCss())){
+			alias = CourseNodeAlias.NOTIFICATIONS;
+		}else if(iconCss.contains(CourseNodeAlias.MEMBERS.getIconCss())){
+			alias = CourseNodeAlias.MEMBERS;
+		}else if(iconCss.contains(CourseNodeAlias.CALENDAR.getIconCss())){
+			alias = CourseNodeAlias.CALENDAR;
+		}else if(iconCss.contains(CourseNodeAlias.LINK_LIST.getIconCss())){
+			alias = CourseNodeAlias.LINK_LIST;
+		}else if(iconCss.contains(CourseNodeAlias.CONTACT.getIconCss())){
+			alias = CourseNodeAlias.CONTACT;
+		}
+		
+		return(alias);
+	}
 	
 	/**
 	 * Opens the nth course element in the current course.
 	 * 
 	 * @param browser
 	 * @param nth
-	 * @return true on success
+	 * @return the matching CourseNodeAlias
 	 */
-	public boolean open(Selenium browser, int nth){
+	public CourseNodeAlias open(Selenium browser, int nth){
 		functionalUtil.idle(browser);
 
 		StringBuffer selectorBuffer = new StringBuffer();
 
 		if(nth == -1){
 			selectorBuffer.append("xpath=(//ul[contains(@class, 'b_tree_l0')]//li)//a");
+			
+			functionalUtil.waitForPageToLoadElement(browser, selectorBuffer.toString());
+			browser.click(selectorBuffer.toString());
+			
+			return(CourseNodeAlias.STRUCTURE);
 		}else{
 			selectorBuffer.append("xpath=(//ul[contains(@class, 'b_tree_l1')]//li)[")
 			.append(nth + 1)
 			.append("]//a");
-		}
-
-		functionalUtil.waitForPageToLoadElement(browser, selectorBuffer.toString());
-		browser.click(selectorBuffer.toString());
 		
-		return(true);
+	
+			functionalUtil.waitForPageToLoadElement(browser, selectorBuffer.toString());
+			browser.click(selectorBuffer.toString());
+			
+			functionalUtil.idle(browser);
+			String alias = browser.getAttribute(selectorBuffer.toString() + "@class");
+			
+			return(findCourseNodeAliasOfIconCss(alias));
+		}
 	}
 	
 	/**
@@ -503,6 +613,19 @@ public class FunctionalCourseUtil {
 		return(true);
 	}
 	
+	/**
+	 * Opens the nth learning resource.
+	 * 
+	 * @param browser
+	 * @param nth
+	 * @return The matching CSS CourseNodeAlias
+	 */
+	public CourseNodeAlias openLearningResource(Selenium browser, int nth){
+		//TODO:JK: implement me
+		
+		return(null);
+	}
+	
 	/**
 	 * Opens the nth course element within the specified course
 	 * without using business paths.
@@ -848,6 +971,7 @@ public class FunctionalCourseUtil {
 			String[] catalogSelectors = createCatalogSelectors(catalog);
 			
 			for(String catalogSelector: catalogSelectors){
+				functionalUtil.idle(browser);
 				functionalUtil.waitForPageToLoadElement(browser, catalogSelector);
 
 				if(browser.isElementPresent(catalogSelector + "/../img[contains(@class, 'x-tree-elbow-end-plus')]")){
diff --git a/src/test/java/org/olat/util/FunctionalEPortfolioUtil.java b/src/test/java/org/olat/util/FunctionalEPortfolioUtil.java
index 6586e92d933..741202ae5f4 100644
--- a/src/test/java/org/olat/util/FunctionalEPortfolioUtil.java
+++ b/src/test/java/org/olat/util/FunctionalEPortfolioUtil.java
@@ -585,7 +585,7 @@ public class FunctionalEPortfolioUtil {
 		/* wait until tree has loaded */
 		StringBuffer locatorBuffer = new StringBuffer();
 		
-		locatorBuffer.append("xpath=//li[contains(@class, '")
+		locatorBuffer.append("xpath=//div[contains(@class, '")
 		.append(functionalUtil.getTreeNodeLoadingCss())
 		.append("')]");
 		functionalUtil.waitForPageToUnloadElement(browser, locatorBuffer.toString());
@@ -804,7 +804,7 @@ public class FunctionalEPortfolioUtil {
 		
 		StringBuffer locatorBuffer = new StringBuffer();
 		
-		locatorBuffer.append("xpath=//li[contains(@class, '")
+		locatorBuffer.append("xpath=//div[contains(@class, '")
 		.append(functionalUtil.getTreeNodeLoadingCss())
 		.append("')]");
 		functionalUtil.waitForPageToUnloadElement(browser, locatorBuffer.toString());
diff --git a/src/test/java/org/olat/util/FunctionalGroupsSiteUtil.java b/src/test/java/org/olat/util/FunctionalGroupsSiteUtil.java
index af983076646..bb42031b4f2 100644
--- a/src/test/java/org/olat/util/FunctionalGroupsSiteUtil.java
+++ b/src/test/java/org/olat/util/FunctionalGroupsSiteUtil.java
@@ -667,22 +667,27 @@ public class FunctionalGroupsSiteUtil {
 		
 		if(ArrayUtils.contains(conf, MembersConfiguration.CAN_SEE_COACHES)){
 			functionalUtil.clickCheckbox(browser, null, MembersConfiguration.CAN_SEE_COACHES.getValue());
+			functionalUtil.idle(browser);
 		}
 		
 		if(ArrayUtils.contains(conf, MembersConfiguration.CAN_SEE_PARTICIPANTS)){
 			functionalUtil.clickCheckbox(browser, null, MembersConfiguration.CAN_SEE_PARTICIPANTS.getValue());
+			functionalUtil.idle(browser);
 		}
 
 		if(ArrayUtils.contains(conf, MembersConfiguration.ALL_CAN_SEE_COACHES)){
 			functionalUtil.clickCheckbox(browser, null, MembersConfiguration.ALL_CAN_SEE_COACHES.getValue());
+			functionalUtil.idle(browser);
 		}
 
 		if(ArrayUtils.contains(conf, MembersConfiguration.ALL_CAN_SEE_PARTICIPANTS)){
 			functionalUtil.clickCheckbox(browser, null, MembersConfiguration.ALL_CAN_SEE_PARTICIPANTS.getValue());
+			functionalUtil.idle(browser);
 		}
 		
 		if(ArrayUtils.contains(conf, MembersConfiguration.ALL_CAN_DOWNLOAD_LIST_OF_MEMBERS)){
 			functionalUtil.clickCheckbox(browser, null, MembersConfiguration.ALL_CAN_DOWNLOAD_LIST_OF_MEMBERS.getValue());
+			functionalUtil.idle(browser);
 		}
 		
 		return(true);
diff --git a/src/test/java/org/olat/util/FunctionalRepositorySiteUtil.java b/src/test/java/org/olat/util/FunctionalRepositorySiteUtil.java
index 526c99a60df..bfd885a75ab 100644
--- a/src/test/java/org/olat/util/FunctionalRepositorySiteUtil.java
+++ b/src/test/java/org/olat/util/FunctionalRepositorySiteUtil.java
@@ -236,6 +236,27 @@ public class FunctionalRepositorySiteUtil {
 		}
 	}
 	
+	//TODO:JK: add CSS classes
+	public enum DetailedViewAction{
+		CLOSE(null),
+		COPY(null),
+		DELETE(null);
+		
+		private String actionCss;
+		
+		DetailedViewAction(String actionCss){
+			setActionCss(actionCss);
+		}
+
+		public String getActionCss() {
+			return actionCss;
+		}
+
+		public void setActionCss(String actionCss) {
+			this.actionCss = actionCss;
+		}
+	}
+	
 	public final static String TOOLBOX_CONTENT_CSS = "b_toolbox_content";
 	public final static String TOOLBOX_COURSE_CSS = "o_toolbox_course";
 	public final static String TOOLBOX_CONTENT_PACKAGE_CSS = "o_toolbox_content";
@@ -799,16 +820,55 @@ public class FunctionalRepositorySiteUtil {
 	}
 	
 	/**
-	 * Opens the appropriate detailed view.
+	 * Opens a course by its title. The nth search result will be opened.
 	 * 
 	 * @param browser
-	 * @param key
+	 * @param title
+	 * @param nth
 	 * @return
 	 */
-	public boolean openDetailedView(Selenium browser, Long key){
-		//TODO:JK: implement me
+	public boolean openCourse(Selenium browser, String title, int nth){
+		if(!functionalUtil.openSite(browser, OlatSite.LEARNING_RESOURCES))
+			return(false);
+
+		if(!openActionByMenuTree(browser, RepositorySiteAction.SEARCH_FORM))
+			return(false);
+
+		functionalUtil.idle(browser);
 		
-		return(false);
+		//FIXME:JK: use CSS classes instead of ordinal
+		int searchFormIndex = 0;
+
+		/* open search form */
+		functionalUtil.typeText(browser, SearchField.TITLE_OF_LEARNING_RESOURCE.getEntryCss(), title);
+
+		/* click search */
+		StringBuffer selectorBuffer = new StringBuffer();
+
+		selectorBuffer.append("xpath=//form[")
+		.append(searchFormIndex + 1)
+		.append("]")
+		.append("//div[@class='b_form_element']")
+		.append("//a[@class='b_button']");
+
+		browser.click(selectorBuffer.toString());
+		functionalUtil.waitForPageToLoad(browser);
+
+		/* click course */
+		functionalUtil.idle(browser);
+		
+		selectorBuffer = new StringBuffer();
+
+		selectorBuffer.append("//form")
+		.append("//tr")
+		.append("//td[3]") //FIXME:JK: this isn't very safe
+		.append("//a");
+
+		browser.click(selectorBuffer.toString());
+
+		waitForPageToLoadCourse(browser);
+		
+		return(true);
 	}
 	
 	/**
@@ -871,6 +931,18 @@ public class FunctionalRepositorySiteUtil {
 		return(true);
 	}
 	
+	/**
+	 * Opens the appropriate detailed view.
+	 * 
+	 * @param browser
+	 * @param key
+	 * @return
+	 */
+	public boolean openDetailedView(Selenium browser, Long key){
+		//TODO:JK: implement me
+		
+		return(false);
+	}
 	
 	/**
 	 * Opens the detail view of a specified learning resource.
@@ -880,7 +952,7 @@ public class FunctionalRepositorySiteUtil {
 	 * @param nth
 	 * @return true on success
 	 */
-	public boolean openDetailView(Selenium browser, String title, int nth){
+	public boolean openDetailedView(Selenium browser, String title, int nth){
 		if(!functionalUtil.openSite(browser, OlatSite.LEARNING_RESOURCES))
 			return(false);
 
@@ -890,19 +962,23 @@ public class FunctionalRepositorySiteUtil {
 		int searchFormIndex = 0;
 
 		/* open search form */
-		functionalUtil.typeText(browser, SearchField.ID.getEntryCss(), title);
+		functionalUtil.typeText(browser, SearchField.TITLE_OF_LEARNING_RESOURCE.getEntryCss(), title);
 
 		/* click search */
+		functionalUtil.idle(browser);
+		
 		StringBuffer selectorBuffer = new StringBuffer();
 
 		selectorBuffer.append("xpath=//form[")
 		.append(searchFormIndex + 1)
 		.append("]")
 		.append("//div[@class='b_form_element']")
-		.append("//a[@class='b_button']");
+		.append("//a[contains(@class, '")
+		.append(functionalUtil.getButtonDirtyCss())
+		.append("')]");
 
+		functionalUtil.waitForPageToLoadElement(browser, selectorBuffer.toString());
 		browser.click(selectorBuffer.toString());
-		functionalUtil.waitForPageToLoad(browser);
 
 		/* click course */
 		functionalUtil.idle(browser);
@@ -916,8 +992,8 @@ public class FunctionalRepositorySiteUtil {
 		.append("//td[6]") //FIXME:JK: this isn't very safe
 		.append("//a");
 
+		functionalUtil.waitForPageToLoadElement(browser, selectorBuffer.toString());
 		browser.click(selectorBuffer.toString());
-		functionalUtil.waitForPageToLoad(browser);
 		
 		return(true);
 	}
@@ -939,6 +1015,29 @@ public class FunctionalRepositorySiteUtil {
 	}
 	
 	/**
+	 * Clicks the appropriate action defined by parameter actionCss.
+	 * 
+	 * @param browser
+	 * @param actionCss
+	 * @return
+	 */
+	private boolean clickDetailedViewAction(Selenium browser, String actionCss){
+		functionalUtil.idle(browser);
+		
+		StringBuffer selectorBuffer = new StringBuffer();
+		
+		selectorBuffer.append("xpath=//ul//li//a[contains(@class, '")
+		.append(actionCss)
+		.append("')]");
+		
+		functionalUtil.waitForPageToLoadElement(browser, selectorBuffer.toString());
+		browser.click(selectorBuffer.toString());
+		
+		return(true);
+	}
+	
+	/**
+	 * Fills in title and description of a newly created repository entry.
 	 * 
 	 * @param browser
 	 * @param title
@@ -983,7 +1082,7 @@ public class FunctionalRepositorySiteUtil {
 		if(!clickCreate(browser, getCreateBlogCss()))
 			throw(new IOException("can't open create wizard of blog"));
 		
-		if(!openDetailView(browser, title, 0))
+		if(!openDetailedView(browser, title, 0))
 			throw(new IOException("can't open detail view"));
 			
 		long id = readIdFromDetailView(browser);
@@ -1008,6 +1107,7 @@ public class FunctionalRepositorySiteUtil {
 	}
 	
 	/**
+	 * Fills in the title and description of a newly created course.
 	 * 
 	 * @param browser
 	 * @param title
@@ -1196,6 +1296,18 @@ public class FunctionalRepositorySiteUtil {
 		return(true);
 	}
 
+	public boolean copyRepositoryEntry(Selenium browser, String title, String description) {
+		if(!clickDetailedViewAction(browser, DetailedViewAction.COPY.getActionCss())){
+			return(false);
+		}
+		
+		if(!fillInRepositoryEntryPopup(browser, title, description)){
+			return(false);
+		}
+		
+		return(true);
+	}
+	
 	public String getRepositoryPopupCss() {
 		return repositoryPopupCss;
 	}
diff --git a/src/test/java/org/olat/util/FunctionalVOUtil.java b/src/test/java/org/olat/util/FunctionalVOUtil.java
index d27d6bb9f0f..6c5e902f5dd 100644
--- a/src/test/java/org/olat/util/FunctionalVOUtil.java
+++ b/src/test/java/org/olat/util/FunctionalVOUtil.java
@@ -417,6 +417,18 @@ public class FunctionalVOUtil {
 		return(importCourse(deploymentUrl, "/org/olat/course/All_Elements_Course_Without_External_Content.zip", "All_Elements_Course_Without_External_Content.zip", "All Elements Course Without External Content", "All Elements Course Without External Content"));
 	}
 
+	/**
+	 * Imports the "All Elements Course" via REST.
+	 * 
+	 * @param deploymentUrl
+	 * @return
+	 * @throws URISyntaxException
+	 * @throws IOException
+	 */
+	public CourseVO importAllElementsCourse(URL deploymentUrl, String resourceName, String displayName) throws URISyntaxException, IOException{
+		return(importCourse(deploymentUrl, "/org/olat/course/All_Elements_Course_Without_External_Content.zip", "All_Elements_Course_Without_External_Content.zip", resourceName, displayName));
+	}
+	
 	/**
 	 * Imports the "Course including Forum" via REST.
 	 * 
-- 
GitLab