<%if(asset instanceof Folder){%>
<%= LanguageUtil.get(pageContext, "Folder") %>:
<%= APILocator.getIdentifierAPI().find(((Folder) asset).getIdentifier()).getPath() %>
<%}else if(asset instanceof Host){%>
diff --git a/dotcms-integration/pom.xml b/dotcms-integration/pom.xml
index 3c3b088c3ed0..80123dc92e87 100644
--- a/dotcms-integration/pom.xml
+++ b/dotcms-integration/pom.xml
@@ -255,278 +255,278 @@
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- unpack-webapp
-
- unpack
-
- prepare-package
-
-
-
- com.dotcms
- dotcms-core
- ${project.version}
- war
- war
- true
- ${webapp.extract.dir}
-
-
- false
- true
-
-
-
- copy-test-starter
-
- copy
-
- generate-test-resources
-
-
-
- com.dotcms
- starter
- ${starter.run.version}
- zip
- true
- ${starter.download.folder}
- ${starter.run.filename}
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${version.compiler.plugin}
-
-
- maven-resources-plugin
-
-
- copy-resource-one
-
- copy-resources
-
- prepare-package
-
- target/test-classes
-
-
- ${webapp.extract.dir}/WEB-INF/classes
-
- **/*.class
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-antrun-plugin
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ unpack-webapp
+
+ unpack
+
+ prepare-package
+
+
+
+ com.dotcms
+ dotcms-core
+ ${project.version}
+ war
+ war
+ true
+ ${webapp.extract.dir}
+
+
+ false
+ true
+
+
+
+ copy-test-starter
+
+ copy
+
+ generate-test-resources
+
+
+
+ com.dotcms
+ starter
+ ${starter.run.version}
+ zip
+ true
+ ${starter.download.folder}
+ ${starter.run.filename}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${version.compiler.plugin}
+
+
+ maven-resources-plugin
+
+
+ copy-resource-one
+
+ copy-resources
+
+ prepare-package
+
+ target/test-classes
+
+
+ ${webapp.extract.dir}/WEB-INF/classes
+
+ **/*.class
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+ ${coreit.test.skip}
+
+
+
+ cleanup-it-test-data
+
+ run
+
+ pre-integration-test
${coreit.test.skip}
+
+
+
+
+
+
-
-
- cleanup-it-test-data
-
- run
-
- pre-integration-test
-
- ${coreit.test.skip}
-
-
-
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+ ${coreit.test.skip}
+ ${debug.args}
+ ${it.test.forkcount}
+ true
+ 3
+
+
+
+
+ ${failsafe.forkNumber}
+ UTC
+ ${integrationTestCoverageAgent}
+
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.logs.folder}
+
+ f${surefire.forkNumber}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.cache.folder}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.system.cache.folder}
+ ${felix.base.dir}
+ ${felix.system.base.dir}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.load.folder}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.undeployed.folder}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.system.load.folder}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.system.undeployed.folder}
+ ${test.webapp.root}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.assets.folder}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.dynamic.folder}
+ com.dotmarketing.db.SystemEnvDataSourceStrategy
+ org.postgresql.Driver
+ 30000
+ ${test.db.url}
+ ${test.db.username}
+ ${test.db.password}
+ true
+
+ FORCE
+ false
+ false
+ 600
+ ${test.es.endpoints}
+ ${starter.run.path}
+ ${test.webapp.root}/WEB-INF/velocity
+ ${test.webapp.root}/WEB-INF/geoip2/GeoLite2-City.mmdb
+ ${test.webapp.root}/WEB-INF/bin
+ http://localhost:50505/e
+ http://localhost:50505/m
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.temp.folder}
+
+ ${it.test.fork-folder}${surefire.forkNumber}/${test.working.folder}
+ ${project.build.directory}
+
+
+ **/MainSuite1*.java
+ **/MainSuite2*.java
+ **/Junit5Suite*.java
+
+
+
+
+ default
+
+ integration-test
+ verify
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+ ${coreit.test.skip}
+ database,opensearch
+
+
+
+
+
+
+ ${project.basedir}/src/docker-compose/it-test/initdb.sh:/docker-entrypoint-initdb.d/initdb.sh
+
+
+
+
+
+
+
+
+ docker-pre-stop
+
+ stop
+
+ pre-integration-test
+
+ ${coreit.test.skip}
+
+
+
+ docker-pre-remove-volumes
+
+ volume-remove
+
+ pre-integration-test
- true
+ ${coreit.test.skip}
-
-
- org.apache.maven.plugins
- maven-failsafe-plugin
+
+
+ docker-start
+
+ start
+
+ pre-integration-test
${coreit.test.skip}
- ${debug.args}
- ${it.test.forkcount}
- true
- 3
-
-
-
-
- ${failsafe.forkNumber}
- UTC
- ${integrationTestCoverageAgent}
-
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.logs.folder}
-
- f${surefire.forkNumber}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.cache.folder}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.system.cache.folder}
- ${felix.base.dir}
- ${felix.system.base.dir}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.load.folder}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.undeployed.folder}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.system.load.folder}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.felix.system.undeployed.folder}
- ${test.webapp.root}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.assets.folder}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.dynamic.folder}
- com.dotmarketing.db.SystemEnvDataSourceStrategy
- org.postgresql.Driver
- 30000
- ${test.db.url}
- ${test.db.username}
- ${test.db.password}
- true
-
- FORCE
- false
- false
- 600
- ${test.es.endpoints}
- ${starter.run.path}
- ${test.webapp.root}/WEB-INF/velocity
- ${test.webapp.root}/WEB-INF/geoip2/GeoLite2-City.mmdb
- ${test.webapp.root}/WEB-INF/bin
- http://localhost:50505/e
- http://localhost:50505/m
- true
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.temp.folder}
-
- ${it.test.fork-folder}${surefire.forkNumber}/${test.working.folder}
- ${project.build.directory}
-
-
- **/MainSuite1*.java
- **/MainSuite2*.java
- **/Junit5Suite*.java
-
-
-
- default
-
- integration-test
- verify
-
-
-
-
-
- io.fabric8
- docker-maven-plugin
+
+
+ docker-stop
+
+ stop
+
+ post-integration-test
${coreit.test.skip}
- database,opensearch
-
-
-
-
-
-
- ${project.basedir}/src/docker-compose/it-test/initdb.sh:/docker-entrypoint-initdb.d/initdb.sh
-
-
-
-
-
-
-
- docker-pre-stop
-
- stop
-
- pre-integration-test
-
- ${coreit.test.skip}
-
-
-
- docker-pre-remove-volumes
-
- volume-remove
-
- pre-integration-test
-
- ${coreit.test.skip}
-
-
-
- docker-start
-
- start
-
- pre-integration-test
-
- ${coreit.test.skip}
-
-
-
- docker-stop
-
- stop
-
- post-integration-test
-
- ${coreit.test.skip}
-
-
-
- docker-post-remove-volumes
-
- volume-remove
-
- post-integration-test
-
- ${coreit.test.skip}
-
-
-
-
-
-
+
+
+ docker-post-remove-volumes
+
+ volume-remove
+
+ post-integration-test
+
+ ${coreit.test.skip}
+
+
+
+
+
+
+
quick-test
@@ -550,8 +550,5 @@
-
-
-
diff --git a/dotcms-integration/src/test/java/com/dotcms/analytics/track/RequestMatcherTest.java b/dotcms-integration/src/test/java/com/dotcms/analytics/track/RequestMatcherTest.java
index cce2d358a8af..6e64cfa12d45 100644
--- a/dotcms-integration/src/test/java/com/dotcms/analytics/track/RequestMatcherTest.java
+++ b/dotcms-integration/src/test/java/com/dotcms/analytics/track/RequestMatcherTest.java
@@ -1,5 +1,10 @@
package com.dotcms.analytics.track;
+import com.dotcms.analytics.track.matchers.FilesRequestMatcher;
+import com.dotcms.analytics.track.matchers.PagesAndUrlMapsRequestMatcher;
+import com.dotcms.analytics.track.matchers.RequestMatcher;
+import com.dotcms.analytics.track.matchers.RulesRedirectsRequestMatcher;
+import com.dotcms.analytics.track.matchers.VanitiesRequestMatcher;
import com.dotcms.mock.request.DotCMSMockRequest;
import com.dotcms.mock.request.FakeHttpRequest;
import com.dotcms.mock.response.DotCMSMockResponse;
diff --git a/dotcms-integration/src/test/resources/log4j2.xml b/dotcms-integration/src/test/resources/log4j2.xml
index 7a24f4be5efd..1e7cfd2783c4 100644
--- a/dotcms-integration/src/test/resources/log4j2.xml
+++ b/dotcms-integration/src/test/resources/log4j2.xml
@@ -59,8 +59,7 @@
-
+
diff --git a/e2e/dotcms-e2e-java/pom.xml b/e2e/dotcms-e2e-java/pom.xml
new file mode 100644
index 000000000000..46834db84515
--- /dev/null
+++ b/e2e/dotcms-e2e-java/pom.xml
@@ -0,0 +1,268 @@
+
+
+ 4.0.0
+
+
+ com.dotcms
+ dotcms-build-parent
+ ${revision}${sha1}${changelist}
+ ../../build-parent/pom.xml
+
+
+ jar
+ dotcms-e2e-java
+
+
+ UTF-8
+
+ ${ext.docker.image.wiremock}
+ ./src/test/resources
+ /home/wiremock
+ 4
+ true
+ ${e2e.test.forkCount}
+ ${debug.args}
+ true
+ 3
+
+ true
+ 1.10.6
+ true
+ true
+ tomcat:9.0.74-jdk11
+ ${ext.docker.image.wiremock}
+ ./src/test/resources
+ /home/wiremock
+ 50505
+ ${ext.wiremock.api.key}
+ 8080
+ false
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ test
+
+
+ io.vavr
+ vavr
+ test
+
+
+ org.slf4j
+ slf4j-api
+ test
+
+
+ org.apache.logging.log4j
+ log4j-core
+ test
+
+
+ org.apache.logging.log4j
+ log4j-1.2-api
+ test
+
+
+ org.apache.logging.log4j
+ log4j-slf4j2-impl
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ junit
+ junit
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.junit.platform
+ junit-platform-suite
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.junit.platform
+ junit-platform-suite
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+ org.junit.platform
+ junit-platform-suite
+ test
+
+
+ com.microsoft.playwright
+ playwright
+ test
+
+
+
+
+
+
+ com.dotcms
+ dotcms-application-bom
+ ${project.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+ ${e2e.test.skip}
+ ${debug.args}
+ ${e2e.test.forkCount}
+ true
+ 3
+
+ **/E2eTestSuite.java
+
+
+
+
+ default
+
+ integration-test
+ verify
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+ true
+ true
+ ${e2e.test.skip}
+
+
+ ${docker.image.wiremock}
+
+
+ wiremock.port:8080
+
+
+
+ ${docker.wm.volume}:${docker.wm.volume.internal}
+
+
+
+ [WireMock]
+ green
+
+
+
+
+
+
+ -XX:+PrintFlagsFinal
+ 15
+ FORCE
+ false
+ false
+ 600
+ obfuscate_me
+ true
+ http://localhost:8080
+ true
+ true
+ http://wm:8080/c
+ http://wm:8080/i
+ http://wm:8080/e
+ http://wm:8080/m
+
+
+ wiremock:wm
+
+
+
+
+
+
+
+ cleanup-at-start
+
+ stop
+ volume-remove
+
+ pre-integration-test
+
+
+ start
+
+ start
+
+ pre-integration-test
+
+
+ stop
+
+ stop
+
+ verify
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/dotcms-e2e-java/src/main/java/com/dotcms/DummyInterface.java b/e2e/dotcms-e2e-java/src/main/java/com/dotcms/DummyInterface.java
new file mode 100644
index 000000000000..ec9371012d5c
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/main/java/com/dotcms/DummyInterface.java
@@ -0,0 +1,9 @@
+package com.dotcms;
+
+/*
+This interface is here as a placeholder of non test code that is part of the dotcms-e2e module
+Without something in main to compile tests will not run.
+ */
+public interface DummyInterface {
+ public void aMethod();
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/E2eKeys.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/E2eKeys.java
new file mode 100644
index 000000000000..dc72cd205616
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/E2eKeys.java
@@ -0,0 +1,12 @@
+package com.dotcms.e2e;
+
+public class E2eKeys {
+
+ public static final String CI_KEY = "ci";
+ public static final String E2E_BASE_URL_KEY = "baseUrl";
+ public static final String DEFAULT_BASE_URL = "http://localhost:8080";
+ public static final String E2E_BROWSER_KEY = "browser";
+ public static final String DEFAULT_USER_KEY = "default_user";
+ public static final String DEFAULT_PASSWORD_KEY = "default_password";
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/E2eTestSuite.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/E2eTestSuite.java
new file mode 100644
index 000000000000..c667e05f7353
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/E2eTestSuite.java
@@ -0,0 +1,15 @@
+package com.dotcms.e2e;
+
+import com.dotcms.e2e.test.LoginTests;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+@Suite
+@SelectClasses({
+ LoginTests.class/*,
+ ContentPagesTests.class,
+ ContentSearchTests.class,
+ SiteLayoutContainersTests.class*/
+})
+public class E2eTestSuite {
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/browser/BrowserType.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/browser/BrowserType.java
new file mode 100644
index 000000000000..164dd9ab88c6
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/browser/BrowserType.java
@@ -0,0 +1,13 @@
+package com.dotcms.e2e.browser;
+
+import java.util.stream.Stream;
+
+public enum BrowserType {
+
+ CHROMIUM, FIREFOX, WEBKIT;
+
+ public static BrowserType fromString(final String value) {
+ return Stream.of(values()).filter(element -> element.name().equals(value)).findFirst().orElse(null);
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/logging/Logger.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/logging/Logger.java
new file mode 100644
index 000000000000..9762cbba6283
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/logging/Logger.java
@@ -0,0 +1,57 @@
+package com.dotcms.e2e.logging;
+
+import org.slf4j.LoggerFactory;
+
+import java.util.function.Supplier;
+
+public class Logger {
+
+ private Logger() {
+ }
+
+ public static void info(final Class> clazz, final String message) {
+ final org.slf4j.Logger logger = LoggerFactory.getLogger(clazz);
+ logger.info(message);
+ }
+
+ public static void info(final Class> clazz, final Supplier message) {
+ info(clazz, message.get());
+ }
+
+ public static void warn(final Class> clazz, final String message) {
+ final org.slf4j.Logger logger = LoggerFactory.getLogger(clazz);
+ logger.warn(message);
+ }
+
+ public static void warn(final Class> clazz, final Supplier message) {
+ warn(clazz, message.get());
+ }
+
+ public static void warn(final Class> clazz, final String message, final Throwable throwable) {
+ final org.slf4j.Logger logger = LoggerFactory.getLogger(clazz);
+ logger.warn(message, throwable);
+ }
+
+ public static void warn(final Class> clazz, final Supplier message, final Throwable throwable) {
+ warn(clazz, message.get(), throwable);
+ }
+
+ public static void error(final Class> clazz, final String message) {
+ final org.slf4j.Logger logger = LoggerFactory.getLogger(clazz);
+ logger.error(message);
+ }
+
+ public static void error(final Class> clazz, final Supplier message) {
+ error(clazz, message.get());
+ }
+
+ public static void error(final Class> clazz, final String message, final Throwable throwable) {
+ final org.slf4j.Logger logger = LoggerFactory.getLogger(clazz);
+ logger.error(message, throwable);
+ }
+
+ public static void error(final Class> clazz, final Supplier message, final Throwable throwable) {
+ error(clazz, message.get(), throwable);
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/GroupEntries.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/GroupEntries.java
new file mode 100644
index 000000000000..fec5750f8e50
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/GroupEntries.java
@@ -0,0 +1,24 @@
+package com.dotcms.e2e.page;
+
+public enum GroupEntries {
+
+ CONTENT_MENU(":text('format_align_leftContentarrow_drop_up')"),
+ SITE_MENU(":text('folder_openSitearrow_drop_up')"),
+ SITE_LAYOUT(":text('folder_openSite')");
+
+ private final String group;
+
+ GroupEntries(final String group) {
+ this.group = group;
+ }
+
+ /**
+ * Get the group entries
+ *
+ * @return the group entries
+ */
+ public String getGroup() {
+ return group;
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/LoginPage.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/LoginPage.java
new file mode 100644
index 000000000000..ac8e0495346b
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/LoginPage.java
@@ -0,0 +1,49 @@
+package com.dotcms.e2e.page;
+
+import com.dotcms.e2e.E2eKeys;
+import com.dotcms.e2e.service.EnvironmentService;
+import com.microsoft.playwright.Page;
+
+public class LoginPage {
+
+ private static final String USERNAME_LOCATOR = "input[id=inputtext]";
+ private static final String PASSWORD_LOCATOR = "input[id=password]";
+ private static final String LOGIN_BUTTON = "submitButton";
+ private static final String LOGIN_LANGUAGE = "language";
+
+ private final Page loginPage;
+
+ /**
+ * Constructor
+ *
+ * @param loginPage the page object
+ */
+ public LoginPage(final Page loginPage) {
+ this.loginPage = loginPage;
+ }
+
+ public void login(final String user, final String password) {
+ loginPage.locator(USERNAME_LOCATOR).fill(user);
+ loginPage.locator(PASSWORD_LOCATOR).click();
+ loginPage.locator(PASSWORD_LOCATOR).fill(password);
+ loginPage.getByTestId(LOGIN_BUTTON).click();
+ }
+
+ public void successfulLogin() {
+ final EnvironmentService environmentService = EnvironmentService.get();
+ login(
+ environmentService.getProperty(E2eKeys.DEFAULT_USER_KEY),
+ environmentService.getProperty(E2eKeys.DEFAULT_PASSWORD_KEY));
+ }
+
+ /**
+ * Switch the language of the login page
+ *
+ * @param language the language to switch to
+ */
+ public void switchLanguage(final String language) {
+ loginPage.getByTestId(LOGIN_LANGUAGE).click();
+ loginPage.getByLabel(language).click();
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/MenuNavigation.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/MenuNavigation.java
new file mode 100644
index 000000000000..92c0cea2cfa1
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/MenuNavigation.java
@@ -0,0 +1,24 @@
+package com.dotcms.e2e.page;
+
+import com.microsoft.playwright.Page;
+
+public class MenuNavigation {
+
+ private final Page menuNavigation;
+
+ public MenuNavigation(final Page page) {
+ this.menuNavigation = page;
+ }
+
+ /**
+ * Navigate to the given group and tool entries
+ *
+ * @param groupEntries the group entries to navigate to
+ * @param toolEntries the tool entries to navigate to
+ */
+ public void navigateTo(final GroupEntries groupEntries, final ToolEntries toolEntries) {
+ menuNavigation.locator(groupEntries.getGroup()).hover();
+ menuNavigation.locator(toolEntries.getTool()).click();
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/ToolEntries.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/ToolEntries.java
new file mode 100644
index 000000000000..782e8541632d
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/page/ToolEntries.java
@@ -0,0 +1,30 @@
+package com.dotcms.e2e.page;
+
+public enum ToolEntries {
+
+ GETTING_WELCOME("[href='#/starter']"),
+ // Content group locators
+ CONTENT_SEARCH("[href='#/c/content']"),
+ CONTENT_PAGES("[href='#/pages']"),
+ CONTENT_ACTIVITIES("[href='#/c/c_Activities']"),
+ CONTENT_BANNER("[href='#/c/c_Banners']"),
+ //Site group locators
+ SITE_BROWSER("[href='#/c/site-browser']"),
+ LAYOUT_CONTAINERS("[href='#/containers']");
+
+ private final String tool;
+
+ ToolEntries(final String tool) {
+ this.tool = tool;
+ }
+
+ /**
+ * Get the tool entries
+ *
+ * @return the tool entries
+ */
+ public String getTool() {
+ return tool;
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/playwright/PlaywrightSupport.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/playwright/PlaywrightSupport.java
new file mode 100644
index 000000000000..e0312ba7b04e
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/playwright/PlaywrightSupport.java
@@ -0,0 +1,84 @@
+package com.dotcms.e2e.playwright;
+
+import com.dotcms.e2e.E2eKeys;
+import com.dotcms.e2e.browser.BrowserType;
+import com.dotcms.e2e.logging.Logger;
+import com.dotcms.e2e.service.EnvironmentService;
+import com.microsoft.playwright.Browser;
+import com.microsoft.playwright.BrowserContext;
+import com.microsoft.playwright.Page;
+import com.microsoft.playwright.Playwright;
+import com.microsoft.playwright.assertions.LocatorAssertions;
+import io.vavr.Tuple2;
+import io.vavr.control.Try;
+
+import java.util.Objects;
+import java.util.stream.Stream;
+
+public class PlaywrightSupport {
+
+ private static final PlaywrightSupport INSTANCE = new PlaywrightSupport();
+
+ public static PlaywrightSupport get() {
+ return INSTANCE;
+ }
+
+ private PlaywrightSupport() {
+ }
+
+ public Playwright playwright() {
+ return Playwright.create();
+ }
+
+ public boolean isCi() {
+ return Boolean.parseBoolean(EnvironmentService.get().getProperty(E2eKeys.CI_KEY));
+ }
+
+ public String getBrowser() {
+ return EnvironmentService.get().getProperty(E2eKeys.E2E_BROWSER_KEY);
+ }
+
+ public Browser launchBrowser(final Playwright playwright) {
+ return resolveBrowser(playwright, getBrowser().toUpperCase())
+ .launch(new com.microsoft.playwright.BrowserType.LaunchOptions().setHeadless(isCi()));
+ }
+
+ public void close(C... stuffToClose) {
+ Stream.of(stuffToClose)
+ .filter(Objects::nonNull)
+ .forEach(closeable -> Try
+ .run(closeable::close)
+ .onFailure(ex -> Logger.warn(
+ PlaywrightSupport.class,
+ "Could not close Playwright component",
+ ex)));
+ }
+
+ public Tuple2 createContextAndPage(final com.microsoft.playwright.Browser browser) {
+ final BrowserContext context = browser.newContext();
+ final Page page = context.newPage();
+ return new Tuple2<>(context, page);
+ }
+
+ public com.microsoft.playwright.BrowserType resolveBrowser(final Playwright playwright, final String browser) {
+ final com.microsoft.playwright.BrowserType resolved;
+ switch (BrowserType.fromString(browser)) {
+ case FIREFOX:
+ resolved = playwright.firefox();
+ break;
+ case WEBKIT:
+ resolved = playwright.webkit();
+ break;
+ case CHROMIUM:
+ default:
+ resolved = playwright.chromium();
+ }
+
+ return resolved;
+ }
+
+ public LocatorAssertions.IsVisibleOptions visibleTimeout(final double timeout) {
+ return new LocatorAssertions.IsVisibleOptions().setTimeout(timeout);
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/service/EnvironmentService.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/service/EnvironmentService.java
new file mode 100644
index 000000000000..3d219e959291
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/service/EnvironmentService.java
@@ -0,0 +1,45 @@
+package com.dotcms.e2e.service;
+
+import com.dotcms.e2e.util.StringToolbox;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class EnvironmentService {
+
+ private static final EnvironmentService INSTANCE = new EnvironmentService();
+
+ public static EnvironmentService get() {
+ return INSTANCE;
+ }
+
+ private final Properties props;
+
+ private EnvironmentService() {
+ props = new Properties();
+ final String envFile = System.getProperty("env", "environment");
+ final String filePath = envFile.concat(".properties");
+
+ try {
+ props.load(EnvironmentService.class.getClassLoader().getResourceAsStream(filePath));
+ } catch (IOException e) {
+ System.out.println("Did not manage to read this property file");
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getProperty(final String key, final String defaultValue) {
+ final String propValue = props.getProperty(key);
+ if (StringUtils.isNotBlank(propValue)) {
+ return propValue;
+ }
+
+ return StringUtils.defaultString(System.getenv(StringToolbox.camelToSnake(key)), defaultValue);
+ }
+
+ public String getProperty(final String key) {
+ return getProperty(key, null);
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/test/BaseE2eTest.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/test/BaseE2eTest.java
new file mode 100644
index 000000000000..18c406ebbc33
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/test/BaseE2eTest.java
@@ -0,0 +1,63 @@
+package com.dotcms.e2e.test;
+
+import com.dotcms.e2e.E2eKeys;
+import com.dotcms.e2e.playwright.PlaywrightSupport;
+import com.dotcms.e2e.service.EnvironmentService;
+import com.microsoft.playwright.Browser;
+import com.microsoft.playwright.BrowserContext;
+import com.microsoft.playwright.Page;
+import com.microsoft.playwright.Playwright;
+import io.vavr.Tuple2;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+
+public abstract class BaseE2eTest {
+
+ protected static Playwright playwright;
+ protected static Browser browser;
+
+ protected BrowserContext context;
+ protected Page page;
+
+ @BeforeAll
+ public static void launchBrowser() {
+ playwright = PlaywrightSupport.get().playwright();
+ browser = PlaywrightSupport.get().launchBrowser(playwright);
+ }
+
+ @AfterAll
+ public static void closeBrowser() {
+ PlaywrightSupport.get().close(browser, playwright);
+ }
+
+ @BeforeEach
+ void createContextAndPage() {
+ final Tuple2 contextAndPage = PlaywrightSupport.get().createContextAndPage(browser);
+ context = contextAndPage._1;
+ page = contextAndPage._2;
+ }
+
+ @AfterEach
+ void closeContextAndPage() {
+ PlaywrightSupport.get().close(page, context);
+ }
+
+ public String getProperty(final String key) {
+ return EnvironmentService.get().getProperty(key);
+ }
+
+ public String getProperty(final String key, final String defaultValue) {
+ return EnvironmentService.get().getProperty(key, defaultValue);
+ }
+
+ public String getBaseUrl() {
+ return getProperty(E2eKeys.E2E_BASE_URL_KEY, E2eKeys.DEFAULT_BASE_URL);
+ }
+
+ public void navigateToBaseUrl() {
+ page.navigate(getBaseUrl());
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/test/LoginTests.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/test/LoginTests.java
new file mode 100644
index 000000000000..68467b4deedc
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/test/LoginTests.java
@@ -0,0 +1,86 @@
+package com.dotcms.e2e.test;
+
+import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
+
+import com.dotcms.e2e.page.LoginPage;
+import com.dotcms.e2e.playwright.PlaywrightSupport;
+import com.microsoft.playwright.options.AriaRole;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+@DisplayName("Login portlet tests")
+public class LoginTests extends BaseE2eTest {
+
+ private LoginPage loginPage;
+
+ @BeforeEach
+ public void setup() {
+ loginPage = new LoginPage(page);
+ page.navigate(getBaseUrl());
+ }
+
+ /**
+ * Test the login functionality with the default user
+ */
+ @Test
+ public void loginSuccess() {
+ loginPage.successfulLogin();
+ assertThat(page.getByRole(AriaRole.IMG)).isVisible();
+ }
+
+ /**
+ * Test the login functionality with a wrong user
+ */
+ @Test
+ public void loginWrongUser() {
+ loginPage.login("jon.snow@got.com", "admin");
+ assertThat(page.getByTestId("message")).isVisible();
+ }
+
+ /**
+ * Test the login functionality with a wrong password
+ */
+ @Test
+ public void loginWrongPass() {
+ loginPage.login("admin@dotcms.com", "winteriscoming");
+ assertThat(page.getByTestId("message")).isVisible(PlaywrightSupport.get().visibleTimeout(10000));
+ }
+
+ /**
+ * Test the login functionality with Spanish language
+ */
+ @Test
+ public void loginSpanishLanguage() {
+ loginPage.switchLanguage("español (España)");
+ assertThat(page.getByText("¡Bienvenido!")).isVisible();
+ }
+
+ /**
+ * Test the login functionality with Italian language
+ */
+ @Test
+ public void loginItalianLanguage() {
+ loginPage.switchLanguage("italiano (Italia)");
+ assertThat(page.getByText("Benvenuto! ")).isVisible();
+ }
+
+ /**
+ * Test the login functionality with French language
+ */
+ @Test
+ public void loginFrenchLanguage() {
+ loginPage.switchLanguage("français (France)");
+ assertThat(page.getByText("Bienvenue !")).isVisible();
+ }
+
+ /**
+ * Test the login functionality with Dutch language
+ */
+ @Test
+ public void loginDutchLanguage() {
+ loginPage.switchLanguage("Deutsch (Deutschland)");
+ assertThat(page.getByText("Willkommen! ")).isVisible();
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/util/StdOutErrLog.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/util/StdOutErrLog.java
new file mode 100644
index 000000000000..6440f8cd9ed0
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/util/StdOutErrLog.java
@@ -0,0 +1,23 @@
+package com.dotcms.e2e.util;
+
+import com.dotcms.e2e.logging.Logger;
+
+import java.io.PrintStream;
+
+public class StdOutErrLog {
+
+ public static void tieSystemOutAndErrToLog() {
+ System.setOut(createLoggingProxy(System.out));
+ System.setErr(createLoggingProxy(System.err));
+ }
+
+ public static PrintStream createLoggingProxy(final PrintStream realPrintStream) {
+ return new PrintStream(realPrintStream) {
+ public void print(final String string) {
+ //realPrintStream.print(string);
+ Logger.info(StdOutErrLog.class, string);
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/util/StringToolbox.java b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/util/StringToolbox.java
new file mode 100644
index 000000000000..f869fef3ed73
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/java/com/dotcms/e2e/util/StringToolbox.java
@@ -0,0 +1,12 @@
+package com.dotcms.e2e.util;
+
+public class StringToolbox {
+
+ private StringToolbox() {
+ }
+
+ public static String camelToSnake(String str) {
+ return str.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase();
+ }
+
+}
diff --git a/e2e/dotcms-e2e-java/src/test/resources/environment.properties b/e2e/dotcms-e2e-java/src/test/resources/environment.properties
new file mode 100644
index 000000000000..fecb45be2d74
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/resources/environment.properties
@@ -0,0 +1,9 @@
+## Server URL to run the tests
+baseUrl = http://localhost:8080/c
+
+## Set the browser you want to run (Chrome // Firefox // Safari))
+browser = Chromium
+
+## Valid credentials
+default_user = admin@dotcms.com
+default_password = admin
diff --git a/e2e/dotcms-e2e-java/src/test/resources/log4j2.xml b/e2e/dotcms-e2e-java/src/test/resources/log4j2.xml
new file mode 100644
index 000000000000..a198e119167a
--- /dev/null
+++ b/e2e/dotcms-e2e-java/src/test/resources/log4j2.xml
@@ -0,0 +1,43 @@
+
+
+
+ ${sys:DOTCMS_LOGGING_HOME}/dotcms-${sys:DOTCMS_FORK_NUMBER}.log
+ ${sys:DOTCMS_LOGGING_HOME}/archive/dotcms-%i-.log.gz
+ java.lang.reflect.Method,
+ org.junit,
+ jdk.internal.reflect,
+ com.dotcms.junit,
+ org.apache.maven.surefire,
+ sun.reflect,
+ net.sf.cglib,
+ ByCGLIB
+ [%d{dd/MM/yy HH:mm:ss:SSS z}] %5p %c{2}: %m%n%ex{filters(${stack.filter})}
+ %-5level %d $${sys:DOTCMS_FORK_NUMBER} %c:%M(%L): %m%n%ex{filters(${stack.filter})}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/pom.xml b/e2e/pom.xml
new file mode 100644
index 000000000000..77bd5cd7315c
--- /dev/null
+++ b/e2e/pom.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ com.dotcms
+ dotcms-parent
+ ${revision}${sha1}${changelist}
+ ../parent/pom.xml
+
+
+ 4.0.0
+ dotcms-e2e-parent
+ pom
+
+
+ UTF-8
+
+
+
+ dotcms-e2e-java
+
+
+
+
diff --git a/justfile b/justfile
index 14c4c6b03b06..61a026e88707 100644
--- a/justfile
+++ b/justfile
@@ -125,8 +125,20 @@ test-integration-ide:
# Stops integration test services
test-integration-stop:
./mvnw -pl :dotcms-integration -Pdocker-stop -Dcoreit.test.skip=false
-# Docker Commands
+# Executes Java E2E tests
+test-e2e-java:
+ ./mvnw -pl :dotcms-e2e-java verify -De2e.test.skip=false
+
+# Suspends execution for debugging Java E2E tests
+test-e2e-java-debug-suspend:
+ ./mvnw -pl :dotcms-e2e-java verify -De2e.test.skip=false -Pdebug-suspend-e2e-tests
+
+# Stops E2E test services
+test-e2e-stop:
+ ./mvnw -pl :dotcms-e2e -Pdocker-stop -De2e.test.skip=false
+
+# Docker Commands
# Runs a published dotCMS Docker image on a dynamic port
docker-ext-run tag='latest':
./mvnw -pl :dotcms-core -Pdocker-start -Dcontext.name=ext-{{ tag }} -Ddotcms.image.name=dotcms/dotcms:{{ tag }}
@@ -139,7 +151,6 @@ docker-test-ext-run tag='master':
docker-ext-stop tag='latest':
./mvnw -pl :dotcms-core -Pdocker-stop -Dcontext.name=ext-{{ tag }}
-
# Generate a cli uber-jar
cli-build-uber-jar:
./mvnw -pl :dotcms-cli package -DskipTests=true
diff --git a/parent/pom.xml b/parent/pom.xml
index 4cea7ba8c0e1..0d3d3db7aa0e 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -1377,6 +1377,53 @@
+
+ debug-suspend-e2e-docker
+
+
+
+ debug.suspend.e2e.docker
+
+
+
+ y
+ ${docker.debug.args.default}
+ 7200000
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+
+
+
+
+ debug.port:${debug.port}
+
+
+
+
+
+
+
+
+
+
+ debug-suspend-e2e-tests
+
+
+
+ debug.suspend.e2e.tests
+
+
+
+ y
+ ${debug.args.default}
+ 7200000
+
+
docker-start
@@ -1431,11 +1478,11 @@
-
+
-
+
diff --git a/pom.xml b/pom.xml
index e95e4390576b..baf25e35824f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,8 @@
dotcms-integration
dotcms-postman
reports
+ e2e/dotcms-e2e-java
+