diff --git a/src/main/java/org/olat/core/configuration/AbstractSpringModule.java b/src/main/java/org/olat/core/configuration/AbstractSpringModule.java
index 1c7d64c6986115cd3564139dc24a1f28c7a459c7..4859bdce0e59c3a9722d3c1a5e00f7deb3783d02 100644
--- a/src/main/java/org/olat/core/configuration/AbstractSpringModule.java
+++ b/src/main/java/org/olat/core/configuration/AbstractSpringModule.java
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.olat.core.gui.control.Event;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.event.GenericEventListener;
 import org.springframework.beans.factory.DisposableBean;
@@ -99,6 +100,9 @@ public abstract class AbstractSpringModule implements GenericEventListener, Init
 
 	@Override
 	public void afterPropertiesSet()  {
+		if (!StringHelper.containsNonWhitespace(userDataDirectory)) {
+			userDataDirectory = System.getProperty("java.io.tmpdir") + "/olatdata";
+		}
 		moduleConfigProperties.setUserDataDirectory(userDataDirectory);
 		moduleConfigProperties.init();
 		initDefaultProperties();
diff --git a/src/main/java/org/olat/ims/qti/qpool/QTI12ItemFactory.java b/src/main/java/org/olat/ims/qti/qpool/QTI12ItemFactory.java
index 0645b197e2cb7b74f9a2b67aa9d0bc070909cfec..14d93f248b1df63165043d7df934cdf935be49de 100644
--- a/src/main/java/org/olat/ims/qti/qpool/QTI12ItemFactory.java
+++ b/src/main/java/org/olat/ims/qti/qpool/QTI12ItemFactory.java
@@ -44,6 +44,11 @@ public class QTI12ItemFactory implements QItemFactory {
 	public QTI12ItemFactory(Type type) {
 		this.type = type;
 	}
+	
+	@Override
+	public String getType() {
+		return "qti12_".concat(type.name());
+	}
 
 	@Override
 	public String getLabel(Locale locale) {
@@ -61,8 +66,7 @@ public class QTI12ItemFactory implements QItemFactory {
 	@Override
 	public QuestionItem createItem(Identity owner, String title, Locale locale) {
 		QTIQPoolServiceProvider spi = CoreSpringFactory.getImpl(QTIQPoolServiceProvider.class);
-		QuestionItem newItem = spi.createItem(owner, type, title, locale);
-		return newItem;
+		return spi.createItem(owner, type, title, locale);
 	}
 	
 	public enum Type {
diff --git a/src/main/java/org/olat/ims/qti21/pool/QTI21AssessmentItemFactory.java b/src/main/java/org/olat/ims/qti21/pool/QTI21AssessmentItemFactory.java
index af8dfaf19d8681698feef51da8c230c7f7dfc69b..55d451bcf4429593032793b65b00ffbb9ec5dd64 100644
--- a/src/main/java/org/olat/ims/qti21/pool/QTI21AssessmentItemFactory.java
+++ b/src/main/java/org/olat/ims/qti21/pool/QTI21AssessmentItemFactory.java
@@ -43,6 +43,11 @@ public class QTI21AssessmentItemFactory implements QItemFactory {
 	public QTI21AssessmentItemFactory(QTI21QuestionType type) {
 		this.type = type;
 	}
+	
+	@Override
+	public String getType() {
+		return "qti21_".concat(type.name());
+	}
 
 	@Override
 	public String getLabel(Locale locale) {
diff --git a/src/main/java/org/olat/modules/qpool/QItemFactory.java b/src/main/java/org/olat/modules/qpool/QItemFactory.java
index 138ac1d9c1caa6b130dca6d87bd33c7ec5531515..8293bc404c39379578962505a8d5c45f2ff2004e 100644
--- a/src/main/java/org/olat/modules/qpool/QItemFactory.java
+++ b/src/main/java/org/olat/modules/qpool/QItemFactory.java
@@ -31,6 +31,8 @@ import org.olat.core.id.Identity;
  */
 public interface QItemFactory {
 	
+	public String getType();
+	
 	public String getLabel(Locale locale);
 	
 	public QuestionItem createItem(Identity owner, String title, Locale locale);
diff --git a/src/main/java/org/olat/modules/qpool/site/QuestionPoolSite.java b/src/main/java/org/olat/modules/qpool/site/QuestionPoolSite.java
index 64ffbcc5720c16f08b4347eb4676318bc111b823..b967c5862bd18d6caf76abf7773463c5d9f5eae4 100644
--- a/src/main/java/org/olat/modules/qpool/site/QuestionPoolSite.java
+++ b/src/main/java/org/olat/modules/qpool/site/QuestionPoolSite.java
@@ -67,8 +67,7 @@ public class QuestionPoolSite extends AbstractSiteInstance {
 		OLATResourceable ores = OresHelper.createOLATResourceableInstance("QPool", 0l);
 		ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
 		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ureq, ores, new StateSite(this), wControl, true);
-		MainLayoutController c = new QuestionPoolSiteMainController(ureq, bwControl);
-		return c;
+		return new QuestionPoolSiteMainController(ureq, bwControl);
 	}
 	
 	@Override
diff --git a/src/main/java/org/olat/modules/qpool/ui/NewItemOptionsController.java b/src/main/java/org/olat/modules/qpool/ui/NewItemOptionsController.java
index 696753f3295f4e483ba73d8466c44b8cc2e876d3..be794ba96b2e29315bff10d1e3a75e7e5b7a16b8 100644
--- a/src/main/java/org/olat/modules/qpool/ui/NewItemOptionsController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/NewItemOptionsController.java
@@ -85,8 +85,10 @@ public class NewItemOptionsController extends FormBasicController {
 	
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		formLayout.setElementCssClass("o_sel_new_item_form");
 		//title
 		titleEl = uifactory.addTextElement("general.title", "general.title", 128, "", formLayout);
+		titleEl.setElementCssClass("o_sel_item_title");
 
 		//type
 		List<QItemFactory> factories = new ArrayList<>();
@@ -100,7 +102,7 @@ public class NewItemOptionsController extends FormBasicController {
 		String[] typeKeys = new String[factories.size()];
 		String[] valueKeys = new String[factories.size()];
 		for(QItemFactory factory:factories) {
-			String typeKey = "item.type." + count;
+			String typeKey = factory.getType();
 			typeKeys[count] = typeKey;
 			keyToFactoryMap.put(typeKey, factory);
 			valueKeys[count] = factory.getLabel(getLocale());
@@ -108,6 +110,7 @@ public class NewItemOptionsController extends FormBasicController {
 		}
 
 		typeEl = uifactory.addDropdownSingleselect("question.type", "menu.admin.types", formLayout, typeKeys, valueKeys, null);
+		typeEl.setElementCssClass("o_sel_item_type");
 		
 		//subject
 		taxonomyLevelEl = uifactory.addDropdownSingleselect("process.start.review.taxonomy.level", formLayout,
@@ -121,6 +124,7 @@ public class NewItemOptionsController extends FormBasicController {
 			}
 		}
 		taxonomyLevelEl.setVisible(qPoolSecurityCallback.canUseTaxonomy());
+		taxonomyLevelEl.setElementCssClass("o_sel_item_taxonomy_level");
 		
 		FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
 		buttonLayout.setRootForm(mainForm);
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionPoolMainEditorController.java b/src/main/java/org/olat/modules/qpool/ui/QuestionPoolMainEditorController.java
index 103c40ca91f3abfc27bda5827444ace4fe8b6007..09cb270809a0911d8681ed8bf99be7037805af4e 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionPoolMainEditorController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionPoolMainEditorController.java
@@ -117,6 +117,7 @@ public class QuestionPoolMainEditorController extends BasicController implements
 		
 		content = new Panel("list");
 		columnLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), menuTree, content, "qpool");
+		columnLayoutCtr.addCssClassToMain("o_qpool");
 		
 		doSelectControllerTreeNode(ureq, treeModel.getMyNode(), null, null);
 		putInitialPanel(columnLayoutCtr.getInitialComponent());
diff --git a/src/main/java/org/olat/modules/qpool/ui/ReviewController.java b/src/main/java/org/olat/modules/qpool/ui/ReviewController.java
index 44d63127375ad5894700166e002f7c73bee992ae..7b32bca76fd7014dded56df31af62d5e10a53df1 100644
--- a/src/main/java/org/olat/modules/qpool/ui/ReviewController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/ReviewController.java
@@ -56,6 +56,7 @@ public class ReviewController extends FormBasicController {
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		setFormDescription("process.rating.description");
+		formLayout.setElementCssClass("o_sel_qpool_review_form");
 		
 		ratingEl = uifactory.addRatingItem("process.rating", "process.rating", 0f, 5, true, formLayout);
 		
diff --git a/src/main/java/org/olat/modules/qpool/ui/ReviewStartController.java b/src/main/java/org/olat/modules/qpool/ui/ReviewStartController.java
index 9e7092b1d4acafb9388cbce9d1ea47d92b1315fe..1a74e80d66f8fdfac37951078bc375c0c65e6bfb 100644
--- a/src/main/java/org/olat/modules/qpool/ui/ReviewStartController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/ReviewStartController.java
@@ -66,6 +66,7 @@ public class ReviewStartController extends FormBasicController {
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		formLayout.setElementCssClass("o_sel_qpool_confirm_start_form");
 		setFormDescription("process.start.review.description", new String[] {item.getTitle()});
 		
 		qpoolTaxonomyTreeBuilder.loadTaxonomyLevelsSelection(getIdentity(), false, ignoreCompetences);
diff --git a/src/main/java/org/olat/modules/qpool/ui/admin/QuestionPoolAdminConfigurationController.java b/src/main/java/org/olat/modules/qpool/ui/admin/QuestionPoolAdminConfigurationController.java
index 2c797ec56183abc58bc6027830e52ecdd19d5637..3c836752b568850709f91aedf3cc596322007a0a 100644
--- a/src/main/java/org/olat/modules/qpool/ui/admin/QuestionPoolAdminConfigurationController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/admin/QuestionPoolAdminConfigurationController.java
@@ -103,6 +103,7 @@ public class QuestionPoolAdminConfigurationController extends FormBasicControlle
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		FormLayoutContainer moduleCont = FormLayoutContainer.createDefaultFormLayout("module", getTranslator());
+		moduleCont.setElementCssClass("o_sel_qpool_configuration");
 		moduleCont.setFormTitle(translate("admin.configuration.title"));
 		moduleCont.setRootForm(mainForm);
 		formLayout.add("module", moduleCont);
@@ -110,6 +111,7 @@ public class QuestionPoolAdminConfigurationController extends FormBasicControlle
 		String[] onValues = new String[] { translate("on") };
 		reviewProcessEnabledEl = uifactory.addCheckboxesHorizontal("review.process.enabled", moduleCont, onKeys, onValues);
 		reviewProcessEnabledEl.addActionListener(FormEvent.ONCHANGE);
+		reviewProcessEnabledEl.setElementCssClass("o_sel_qpool_review_process");
 		if (qpoolModule.isReviewProcessEnabled()) {
 			reviewProcessEnabledEl.select(onKeys[0], true);
 		}
@@ -196,6 +198,7 @@ public class QuestionPoolAdminConfigurationController extends FormBasicControlle
 		poolManagerRightsEl.select(POOL_MANAGER_EDUCATIONAL_CONTEXT, qpoolModule.isPoolAdminAllowedToConfigEducationalContext());
 		
 		FormLayoutContainer buttonsWrapperCont = FormLayoutContainer.createDefaultFormLayout("global", getTranslator());
+		buttonsWrapperCont.setElementCssClass("o_sel_qpool_buttons");
 		buttonsWrapperCont.setRootForm(mainForm);
 		formLayout.add("buttonsWrapper", buttonsWrapperCont);
 		FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
diff --git a/src/main/java/org/olat/modules/qpool/ui/admin/ReviewProcessActivationController.java b/src/main/java/org/olat/modules/qpool/ui/admin/ReviewProcessActivationController.java
index d9d3cc37da71aed166804e1c0c3f730941b4e1c9..fe95a61f6d16dd317e9f0d0296bb010706fb2c1e 100644
--- a/src/main/java/org/olat/modules/qpool/ui/admin/ReviewProcessActivationController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/admin/ReviewProcessActivationController.java
@@ -51,10 +51,12 @@ public class ReviewProcessActivationController extends FormBasicController {
 	
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		formLayout.setElementCssClass("o_sel_qpool_reset_status_form");
 		uifactory.addStaticTextElement("description", "", translate("review.process.confirm.enable.description"), formLayout);
 		
 		String[] resetValues = new String[] { translate("reset.status") };
 		resetStatusaEl = uifactory.addCheckboxesHorizontal("reset.status", "", formLayout, resetKeys, resetValues);
+		resetStatusaEl.setElementCssClass("o_sel_qpool_reset_status");
 		
 		FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
 		formLayout.add("buttons", buttonLayout);
diff --git a/src/main/java/org/olat/modules/qpool/ui/admin/ReviewProcessAdminController.java b/src/main/java/org/olat/modules/qpool/ui/admin/ReviewProcessAdminController.java
index d89019ebfa1b45ba1d5fdc2b2c6728fb6aff47e7..068f7542c1d80b5393d6e4d5672c4c95e4937904 100644
--- a/src/main/java/org/olat/modules/qpool/ui/admin/ReviewProcessAdminController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/admin/ReviewProcessAdminController.java
@@ -67,6 +67,7 @@ public class ReviewProcessAdminController extends FormBasicController {
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		formLayout.setElementCssClass("o_sel_qpool_review_process_admin");
 		setFormTitle("admin.review.process.title");
 		
 		// decision provider
@@ -98,6 +99,7 @@ public class ReviewProcessAdminController extends FormBasicController {
 		
 		String numberOfRatings = Integer.toString(qpoolModule.getReviewDecisionNumberOfRatings());
 		numberOfRatingsEl = uifactory.addTextElement("number.of.ratings", 5, numberOfRatings, formLayout);
+		numberOfRatingsEl.setElementCssClass("o_sel_qpool_num_of_reviews");
 		numberOfRatingsEl.setMandatory(true);
 		numberOfRatingsEl.setDisplaySize(5);
 
@@ -112,6 +114,7 @@ public class ReviewProcessAdminController extends FormBasicController {
 				translate(FINAL_VISIBILITY_MANAGER),
 				translate(FINAL_VISIBILITY_MANAGER_TEACHER)};
 		finalVisibilityEl = uifactory.addDropdownSingleselect("final.visibility", formLayout, finalVisibilityKeys, finalVisibilityValues);
+		finalVisibilityEl.setElementCssClass("o_sel_qpool_final_visibility");
 		String selectedKey = qpoolModule.isFinalVisibleTeach()? FINAL_VISIBILITY_MANAGER_TEACHER: FINAL_VISIBILITY_MANAGER;
 		finalVisibilityEl.select(selectedKey, true);
 
diff --git a/src/main/java/org/olat/modules/qpool/ui/tree/QuestionPoolMenuTreeModel.java b/src/main/java/org/olat/modules/qpool/ui/tree/QuestionPoolMenuTreeModel.java
index 244cdc15ca8edc0f1e3252fd23d98f5c6806de71..fedaa516fe55d8f7c57a89aa40208ad866e3bfe8 100644
--- a/src/main/java/org/olat/modules/qpool/ui/tree/QuestionPoolMenuTreeModel.java
+++ b/src/main/java/org/olat/modules/qpool/ui/tree/QuestionPoolMenuTreeModel.java
@@ -295,6 +295,7 @@ public class QuestionPoolMenuTreeModel extends GenericTreeModel implements DnDTr
 		if(!taxonomyLevels.isEmpty()) {
 			reviewNode = new GenericTreeNode(translator.translate("menu.review"));
 			reviewNode.setTitle(translator.translate("menu.review"));
+			reviewNode.setCssClass("o_sel_qpool_review_taxonomy_levels");
 			rootNode.addChild(reviewNode);
 			
 			for(TaxonomyLevel taxonomyLevel:taxonomyLevels) {
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/EditTaxonomyLevelController.java b/src/main/java/org/olat/modules/taxonomy/ui/EditTaxonomyLevelController.java
index b26668a01baa1096b725f8509c30448b827cc32c..7f550ac2565f86f451a718590f08756c90c51ec4 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/EditTaxonomyLevelController.java
+++ b/src/main/java/org/olat/modules/taxonomy/ui/EditTaxonomyLevelController.java
@@ -121,6 +121,8 @@ public class EditTaxonomyLevelController extends FormBasicController {
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		formLayout.setElementCssClass("o_sel_taxonomy_level_form");
+		
 		if(level == null || level.getKey() == null) {
 			String[] pathKeys;
 			String[] pathValues;
@@ -141,10 +143,12 @@ public class EditTaxonomyLevelController extends FormBasicController {
 		String identifier = level == null ? "" : level.getIdentifier();
 		identifierEl = uifactory.addTextElement("level.identifier", "level.identifier", 255, identifier, formLayout);
 		identifierEl.setEnabled(!TaxonomyLevelManagedFlag.isManaged(level, TaxonomyLevelManagedFlag.identifier));
+		identifierEl.setElementCssClass("o_sel_taxonomy_level_identifier");
 		identifierEl.setMandatory(true);
 
 		String displayName = level == null ? "" : level.getDisplayName();
 		displayNameEl = uifactory.addTextElement("level.displayname", "level.displayname", 255, displayName, formLayout);
+		displayNameEl.setElementCssClass("o_sel_taxonomy_level_name");
 		displayNameEl.setMandatory(true);
 		displayNameEl.setEnabled(!TaxonomyLevelManagedFlag.isManaged(level, TaxonomyLevelManagedFlag.displayName));
 		if(!StringHelper.containsNonWhitespace(displayName)) {
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelCompetenceController.java b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelCompetenceController.java
index bc27acac2cf27667f843e78024d7eb116bb38c1a..d2b796f23b6455e458699b12b37e8817a15a1903 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelCompetenceController.java
+++ b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelCompetenceController.java
@@ -126,6 +126,7 @@ public class TaxonomyLevelCompetenceController extends FormBasicController {
 				|| !TaxonomyLevelManagedFlag.isManaged(taxonomyLevel, TaxonomyLevelManagedFlag.haveCompetence)
 				|| !TaxonomyLevelManagedFlag.isManaged(taxonomyLevel, TaxonomyLevelManagedFlag.targetCompetence)) {
 			addCompetencesButton = uifactory.addFormLink("add.competences", formLayout, Link.BUTTON);
+			addCompetencesButton.setElementCssClass("o_sel_competence_add");
 			removeCompetencesButton = uifactory.addFormLink("delete", formLayout, Link.BUTTON);
 			multiSelect = true;
 		}
@@ -216,7 +217,7 @@ public class TaxonomyLevelCompetenceController extends FormBasicController {
 			} else if (event instanceof MultiIdentityChosenEvent) {
 				MultiIdentityChosenEvent multiEvent = (MultiIdentityChosenEvent)event;
 				List<Identity> toAdd = multiEvent.getChosenIdentities();
-				if(toAdd.size() > 0) {
+				if(!toAdd.isEmpty()) {
 					doAddCompetence(toAdd, (TaxonomyCompetenceTypes)userSearchCtrl.getUserObject());
 					loadModel();
 				}
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelOverviewController.java b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelOverviewController.java
index 3b57a959d98f3b0a8ffb4446b8f96cb52c6d381e..b46495bbb62fc50e89725cf60f50e93cbd0e3c8c 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelOverviewController.java
+++ b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelOverviewController.java
@@ -26,7 +26,6 @@ import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.components.link.LinkFactory;
-import org.olat.core.gui.components.tabbedpane.TabCreator;
 import org.olat.core.gui.components.tabbedpane.TabbedPane;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Controller;
@@ -86,6 +85,7 @@ public class TaxonomyLevelOverviewController extends BasicController implements
 		actionButton.setIconLeftCSS("o_icon o_icon_actions");
 		
 		tabPane = new TabbedPane("tabs", ureq.getLocale());
+		tabPane.setElementCssClass("o_sel_taxonomy_level_tabs");
 		tabPane.addListener(this);
 		
 		metadataCtrl = new EditTaxonomyLevelController(ureq, getWindowControl(), taxonomyLevel);
@@ -100,22 +100,16 @@ public class TaxonomyLevelOverviewController extends BasicController implements
 	}
 	
 	private void initTabPane() {
-		tabPane.addTab(translate("taxonomy.level.competences"), new TabCreator() {
-			@Override
-			public Component create(UserRequest uureq) {
-				competencesCtrl = new TaxonomyLevelCompetenceController(uureq, getWindowControl(), taxonomyLevel);
-				listenTo(competencesCtrl);
-				return competencesCtrl.getInitialComponent();
-			}
+		tabPane.addTab(translate("taxonomy.level.competences"), uureq -> {
+			competencesCtrl = new TaxonomyLevelCompetenceController(uureq, getWindowControl(), taxonomyLevel);
+			listenTo(competencesCtrl);
+			return competencesCtrl.getInitialComponent();
 		});
 		
-		tabPane.addTab(translate("taxonomy.level.relations"), new TabCreator() {
-			@Override
-			public Component create(UserRequest uureq) {
-				relationsCtrl = new TaxonomyLevelRelationsController(uureq, getWindowControl(), taxonomyLevel);
-				listenTo(relationsCtrl);
-				return relationsCtrl.getInitialComponent();
-			}
+		tabPane.addTab(translate("taxonomy.level.relations"),  uureq -> {
+			relationsCtrl = new TaxonomyLevelRelationsController(uureq, getWindowControl(), taxonomyLevel);
+			listenTo(relationsCtrl);
+			return relationsCtrl.getInitialComponent();
 		});
 	}
 	
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyOverviewController.java b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyOverviewController.java
index bf9750840485e23b900d4e4c6363025e77fbed5a..bb677709a69db8bbebc2e6ae84bdbae6fbf60727 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyOverviewController.java
+++ b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyOverviewController.java
@@ -69,14 +69,18 @@ public class TaxonomyOverviewController extends BasicController implements Bread
 		
 		segmentView = SegmentViewFactory.createSegmentView("segments", mainVC, this);
 		metadataLink = LinkFactory.createLink("taxonomy.metadata", mainVC, this);
+		metadataLink.setElementCssClass("o_sel_taxonomy_metadata");
 		segmentView.addSegment(metadataLink, true);
 		doOpenMetadata(ureq);
 		
 		typesLink = LinkFactory.createLink("taxonomy.types", mainVC, this);
+		typesLink.setElementCssClass("o_sel_taxonomy_types");
 		segmentView.addSegment(typesLink, false);
 		levelsLink = LinkFactory.createLink("taxonomy.levels", mainVC, this);
+		levelsLink.setElementCssClass("o_sel_taxonomy_levels");
 		segmentView.addSegment(levelsLink, false);
 		lostFoundLink = LinkFactory.createLink("taxonomy.lost.found", mainVC, this);
+		lostFoundLink.setElementCssClass("o_sel_taxonomy_lost_found");
 		segmentView.addSegment(lostFoundLink, false);
 
 		putInitialPanel(mainVC);
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableController.java b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableController.java
index 773385b24765dec0e0307d73e0cb7ee0c6c80516..fc855ece13c6c8a8ab33c7356bc64aa2c4fa18dd 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableController.java
+++ b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableController.java
@@ -120,6 +120,7 @@ public class TaxonomyTreeTableController extends FormBasicController implements
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 
 		newLevelButton = uifactory.addFormLink("add.taxonomy.level", formLayout, Link.BUTTON);
+		newLevelButton.setElementCssClass("o_sel_taxonomy_new_level");
 		deleteButton = uifactory.addFormLink("delete", formLayout, Link.BUTTON);
 		mergeButton = uifactory.addFormLink("merge.taxonomy.level", formLayout, Link.BUTTON);
 		typeButton = uifactory.addFormLink("type.taxonomy.level", formLayout, Link.BUTTON);
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/_content/admin_taxonomy_levels.html b/src/main/java/org/olat/modules/taxonomy/ui/_content/admin_taxonomy_levels.html
index 6f81943b9f145f5eaf8b9246b68c8d3ac217d317..c2ba20b7d7534cc09471e8d842f1cf6a63233951 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/_content/admin_taxonomy_levels.html
+++ b/src/main/java/org/olat/modules/taxonomy/ui/_content/admin_taxonomy_levels.html
@@ -1,20 +1,22 @@
-#if($r.available("add.taxonomy.level"))
-<div class="o_button_group o_button_group_right">
-	$r.render("add.taxonomy.level")
-</div>
-#end
-$r.render("table")
-<div class="o_button_group">
-	#if($r.available("type.taxonomy.level"))
-		$r.render("type.taxonomy.level")
-	#end
-	#if($r.available("move.taxonomy.level"))
-		$r.render("move.taxonomy.level")
-	#end
-	#if($r.available("merge.taxonomy.level"))
-		$r.render("merge.taxonomy.level")
-	#end
-	#if($r.available("delete"))
-		$r.render("delete")
+<div class="o_sel_taxonomy_levels_tree">
+	#if($r.available("add.taxonomy.level"))
+	<div class="o_button_group o_button_group_right">
+		$r.render("add.taxonomy.level")
+	</div>
 	#end
+	$r.render("table")
+	<div class="o_button_group">
+		#if($r.available("type.taxonomy.level"))
+			$r.render("type.taxonomy.level")
+		#end
+		#if($r.available("move.taxonomy.level"))
+			$r.render("move.taxonomy.level")
+		#end
+		#if($r.available("merge.taxonomy.level"))
+			$r.render("merge.taxonomy.level")
+		#end
+		#if($r.available("delete"))
+			$r.render("delete")
+		#end
+	</div>
 </div>
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/_content/level_competences.html b/src/main/java/org/olat/modules/taxonomy/ui/_content/level_competences.html
index f02de4b506a215b2a356d08a785db34e0049d492..2a3369ae282a282ba20d9e319d309ae9523fd4ae 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/_content/level_competences.html
+++ b/src/main/java/org/olat/modules/taxonomy/ui/_content/level_competences.html
@@ -1,11 +1,13 @@
-#if($r.available("add.competences"))
-<div class="o_button_group o_button_group_right">
-	$r.render("add.competences")
-</div>
-#end
-$r.render("table")
-#if($r.available("delete"))
-<div class="o_button_group">
-	$r.render("delete")
-</div>
-#end
\ No newline at end of file
+<div class="o_sel_taxonomy_level_competences">
+	#if($r.available("add.competences"))
+	<div class="o_button_group o_button_group_right">
+		$r.render("add.competences")
+	</div>
+	#end
+	$r.render("table")
+	#if($r.available("delete"))
+	<div class="o_button_group">
+		$r.render("delete")
+	</div>
+	#end
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/_content/taxonomy_level_overview.html b/src/main/java/org/olat/modules/taxonomy/ui/_content/taxonomy_level_overview.html
index 45179c50e9812241e7a15569f3e37dead47ef2a2..536b360613d76780554c74d3ac36bb423dacc3a1 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/_content/taxonomy_level_overview.html
+++ b/src/main/java/org/olat/modules/taxonomy/ui/_content/taxonomy_level_overview.html
@@ -3,7 +3,7 @@
 	$r.render("actions")
 </div>
 #end
-<div class="o_form form-horizontal clearfix">
+<div class="o_form form-horizontal o_sel_taxonomy_level_overview clearfix">
 	<div class="form-group clearfix">
 		<div>
 			<label class="control-label col-sm-3" for="tax_level_key">$r.translate("taxonomy.level.key")</label>
diff --git a/src/main/java/org/olat/modules/taxonomy/ui/_content/tools.html b/src/main/java/org/olat/modules/taxonomy/ui/_content/tools.html
index ee364a66e1151caa279c872f06410e111e07e20d..6115e224832012d01a121f0febb05960dd3e7617 100644
--- a/src/main/java/org/olat/modules/taxonomy/ui/_content/tools.html
+++ b/src/main/java/org/olat/modules/taxonomy/ui/_content/tools.html
@@ -1,4 +1,4 @@
-<ul class="o_dropdown list-unstyled">
+<ul class="o_dropdown list-unstyled o_sel_tools">
 #foreach($link in $links)
 	#if($link == "-")
 	<li class="divider"></li>
diff --git a/src/test/java/org/olat/restapi/RepositoryEntryResourceTest.java b/src/test/java/org/olat/restapi/RepositoryEntryResourceTest.java
index ac8253425f1a3dacfe1b5c5b956a9280bba5a07f..b9417e6027defb722f77969826b9648a9d698ebb 100644
--- a/src/test/java/org/olat/restapi/RepositoryEntryResourceTest.java
+++ b/src/test/java/org/olat/restapi/RepositoryEntryResourceTest.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.restapi;
 
 import java.io.File;
diff --git a/src/test/java/org/olat/selenium/QuestionPoolTest.java b/src/test/java/org/olat/selenium/QuestionPoolTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..52839020b0807fbe7b4bfba9f4dec1d6c5ccf122
--- /dev/null
+++ b/src/test/java/org/olat/selenium/QuestionPoolTest.java
@@ -0,0 +1,174 @@
+/**
+ * <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;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.UUID;
+
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.graphene.page.InitialPage;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.olat.ims.qti21.model.QTI21QuestionType;
+import org.olat.modules.taxonomy.TaxonomyCompetenceTypes;
+import org.olat.selenium.page.LoginPage;
+import org.olat.selenium.page.NavigationPage;
+import org.olat.selenium.page.core.AdministrationPage;
+import org.olat.selenium.page.qpool.QuestionPoolPage;
+import org.olat.selenium.page.user.UserToolsPage;
+import org.olat.test.rest.UserRestClient;
+import org.olat.user.restapi.UserVO;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * Test the question pool.
+ * 
+ * 
+ * Initial date: 27 avr. 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@RunWith(Arquillian.class)
+public class QuestionPoolTest extends Deployments {
+	
+	@Drone
+	private WebDriver browser;
+	@ArquillianResource
+	private URL deploymentUrl;
+	@Page
+	private NavigationPage navBar;
+	
+	/**
+	 * Smoke test: an author create a QTI 2.1 question,
+	 * a single choice one.
+	 * 
+	 * @param loginPage
+	 * @throws IOException
+	 * @throws URISyntaxException
+	 */
+	@Test
+	@RunAsClient
+	public void questionPool(@InitialPage LoginPage loginPage)
+	throws IOException, URISyntaxException {
+		UserVO author = new UserRestClient(deploymentUrl).createAuthor("Lili");
+		
+		loginPage
+			.loginAs(author.getLogin(), author.getPassword())
+			.resume();
+		
+		String questionTitle = "SC-" + UUID.randomUUID();
+		QuestionPoolPage questionPool = navBar.assertOnNavigationPage()
+			.openQuestionPool();
+		questionPool
+			.selectMyQuestions()
+			.newQuestion(questionTitle, QTI21QuestionType.sc)
+			.clickToolbarBack()
+			.assertQuestionInList(questionTitle, QTI21QuestionType.sc.name());
+	}
+	
+	/**
+	 * An administrator set up the review process. A question need
+	 * 1 review with more than 2 stars. Than it checks that the taxonomy
+	 * for the question pool as an "At least one" taxonomy level.<br>
+	 * An author create a question. A reviewer reviews the question
+	 * and give it 4 stars. The author see the result.
+	 * 
+	 * @param loginPage
+	 * @throws IOException
+	 * @throws URISyntaxException
+	 */
+	@Test
+	@RunAsClient
+	public void reviewProcess(@InitialPage LoginPage loginPage)
+	throws IOException, URISyntaxException {
+
+		UserVO reviewer = new UserRestClient(deploymentUrl).createAuthor("Albert");
+		
+		loginPage
+			.loginAs("administrator", "openolat")
+			.resume();
+		AdministrationPage administration = new NavigationPage(browser)
+			.openAdministration();
+		// configure the review process
+		administration
+			.openQuestionPool()
+			.enableReviews()
+			.reviewsConfiguration(1, 2);
+		// configure the taxonomy
+		administration
+			.openTaxonomy()
+			.selectTaxonomy("QPOOL")
+			.selectTaxonomyTree()
+			.atLeastOneLevel("at-least-one", "At least one")
+			.selectTaxonomyLevel("at-least-one")
+			.selectCompetence()
+			.addCompetence(reviewer, TaxonomyCompetenceTypes.teach);
+		
+		new UserToolsPage(browser).logout();
+		
+		// author create a question
+		UserVO author = new UserRestClient(deploymentUrl).createAuthor("Leila");
+		
+		loginPage
+			.loginAs(author.getLogin(), author.getPassword())
+			.resume();
+		
+		String questionTitle = "SC-" + UUID.randomUUID();
+		QuestionPoolPage questionPool = navBar.assertOnNavigationPage()
+			.openQuestionPool();
+		questionPool
+			.selectMyQuestions()
+			.newQuestion(questionTitle, QTI21QuestionType.sc)
+			.startReviewProcess()
+			.clickToolbarBack()
+			.assertQuestionInList(questionTitle, QTI21QuestionType.sc.name());
+		// author log out
+		new UserToolsPage(browser).logout();
+		
+		// reviewer make its job
+		loginPage
+			.loginAs(reviewer.getLogin(), reviewer.getPassword())
+			.resume();
+		navBar.assertOnNavigationPage()
+			.openQuestionPool()
+			.selectTaxonomyLevel("At least one")
+			.selectQuestionInList(questionTitle)
+			.assessQuestion(4)
+			.assertFinalOnQuestion();
+
+		new UserToolsPage(browser).logout();
+		
+		// author come to see the result
+		loginPage
+			.loginAs(author.getLogin(), author.getPassword())
+			.resume();
+		navBar.assertOnNavigationPage()
+			.openQuestionPool()
+			.selectMyQuestions()
+			.assertFinalQuestionInList(questionTitle);
+	}
+
+}
diff --git a/src/test/java/org/olat/selenium/page/NavigationPage.java b/src/test/java/org/olat/selenium/page/NavigationPage.java
index 2b17a70d6b37e48271d68d0b3df8eb0620248c56..b6bfcdb5b827e1dbf0401728c452622ec99de8d7 100644
--- a/src/test/java/org/olat/selenium/page/NavigationPage.java
+++ b/src/test/java/org/olat/selenium/page/NavigationPage.java
@@ -27,6 +27,7 @@ import org.olat.selenium.page.core.AdministrationPage;
 import org.olat.selenium.page.course.MyCoursesPage;
 import org.olat.selenium.page.graphene.OOGraphene;
 import org.olat.selenium.page.group.GroupsPage;
+import org.olat.selenium.page.qpool.QuestionPoolPage;
 import org.olat.selenium.page.repository.AuthoringEnvPage;
 import org.olat.selenium.page.repository.CatalogAdminPage;
 import org.olat.selenium.page.repository.CatalogPage;
@@ -47,6 +48,7 @@ public class NavigationPage {
 	
 	private static final By navigationSitesBy = By.cssSelector("ul.o_navbar_sites");
 	private static final By authoringEnvTabBy = By.cssSelector("li.o_site_author_env > a");
+	private static final By questionPoolTabBy = By.cssSelector("li.o_site_qpool > a");
 	private static final By portalBy = By.cssSelector("li.o_site_portal > a");
 	private static final By myCoursesBy = By.cssSelector("li.o_site_repository > a");
 	private static final By userManagementBy = By.cssSelector("li.o_site_useradmin > a");
@@ -83,6 +85,12 @@ public class NavigationPage {
 		return new AuthoringEnvPage(browser);
 	}
 	
+	public QuestionPoolPage openQuestionPool() {
+		navigate(questionPoolTabBy);
+		return new QuestionPoolPage(browser)
+				.assertOnQuestionPool();
+	}
+	
 	public PortalPage openPortal() {
 		navigate(portalBy);
 		return new PortalPage(browser);
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 b7aa74cf1a86fb2ad9662e2894bc826ac28cb196..ea7d3b6ab3f8f182be60b8fcaeadf9bdf2dd0583 100644
--- a/src/test/java/org/olat/selenium/page/core/AdministrationPage.java
+++ b/src/test/java/org/olat/selenium/page/core/AdministrationPage.java
@@ -24,6 +24,8 @@ import java.util.List;
 import org.junit.Assert;
 import org.olat.selenium.page.graphene.OOGraphene;
 import org.olat.selenium.page.lecture.LectureAdminSettingsPage;
+import org.olat.selenium.page.qpool.QuestionPoolAdminPage;
+import org.olat.selenium.page.taxonomy.TaxonomyAdminPage;
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
@@ -163,4 +165,24 @@ public class AdministrationPage {
 		OOGraphene.waitElement(licenseAdminBy, browser);
 		return new LicensesAdminstrationPage(browser);
 	}
+	
+	public QuestionPoolAdminPage openQuestionPool() {
+		selectAssessment();
+		
+		By poolBy = By.cssSelector(".o_sel_qpool span.o_tree_level_label_leaf>a");
+		browser.findElement(poolBy).click();
+		OOGraphene.waitBusy(browser);
+		
+		return new QuestionPoolAdminPage(browser).assertOnConfiguration();
+	}
+	
+	public TaxonomyAdminPage openTaxonomy() {
+		selectModules();
+		
+		By taxonomyBy = By.cssSelector(".o_sel_taxonomy span.o_tree_level_label_leaf>a");
+		browser.findElement(taxonomyBy).click();
+		OOGraphene.waitBusy(browser);
+		
+		return new TaxonomyAdminPage(browser).assertOnTaxonomyList();
+	}
 }
diff --git a/src/test/java/org/olat/selenium/page/qpool/QuestionPoolAdminPage.java b/src/test/java/org/olat/selenium/page/qpool/QuestionPoolAdminPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f2685ff06d5be90351ee31a6c301342f07aee29
--- /dev/null
+++ b/src/test/java/org/olat/selenium/page/qpool/QuestionPoolAdminPage.java
@@ -0,0 +1,96 @@
+/**
+ * <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.qpool;
+
+import org.olat.selenium.page.graphene.OOGraphene;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+/**
+ * 
+ * Initial date: 27 avr. 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QuestionPoolAdminPage {
+	
+	private final WebDriver browser;
+	
+	public QuestionPoolAdminPage(WebDriver browser) {
+		this.browser = browser;
+	}
+	
+	public QuestionPoolAdminPage assertOnConfiguration() {
+		By configBy = By.className("o_sel_qpool_configuration");
+		OOGraphene.waitElement(configBy, browser);
+		return this;
+	}
+	
+	public QuestionPoolAdminPage enableReviews() {
+		assertOnConfiguration();
+		
+		By reviewBy = By.cssSelector(".o_sel_qpool_configuration .o_sel_qpool_review_process input[type='checkbox']");
+		WebElement reviewEl = browser.findElement(reviewBy);
+		String checked = reviewEl.getAttribute("checked");
+		if(checked == null) {
+			OOGraphene.check(reviewEl, Boolean.TRUE);
+			OOGraphene.waitModalDialog(browser);
+			
+			//don't reset the status
+			By statusBy = By.cssSelector("fieldset.o_sel_qpool_reset_status_form .o_sel_qpool_reset_status input[type='checkbox']");
+			WebElement statusEl = browser.findElement(statusBy);
+			OOGraphene.check(statusEl, Boolean.TRUE);
+
+			//engage review process
+			By confirmBy = By.cssSelector("fieldset.o_sel_qpool_reset_status_form button.btn-primary");
+			browser.findElement(confirmBy).click();
+			OOGraphene.waitBusy(browser);
+			OOGraphene.waitModalDialogDisappears(browser);
+			
+			By saveConfigurationBy = By.cssSelector(".o_sel_qpool_buttons button.btn-primary");
+			browser.findElement(saveConfigurationBy).click();
+			OOGraphene.waitBusy(browser);
+		}
+		return this;
+	}
+	
+	public QuestionPoolAdminPage reviewsConfiguration(int numOfReviews, int numOfStars) {
+		By reviewBy = By.xpath("//div[contains(@class,'o_segments')]/a[contains(@onclick,'segment.review.process')]");
+		browser.findElement(reviewBy).click();
+		OOGraphene.waitBusy(browser);
+		
+		By numOfReviewsBy = By.cssSelector("fieldset.o_sel_qpool_review_process_admin .o_sel_qpool_num_of_reviews input[type='text']");
+		OOGraphene.waitElement(numOfReviewsBy, browser);
+		WebElement numOfReviewEl = browser.findElement(numOfReviewsBy);
+		numOfReviewEl.clear();
+		numOfReviewEl.sendKeys(Integer.toString(numOfReviews));
+		
+		By numOfStarsBy = By.xpath("//div[contains(@class,'o_rating')]/div[contains(@class,'o_rating_items')]/a[" + numOfStars + "]");
+		browser.findElement(numOfStarsBy).click();
+		OOGraphene.waitBusy(browser);
+		
+		By saveBy = By.cssSelector("fieldset.o_sel_qpool_review_process_admin button.btn-primary");
+		browser.findElement(saveBy).click();
+		OOGraphene.waitBusy(browser);
+		return this;
+	}
+	
+}
diff --git a/src/test/java/org/olat/selenium/page/qpool/QuestionPoolPage.java b/src/test/java/org/olat/selenium/page/qpool/QuestionPoolPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..b383e6e2570b58f67fc5ae6c50eaa0e8190d7f7a
--- /dev/null
+++ b/src/test/java/org/olat/selenium/page/qpool/QuestionPoolPage.java
@@ -0,0 +1,183 @@
+/**
+ * <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.qpool;
+
+import org.olat.ims.qti21.model.QTI21QuestionType;
+import org.olat.selenium.page.NavigationPage;
+import org.olat.selenium.page.graphene.OOGraphene;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * 
+ * Initial date: 26 avr. 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QuestionPoolPage {
+	
+	private final WebDriver browser;
+	
+	public QuestionPoolPage(WebDriver browser) {
+		this.browser = browser;
+	}
+	
+	public QuestionPoolPage assertOnQuestionPool() {
+		By mainBy = By.className("o_qpool");
+		OOGraphene.waitElement(mainBy, browser);
+		return this;
+	}
+	
+	/**
+	 * Select the node my questions.
+	 * 
+	 * @return Itself
+	 */
+	public QuestionPoolPage selectMyQuestions() {
+		By myQuestionsBy = By.xpath("//div[contains(@class,'o_tree')]/ul[@class='o_tree_l0']//a[i[contains(@class,'o_sel_qpool_my_items')]]");
+		OOGraphene.waitElement(myQuestionsBy, browser);
+		browser.findElement(myQuestionsBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitElement(By.id("qitems"), browser);
+		return this;
+	}
+	
+	/**
+	 * Select a node in the review process.
+	 * 
+	 * @param name The name of the taxonomy level
+	 * @return Itself
+	 */
+	public QuestionPoolPage selectTaxonomyLevel(String name) {
+		By leafBy = By.xpath("//li[contains(@class,'o_sel_qpool_review_taxonomy_levels')]/ul/li/div[contains(@class,'o_tree_l1')]/span/a[contains(@onclick,'ctncl')][span[text()[contains(.,'" + name + "')]]]");
+		OOGraphene.waitElement(leafBy, browser);
+		browser.findElement(leafBy).click();
+		OOGraphene.waitBusy(browser);
+		return this;
+	}
+	
+	/**
+	 * Select a question in a list.
+	 * 
+	 * @param title The title of the question
+	 * @return Itself
+	 */
+	public QuestionPoolPage selectQuestionInList(String title) {
+		By rowBy = By.xpath("//div[contains(@class,'o_table_flexi')]//tr[td[text()[contains(.,'" + title + "')]]]/td/a[contains(@href,'select-item')]");
+		OOGraphene.waitElement(rowBy, browser);
+		browser.findElement(rowBy).click();
+		OOGraphene.waitBusy(browser);
+		return this;
+	}
+	
+	public QuestionPoolPage assessQuestion(int numOfStars) {
+		By assessmentBy = By.xpath("//div[contains(@class,'o_instruction')]/div[@class='o_button_group']/a[contains(@onclick,'process.activate.review')]");
+		OOGraphene.waitElement(assessmentBy, browser);
+		browser.findElement(assessmentBy).click();
+		OOGraphene.waitModalDialog(browser);
+		
+		By numOfStarsBy = By.xpath("//fieldset[contains(@class,'o_sel_qpool_review_form')]//div[contains(@class,'o_rating')]/div[contains(@class,'o_rating_items')]/a[" + numOfStars + "]");
+		OOGraphene.waitElement(numOfStarsBy, browser);
+		browser.findElement(numOfStarsBy).click();
+		OOGraphene.waitBusy(browser);
+		
+		By saveBy = By.cssSelector("div.modal-content fieldset.o_sel_qpool_review_form button.btn.btn-primary");
+		browser.findElement(saveBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitModalDialogDisappears(browser);
+		return this;
+	}
+	
+	public QuestionPoolPage newQuestion(String title, QTI21QuestionType type) {
+		By newQuestionBy = By.xpath("//a[i[contains(@class,'o_icon_qitem_new')]]");
+		browser.findElement(newQuestionBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitModalDialog(browser);
+		
+		By titleBy = By.cssSelector("fieldset.o_sel_new_item_form div.o_sel_item_title input[type='text']");
+		browser.findElement(titleBy).sendKeys(title);
+		
+		By typeBy = By.cssSelector("fieldset.o_sel_new_item_form div.o_sel_item_type select");
+		WebElement typeEl = browser.findElement(typeBy);
+		new Select(typeEl).selectByValue("qti21_" + type.name());
+		
+		By saveBy = By.cssSelector("fieldset.o_sel_new_item_form button.btn-primary");
+		browser.findElement(saveBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitModalDialogDisappears(browser);
+		
+		By assessmentItemBy = By.cssSelector("ul.o_sel_assessment_item_config");
+		OOGraphene.waitElement(assessmentItemBy, browser);
+		return this;
+	}
+	
+	/**
+	 * Start the process, confirm with the first taxonomy level found.
+	 * 
+	 * @return Itself
+	 */
+	public QuestionPoolPage startReviewProcess() {
+		By startProcessBy = By.xpath("//div[contains(@class,'o_button_group')]/a[contains(@onclick,'process.activate.start.review')]");
+		browser.findElement(startProcessBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitModalDialog(browser);
+		
+		By confirmBy = By.cssSelector("fieldset.o_sel_qpool_confirm_start_form button.btn.btn-primary");
+		OOGraphene.waitElement(confirmBy, browser);
+		browser.findElement(confirmBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitModalDialogDisappears(browser);
+		return this;
+	}
+	
+	public QuestionPoolPage assertQuestionInList(String title, String type) {
+		By rowBy = By.xpath("//div[contains(@class,'o_table_flexi')]//tr[td[text()='" + type + "']]/td[text()[contains(.,'" + title + "')]]");
+		OOGraphene.waitElement(rowBy, browser);
+		return this;
+	}
+	
+	/**
+	 * Check if the mark "Final" is visible in the toolbar.
+	 * 
+	 * @return Itself
+	 */
+	public QuestionPoolPage assertFinalOnQuestion() {
+		By finalBy = By.cssSelector("li.o_tool a.o_qpool_tools_status.o_qpool_status_finalVersion");
+		OOGraphene.waitElement(finalBy, browser);
+		return this;
+	}
+	
+	public QuestionPoolPage assertFinalQuestionInList(String title) {
+		By rowBy = By.xpath("//div[contains(@class,'o_table_flexi')]//tr[td/div/span[contains(@class,'o_qpool_status_finalVersion_light')]]/td[text()[contains(.,'" + title + "')]]");
+		OOGraphene.waitElement(rowBy, browser);
+		return this;
+	}
+	
+	public QuestionPoolPage clickToolbarBack() {
+		browser.findElement(NavigationPage.toolbarBackBy).click();
+		OOGraphene.waitBusy(browser);
+		
+		By tableBy = By.cssSelector("div.o_table_flexi.o_rendertype_classic");
+		OOGraphene.waitElement(tableBy, browser);
+		return this;
+	}
+}
diff --git a/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyAdminPage.java b/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyAdminPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a72c1a02671bf674112a3f1c988d948bf40d87f
--- /dev/null
+++ b/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyAdminPage.java
@@ -0,0 +1,53 @@
+/**
+ * <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.taxonomy;
+
+import org.olat.selenium.page.graphene.OOGraphene;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * 
+ * Initial date: 11 mai 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class TaxonomyAdminPage {
+
+	private final WebDriver browser;
+	
+	public TaxonomyAdminPage(WebDriver browser) {
+		this.browser = browser;
+	}
+	
+	public TaxonomyAdminPage assertOnTaxonomyList() {
+		By taxonomyListBy = By.className("o_taxonomy_listing");
+		OOGraphene.waitElement(taxonomyListBy, browser);
+		return this;
+	}
+	
+	public TaxonomyPage selectTaxonomy(String identifier) {
+		By selectBy = By.xpath("//div[@class='o_taxonomy_row'][div/div/h4/small[text()[contains(.,'" + identifier + "')]]]/div/div[@class='panel-body']/div[@class='pull-right']/a");
+		browser.findElement(selectBy).click();
+		OOGraphene.waitBusy(browser);
+		return new TaxonomyPage(browser);
+	}
+
+}
diff --git a/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyLevelPage.java b/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyLevelPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc269250e3af3addd4fd4ed10ed9db2a770f01b0
--- /dev/null
+++ b/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyLevelPage.java
@@ -0,0 +1,101 @@
+/**
+ * <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.taxonomy;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.olat.modules.taxonomy.TaxonomyCompetenceTypes;
+import org.olat.selenium.page.graphene.OOGraphene;
+import org.olat.selenium.page.group.MembersWizardPage;
+import org.olat.user.restapi.UserVO;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+/**
+ * 
+ * Initial date: 22 mai 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class TaxonomyLevelPage {
+	
+	private final WebDriver browser;
+	
+	public TaxonomyLevelPage(WebDriver browser) {
+		this.browser = browser;
+	}
+	
+	public TaxonomyLevelPage assertOnTaxonomyLevel() {
+		By overviewBy = By.cssSelector("div.o_sel_taxonomy_level_overview");
+		OOGraphene.waitElement(overviewBy, browser);
+		return this;
+	}
+	
+	public TaxonomyLevelPage selectCompetence() {
+		By configBy = By.className("o_sel_taxonomy_level_competences");
+		return selectTab(configBy);
+	}
+	
+	public TaxonomyLevelPage addCompetence(UserVO user, TaxonomyCompetenceTypes competence) {
+		// open callout
+		By addCompetenceBy = By.cssSelector("a.o_sel_competence_add");
+		browser.findElement(addCompetenceBy).click();
+		OOGraphene.waitBusy(browser);
+		//choose competence
+		OOGraphene.waitElement(By.cssSelector("div.popover-content ul.o_sel_tools"), browser);
+		By competenceBy = By.xpath("//ul[contains(@class,'o_sel_tools')]/li/a[contains(@onclick,'add.competence." + competence + "')]");
+		browser.findElement(competenceBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitModalDialog(browser);
+		
+		MembersWizardPage members = new MembersWizardPage(browser);
+		members.searchMember(user, true);
+		OOGraphene.waitBusy(browser);
+		
+		By chooseBy = By.xpath("//fieldset[@class='o_sel_usersearch_searchform']//div[@class='o_table_buttons']/button[@name='msc']");
+		browser.findElement(chooseBy).click();
+		OOGraphene.waitModalDialogDisappears(browser);
+		
+		return this;
+	}
+	
+	private TaxonomyLevelPage selectTab(By tabBy) {
+		By tabLinkBy = By.cssSelector("ul.o_sel_taxonomy_level_tabs>li>a");
+		List<WebElement> tabLinks = browser.findElements(tabLinkBy);
+
+		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;
+	}
+
+}
diff --git a/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyPage.java b/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..e941f3f36c1ca00944edbbfa538efee60d79d56c
--- /dev/null
+++ b/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyPage.java
@@ -0,0 +1,52 @@
+/**
+ * <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.taxonomy;
+
+import org.olat.selenium.page.graphene.OOGraphene;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * 
+ * Initial date: 11 mai 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class TaxonomyPage {
+	
+	private final WebDriver browser;
+	
+	public TaxonomyPage(WebDriver browser) {
+		this.browser = browser;
+	}
+	
+	/**
+	 * Select the tab to manage the taxonomy levels.
+	 * 
+	 * @return The taxonomy tree page
+	 */
+	public TaxonomyTreePage selectTaxonomyTree() {
+		By selectLevelsBy = By.cssSelector("a.o_sel_taxonomy_levels");
+		OOGraphene.waitElement(selectLevelsBy, browser);
+		browser.findElement(selectLevelsBy).click();
+		return new TaxonomyTreePage(browser).assertOnTaxonomyTree();
+	}
+
+}
diff --git a/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyTreePage.java b/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyTreePage.java
new file mode 100644
index 0000000000000000000000000000000000000000..4adc6985025307f695ba2da0ced299687c84d937
--- /dev/null
+++ b/src/test/java/org/olat/selenium/page/taxonomy/TaxonomyTreePage.java
@@ -0,0 +1,89 @@
+/**
+ * <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.taxonomy;
+
+import java.util.List;
+
+import org.olat.selenium.page.graphene.OOGraphene;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+/**
+ * 
+ * Initial date: 11 mai 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class TaxonomyTreePage {
+	
+	private final WebDriver browser;
+	
+	public TaxonomyTreePage(WebDriver browser) {
+		this.browser = browser;
+	}
+	
+	public TaxonomyTreePage assertOnTaxonomyTree() {
+		By levelsTreeBy = By.className("o_sel_taxonomy_levels_tree");
+		OOGraphene.waitElement(levelsTreeBy, browser);
+		return this;
+	}
+	
+	/**
+	 * Check if there is at least one level. If not, create one.
+	 * 
+	 * @return Itself
+	 */
+	public TaxonomyTreePage atLeastOneLevel(String identifier, String name) {
+		By tableBy = By.xpath("//div[contains(@class,'o_sel_taxonomy_levels_tree')]//table//tr/td[text()[contains(.,'" + identifier + "')]]");
+		List<WebElement> tableEls = browser.findElements(tableBy);
+		if(tableEls.isEmpty()) {
+			newLevel(identifier, name);
+		}
+		return this;
+	}
+	
+	public TaxonomyLevelPage selectTaxonomyLevel(String identifier) {
+		By selectBy = By.xpath("//div[contains(@class,'o_sel_taxonomy_levels_tree')]//table//tr[td[text()[contains(.,'" + identifier + "')]]]/td/a[contains(@href,'select')]");
+		browser.findElement(selectBy).click();
+		OOGraphene.waitBusy(browser);
+		return new TaxonomyLevelPage(browser).assertOnTaxonomyLevel();
+	}
+	
+	public TaxonomyTreePage newLevel(String identifier, String name) {
+		By newLevelBy = By.cssSelector("div.o_sel_taxonomy_levels_tree a.o_sel_taxonomy_new_level");
+		browser.findElement(newLevelBy).click();
+		OOGraphene.waitModalDialog(browser);
+		
+		By identifierBy = By.cssSelector("div.o_sel_taxonomy_level_identifier input[type='text']");
+		OOGraphene.waitElement(identifierBy, browser);
+		browser.findElement(identifierBy).sendKeys(identifier);
+		
+		By nameBy = By.cssSelector("div.o_sel_taxonomy_level_name input[type='text']");
+		browser.findElement(nameBy).sendKeys(name);
+		
+		By saveBy = By.cssSelector("fieldset.o_sel_taxonomy_level_form button.btn.btn-primary");
+		browser.findElement(saveBy).click();
+		OOGraphene.waitBusy(browser);
+		OOGraphene.waitModalDialogDisappears(browser);
+		return this;
+	}
+
+}
diff --git a/src/test/java/org/olat/test/rest/UserRestClient.java b/src/test/java/org/olat/test/rest/UserRestClient.java
index 24d1f9d9b8b512c5b51dac3b294b2b869ba8df2b..2189df8b3afb40f5704d021bd77a98e7e014dbad 100644
--- a/src/test/java/org/olat/test/rest/UserRestClient.java
+++ b/src/test/java/org/olat/test/rest/UserRestClient.java
@@ -97,14 +97,22 @@ public class UserRestClient {
 		
 		RolesVO roles = new RolesVO();
 		roles.setAuthor(true);
+		updateRoles(restConnection, user, roles);
+
+		restConnection.shutdown();
+		return user;
+	}
+	
+	public UserVO createPoolManager(String name)
+	throws IOException, URISyntaxException {
+		RestConnection restConnection = new RestConnection(deploymentUrl);
+		assertTrue(restConnection.login(username, password));
 		
-		//update roles of author
-		URI request = getUsersURIBuilder().path(user.getKey().toString()).path("roles").build();
-		HttpPost method = restConnection.createPost(request, MediaType.APPLICATION_JSON);
-		restConnection.addJsonEntity(method, roles);
-		HttpResponse response = restConnection.execute(method);
-		Assert.assertEquals(200, response.getStatusLine().getStatusCode());
-		EntityUtils.consume(response.getEntity());
+		UserVO user = createUser(restConnection, name, "Auth");
+		
+		RolesVO roles = new RolesVO();
+		roles.setPoolAdmin(true);
+		updateRoles(restConnection, user, roles);
 
 		restConnection.shutdown();
 		return user;
@@ -144,6 +152,20 @@ public class UserRestClient {
 		return current;
 	}
 	
+	/**
+	 * Update roles
+	 */
+	private void updateRoles(RestConnection restConnection, UserVO user, RolesVO roles)
+	throws URISyntaxException, IOException {
+		//update roles of pool manager
+		URI request = getUsersURIBuilder().path(user.getKey().toString()).path("roles").build();
+		HttpPost method = restConnection.createPost(request, MediaType.APPLICATION_JSON);
+		restConnection.addJsonEntity(method, roles);
+		HttpResponse response = restConnection.execute(method);
+		Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+		EntityUtils.consume(response.getEntity());
+	}
+	
 	private UriBuilder getUsersURIBuilder()
 	throws URISyntaxException {
 		return UriBuilder.fromUri(deploymentUrl.toURI()).path("restapi").path("users");