diff --git a/.dockerignore b/.dockerignore index 788be24434..05f49862d6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,4 +2,4 @@ docker/postgresql-data docker/postgresql-16-data docker/roller-data -it-selenium +it-playwright diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c0e6e07def..4c7458e9d3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,10 +22,10 @@ on: branches: [master] pull_request: branches: [master, 'feature/**'] - + jobs: build-test: - name: Build+Test on Linux/JDK ${{ matrix.java }} + name: Build+Test on Linux/JDK ${{ matrix.java }} runs-on: ubuntu-latest timeout-minutes: 30 @@ -35,17 +35,12 @@ jobs: java: [ '17', '21', '25' ] steps: - - name: Set up JDK ${{ matrix.java }} + - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v4 with: - java-version: ${{ matrix.java }} + java-version: ${{ matrix.java }} distribution: 'zulu' - - name: Setup Xvfb - run: | - echo "DISPLAY=:99.0" >> $GITHUB_ENV - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & - - name: Checkout Project uses: actions/checkout@v4 with: @@ -56,23 +51,12 @@ jobs: - name: Build Roller and run JUnit Tests run: mvn -V -ntp install - - name: Run Integration Tests - run: | - cd it-selenium - mvn -V -ntp install - - name: Publish JUnit Report uses: test-summary/action@v2 if: always() with: paths: "app/target/surefire-reports/TEST-*.xml" - - name: Publish IT Report - uses: test-summary/action@v2 - if: always() - with: - paths: "it-selenium/target/failsafe-reports/TEST-*.xml" - # only on integration and only once in this matrix - name: Upload Dev Build on Integration if: ${{ (matrix.java == '17') && (github.event_name == 'push') }} @@ -82,3 +66,134 @@ jobs: path: ./app/target/roller.war retention-days: 90 if-no-files-found: error + + # Browser tests against Roller's own user database: the new-user journey + # (register, create a weblog, publish and read an entry). The OIDC tests + # skip themselves on this instance, and roller.expectedAuth turns any other + # unexpected skip into a failure. + ui-tests-db: + name: UI tests (db) + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'zulu' + + - name: Checkout Project + uses: actions/checkout@v4 + with: + persist-credentials: false + submodules: false + show-progress: false + + - name: Build Roller + run: mvn -V -ntp -DskipTests install + + - name: Start Roller with Jetty and Derby + run: | + mvn -ntp jetty:run > jetty.log 2>&1 & + timeout 300 bash -c 'until curl -sf http://localhost:8080/roller/ > /dev/null; do sleep 5; done' + + - name: Install Playwright browser + working-directory: it-playwright + run: mvn -ntp test-compile exec:java -Dexec.classpathScope=test -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install --with-deps chromium" + + - name: Run UI tests + working-directory: it-playwright + run: mvn -ntp verify -Droller.expectedAuth=db + + - name: Publish UI Test Report + uses: test-summary/action@v2 + if: always() + with: + paths: "it-playwright/target/failsafe-reports/TEST-*.xml" + + - name: Upload traces and server log + if: failure() + uses: actions/upload-artifact@v4 + with: + name: ui-tests-db-diagnostics + path: | + it-playwright/target/playwright-traces + jetty.log + if-no-files-found: ignore + + # Browser tests against the Docker Compose stack (Tomcat + PostgreSQL + + # Keycloak), once per authentication method the stack supports: pure OIDC, + # and db-oidc where form login and OIDC are offered side by side. In db-oidc + # mode every suite runs: the journey registers the first user through the + # form before OIDC sign-in provisions any accounts. + ui-tests-oidc: + name: UI tests (${{ matrix.auth }}) + runs-on: ubuntu-latest + timeout-minutes: 45 + + strategy: + fail-fast: false + matrix: + auth: [ 'oidc', 'db-oidc' ] + + steps: + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'zulu' + + - name: Checkout Project + uses: actions/checkout@v4 + with: + persist-credentials: false + submodules: false + show-progress: false + + # Roller discovers Keycloak's endpoints at http://keycloak:9080 and + # redirects the browser to them, so the runner must resolve the compose + # hostname too + - name: Resolve Keycloak's hostname on the runner + run: echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts + + - name: Build and start Roller, PostgreSQL and Keycloak + run: docker compose up -d --build + env: + AUTHENTICATION_METHOD: ${{ matrix.auth }} + + # a fresh database lands on Roller's auto-installer, which waits for a + # click; drive it so the login page is reachable + - name: Wait for Roller and install its database + run: | + timeout 300 bash -c 'until curl -sf http://localhost:8080/ > /dev/null; do sleep 5; done' + curl -sf -X POST http://localhost:8080/roller-ui/install/install!create.rol > /dev/null + curl -sf -X POST http://localhost:8080/roller-ui/install/install!bootstrap.rol > /dev/null + + - name: Install Playwright browser + working-directory: it-playwright + run: mvn -ntp test-compile exec:java -Dexec.classpathScope=test -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install --with-deps chromium" + + - name: Run UI tests + working-directory: it-playwright + run: mvn -ntp verify -Droller.baseUrl=http://localhost:8080/ -Droller.expectedAuth=${{ matrix.auth }} + + - name: Publish UI Test Report + uses: test-summary/action@v2 + if: always() + with: + paths: "it-playwright/target/failsafe-reports/TEST-*.xml" + + - name: Collect container logs + if: failure() + run: docker compose logs > compose.log + + - name: Upload traces and container logs + if: failure() + uses: actions/upload-artifact@v4 + with: + name: ui-tests-${{ matrix.auth }}-diagnostics + path: | + it-playwright/target/playwright-traces + compose.log + if-no-files-found: ignore diff --git a/.gitignore b/.gitignore index f1557451b0..1c33b528d0 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,9 @@ docker/postgresql-data docker/roller-data assembly-release/release.sh it-selenium/overlays/ + +# Playwright MCP session output +.playwright-mcp/ + +# runtime logs from mvn jetty:run logs/ diff --git a/README.md b/README.md index aa8523642e..4f258c5406 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Roller is made up of the following Maven projects: * _app_: Roller Weblogger webapp, JSP pages, Velocity templates * _assembly-release_: Used to create official distributions of Roller * _docs_: Roller documentation in ASCII Doc format -* _it-selenium_: Integrated browser tests for Roller using Selenium +* _it-playwright_: Browser tests for Roller using Playwright (run separately, see below) ## Documentation @@ -46,7 +46,7 @@ Compile and build Roller: $ cd roller $ mvn -DskipTests=true install -Run Roller in Jetty with an embedded Derby database (for testing only): +Run Roller in Jetty with an in-memory Derby database (for testing only): $ mvn jetty:run @@ -63,10 +63,26 @@ Get the code: $ git clone https://github.com/apache/roller.git -Run Docker Compose to build and launch Roller along with a PostgreSQL database: +The compose stack runs Roller against a PostgreSQL database with Keycloak as an OpenID Connect identity provider. +Roller and your browser must both reach Keycloak at the same hostname, so add this line to `/etc/hosts` once: + + 127.0.0.1 keycloak + +Run Docker Compose to build and launch everything: $ cd roller - $ docker-compose up - -It will take a while to build and start the Docker image. -Once it's done browse to to try Roller. + $ docker compose up + +It will take a while to build and start the Docker image. +Once it's done browse to and log in as `admin`/`admin` (administrator) or `user`/`user` (regular user). + + +## Running the tests + +Unit tests run as part of the normal build: + + $ mvn install + +Browser-based UI tests live in `it-playwright` and run against a started Roller, whichever way you started it. +See [it-playwright/README.md](it-playwright/README.md) for instructions. +CI runs both on every push and pull request. diff --git a/assembly-release/src/main/assembly/source.xml b/assembly-release/src/main/assembly/source.xml index ea36aebd97..63d7d4fc07 100644 --- a/assembly-release/src/main/assembly/source.xml +++ b/assembly-release/src/main/assembly/source.xml @@ -33,7 +33,7 @@ app/** assembly-release/** docs/** - it-selenium/** + it-playwright/** LICENSE.txt NOTICE.txt pom.xml diff --git a/it-playwright/README.md b/it-playwright/README.md new file mode 100644 index 0000000000..583f6b5ba1 --- /dev/null +++ b/it-playwright/README.md @@ -0,0 +1,82 @@ + + +# Roller UI tests + +Browser tests for Roller, written in Java with [Playwright](https://playwright.dev/java/). +They are deliberately not part of the main Maven build: nothing here ships to end users, and the build should not depend on a browser being available. +CI runs them on every push and pull request (see `.github/workflows/main.yml`). + +The suite points at a running Roller and adapts to how it is configured: + +* `NewUserJourneyIT` covers what the old Selenium suite did: register the first user, sign in, create a weblog, publish an entry and read it back on the blog. Roller only accepts registrations while it has no users, so this test skips itself on an instance that already has one, and on an instance that delegates login to an identity provider. +* `OidcLoginIT` signs in through an external OIDC provider as an administrator and as a regular user, checks that only the administrator can reach server administration, and that a signed-in OIDC user can create a weblog and see it rendered. It skips itself when no provider is configured. +* `LoginPageIT` checks the login page offers exactly the sign-in mechanisms of the configured authentication method. It only runs when you declare that method (see below). + +Skipping keeps casual local runs friendly, but it also means a misconfigured instance could pass with everything skipped. +Declare what the instance is supposed to be and mismatches become failures instead: + + mvn verify -Droller.expectedAuth=db only the username/password form + mvn verify -Droller.expectedAuth=oidc only identity-provider buttons + mvn verify -Droller.expectedAuth=db-oidc both + +CI covers all three: `db` on Jetty with Derby, `oidc` and `db-oidc` on the Docker Compose stack (the compose file's authentication method can be overridden with the `AUTHENTICATION_METHOD` environment variable). +Roller's remaining authentication methods have no coverage here: `ldap` needs a directory server and `cma` needs container-managed security, and neither is part of this project's stacks. + +## Running against Jetty and Derby (database auth) + +From the project root, build once and start Roller with an in-memory database: + + mvn -DskipTests install + mvn jetty:run + +Then, from this directory: + + mvn verify + +Every `jetty:run` start gives a fresh database, which is what the new-user journey needs. +Run it again without restarting and the journey reports itself skipped rather than failing, because the first user now exists. + +## Running against Docker Compose (OIDC auth) + +The compose stack runs Roller against PostgreSQL with Keycloak as the identity provider. +Roller and your browser must both reach Keycloak at the same hostname, so add this line to `/etc/hosts` once: + + 127.0.0.1 keycloak + +Then, from the project root: + + docker compose up -d + +and from this directory: + + mvn verify -Droller.baseUrl=http://localhost:8080/ + +Keycloak is seeded with an administrator (`admin`/`admin`) and a regular user (`user`/`user`). +To offer form login next to the provider buttons, start the stack with `AUTHENTICATION_METHOD=db-oidc docker compose up -d` instead; on a fresh database the new-user journey then runs against it too. + +## Options + + mvn verify -Dplaywright.headed=true watch the browser + mvn verify -Droller.baseUrl= point at any Roller instance + mvn verify -Dit.test=NewUserJourneyIT run a single test class + +Playwright downloads the browser it needs on first run. +When a test fails, a trace is written to `target/playwright-traces/.zip`; open it with: + + npx playwright show-trace target/playwright-traces/.zip diff --git a/it-playwright/pom.xml b/it-playwright/pom.xml new file mode 100644 index 0000000000..de22374b85 --- /dev/null +++ b/it-playwright/pom.xml @@ -0,0 +1,113 @@ + + + + 4.0.0 + + org.apache.roller + roller-playwright-tests + 6.1.5 + jar + Roller Playwright tests + + + + + 17 + UTF-8 + 1.62.0 + 5.11.4 + + http://localhost:8080/roller/ + false + + + + + + + com.microsoft.playwright + playwright + ${playwright.version} + test + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.5.0 + + + maven-failsafe-plugin + 3.5.2 + + + alphabetical + + ${roller.baseUrl} + ${roller.expectedAuth} + ${playwright.headed} + ${project.build.directory}/playwright-traces + + + + + + integration-test + verify + + + + + + + diff --git a/it-playwright/src/test/java/org/apache/roller/playwright/BaseIT.java b/it-playwright/src/test/java/org/apache/roller/playwright/BaseIT.java new file mode 100644 index 0000000000..1387e83774 --- /dev/null +++ b/it-playwright/src/test/java/org/apache/roller/playwright/BaseIT.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ +package org.apache.roller.playwright; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.microsoft.playwright.Browser; +import com.microsoft.playwright.BrowserContext; +import com.microsoft.playwright.BrowserType; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.Playwright; +import com.microsoft.playwright.Tracing; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; + +/** + * Browser lifecycle for the Roller UI tests. + * + *

Each test gets a fresh browser context, so sessions never leak between + * tests. A Playwright trace is recorded and kept only when a test fails; open + * one with {@code npx playwright show-trace }. + */ +@ExtendWith(BaseIT.TraceOnFailure.class) +abstract class BaseIT { + + /** Roller's base URL, always with a trailing slash. */ + protected static String baseUrl; + + private static Playwright playwright; + private static Browser browser; + + protected BrowserContext context; + protected Page page; + + private boolean failed; + + @BeforeAll + static void launchBrowser() { + String url = System.getProperty("roller.baseUrl", "http://localhost:8080/roller/"); + baseUrl = url.endsWith("/") ? url : url + "/"; + + playwright = Playwright.create(); + browser = playwright.chromium().launch(new BrowserType.LaunchOptions() + .setHeadless(!Boolean.getBoolean("playwright.headed"))); + } + + @AfterAll + static void closeBrowser() { + if (browser != null) { + browser.close(); + } + if (playwright != null) { + playwright.close(); + } + } + + @BeforeEach + void openContext() { + context = browser.newContext(new Browser.NewContextOptions() + .setBaseURL(baseUrl) + .setViewportSize(1280, 1024)); + context.tracing().start(new Tracing.StartOptions() + .setScreenshots(true) + .setSnapshots(true)); + page = context.newPage(); + failed = false; + } + + @AfterEach + void closeContext(org.junit.jupiter.api.TestInfo info) { + Path trace = null; + if (failed) { + trace = tracesDir().resolve(info.getTestMethod() + .map(java.lang.reflect.Method::getName).orElse("test") + ".zip"); + trace.getParent().toFile().mkdirs(); + } + context.tracing().stop(new Tracing.StopOptions().setPath(trace)); + context.close(); + } + + /** Marks the trace for keeping when the test method threw. Runs before {@code @AfterEach}. */ + static class TraceOnFailure implements AfterTestExecutionCallback { + @Override + public void afterTestExecution(ExtensionContext ctx) { + if (ctx.getExecutionException().isPresent()) { + ctx.getTestInstance().ifPresent(instance -> ((BaseIT) instance).failed = true); + } + } + } + + private static Path tracesDir() { + return Paths.get(System.getProperty("playwright.tracesDir", "target/playwright-traces")); + } + + /** Navigates to a path relative to Roller's base URL. */ + protected void goTo(String relativePath) { + page.navigate(relativePath.startsWith("/") ? relativePath.substring(1) : relativePath); + } + + /** + * The authentication method this instance is supposed to be running + * ({@code -Droller.expectedAuth}), or empty when the suite should just + * adapt to whatever the instance offers. + */ + protected static String expectedAuth() { + return System.getProperty("roller.expectedAuth", ""); + } + + // the entry editor, shared by the new-user journey and the OIDC suite + private static final String ENTRY_TITLE_FIELD = "#entry_bean_title"; + private static final String ENTRY_RICH_TEXT = ".note-editable"; + private static final String ENTRY_TEXTAREA = "#edit_content"; + private static final String ENTRY_POST_BUTTON = "input.btn-success[type='submit']"; + + // the rendered weblog + private static final String RENDERED_ENTRY_TITLE = "p.entryTitle, .entryTitle"; + private static final String RENDERED_ENTRY_CONTENT = "p.entryContent, .entryContent"; + + /** Publishes an entry through the editor of the given weblog. */ + protected void publishEntry(String weblogHandle, String title, String text) { + goTo("roller-ui/authoring/entryAdd.rol?weblog=" + weblogHandle); + assertThat(page).hasTitle(java.util.regex.Pattern.compile("New Entry")); + + page.locator(ENTRY_TITLE_FIELD).fill(title); + + // Roller's editor is configurable: a rich text editor replaces the + // textarea with a contenteditable, otherwise the textarea is used as is + var richText = page.locator(ENTRY_RICH_TEXT); + if (richText.count() > 0) { + richText.first().click(); + richText.first().fill(text); + } else { + page.locator(ENTRY_TEXTAREA).fill(text); + } + + page.locator(ENTRY_POST_BUTTON).first().click(); + assertThat(page).hasTitle(java.util.regex.Pattern.compile("Edit Entry")); + } + + /** Asserts the entry is the latest one rendered on the weblog itself. */ + protected void assertEntryOnBlog(String weblogHandle, String title, String text) { + goTo(weblogHandle + "/"); + assertThat(page.locator(RENDERED_ENTRY_TITLE).first()).containsText(title); + assertThat(page.locator(RENDERED_ENTRY_CONTENT).first()).containsText(text); + } +} diff --git a/it-playwright/src/test/java/org/apache/roller/playwright/LoginPageIT.java b/it-playwright/src/test/java/org/apache/roller/playwright/LoginPageIT.java new file mode 100644 index 0000000000..6eae7002f5 --- /dev/null +++ b/it-playwright/src/test/java/org/apache/roller/playwright/LoginPageIT.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ +package org.apache.roller.playwright; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Pins the login page to the authentication method the instance is supposed + * to be running: {@code db} must offer only the username/password form, + * {@code oidc} only the identity-provider buttons, {@code db-oidc} both. + * + *

The other suites skip whatever their instance does not offer, so on a + * misconfigured instance they could all skip and still pass. This test runs + * only when {@code -Droller.expectedAuth} says what the instance is meant to + * be, and then a missing or surplus sign-in mechanism is a failure. + */ +@DisplayName("Login page") +class LoginPageIT extends BaseIT { + + private static final String LOGIN_PAGE = "roller-ui/login.rol"; + private static final String LOGIN_FORM = "input[name='j_username']"; + private static final String PROVIDER_BUTTON = "a[href*='/oauth2/authorization/']"; + + @Test + @DisplayName("offers exactly the sign-in mechanisms of the configured authentication method") + void offersConfiguredMechanisms() { + String expected = expectedAuth(); + assumeTrue(!expected.isEmpty(), + "pass -Droller.expectedAuth=db|oidc|db-oidc to pin what the login page must offer"); + + boolean expectForm = switch (expected) { + case "db", "db-oidc" -> true; + case "oidc" -> false; + default -> Assertions.fail("unknown roller.expectedAuth value: " + expected); + }; + boolean expectProviders = !expected.equals("db"); + + goTo(LOGIN_PAGE); + Assertions.assertEquals(expectForm, page.locator(LOGIN_FORM).count() > 0, + "username/password form on the login page"); + Assertions.assertEquals(expectProviders, page.locator(PROVIDER_BUTTON).count() > 0, + "identity-provider buttons on the login page"); + } +} diff --git a/it-playwright/src/test/java/org/apache/roller/playwright/NewUserJourneyIT.java b/it-playwright/src/test/java/org/apache/roller/playwright/NewUserJourneyIT.java new file mode 100644 index 0000000000..5e8510c21f --- /dev/null +++ b/it-playwright/src/test/java/org/apache/roller/playwright/NewUserJourneyIT.java @@ -0,0 +1,235 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ +package org.apache.roller.playwright; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Pattern; + +import javax.imageio.ImageIO; + +import com.microsoft.playwright.APIResponse; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * The path a brand new Roller install puts its first visitor through: register + * an account, sign in, create a weblog, then publish and read back an entry, + * and finally upload a media file (the first user is the admin, so the journey + * also turns file uploads on, which Roller ships disabled). + * + *

This is the journey the old Selenium suite covered. Roller only accepts + * registrations on an install with no users yet, so the test skips when the + * instance already has one (restart {@code mvn -pl app jetty:run} for a fresh + * in-memory database) or when it delegates login to an identity provider. + */ +@DisplayName("New user journey") +class NewUserJourneyIT extends BaseIT { + + private static final String USERNAME = "bsmith"; + private static final String FULL_NAME = "Bob Smith"; + private static final String PASSWORD = "roller123"; + private static final String EMAIL = "bsmith@example.com"; + private static final String BLOG_HANDLE = "bobsblog"; + private static final String BLOG_NAME = "Bob's Blog"; + private static final String ENTRY_TITLE = "My First Blog Entry"; + private static final String ENTRY_TEXT = "Welcome to my blog!"; + + // pages + private static final String LOGIN_PAGE = "roller-ui/login.rol"; + private static final String REGISTER_PAGE = "roller-ui/register.rol"; + private static final String MENU_PAGE = "roller-ui/menu.rol"; + private static final String CREATE_WEBLOG_PAGE = "roller-ui/createWeblog.rol"; + + // login form + private static final String LOGIN_USERNAME = "input[name='j_username']"; + private static final String LOGIN_PASSWORD = "input[name='j_password']"; + private static final String LOGIN_SUBMIT = "#loginForm input[type='submit'], #loginForm button[type='submit']"; + + // registration form + private static final String REGISTER_USERNAME = "#register_bean_userName"; + private static final String REGISTER_SCREEN_NAME = "#register_bean_screenName"; + private static final String REGISTER_FULL_NAME = "#register_bean_fullName"; + private static final String REGISTER_EMAIL = "#register_bean_emailAddress"; + private static final String REGISTER_PASSWORD = "#register_bean_passwordText"; + private static final String REGISTER_PASSWORD_CONFIRM = "#register_bean_passwordConfirm"; + private static final String REGISTER_SUBMIT = "#submit"; + + // create-weblog form + private static final String WEBLOG_NAME = "#createWeblog_bean_name"; + private static final String WEBLOG_HANDLE = "#createWeblog_bean_handle"; + private static final String WEBLOG_EMAIL = "#createWeblog_bean_emailAddress"; + private static final String WEBLOG_SUBMIT = "#createWeblog_0"; + + // server admin configuration form + private static final String GLOBAL_CONFIG_PAGE = "roller-ui/admin/globalConfig.rol"; + private static final String UPLOADS_ENABLED = "input[name='uploads.enabled']"; + private static final String GLOBAL_CONFIG_SAVE = "#saveButton"; + + // media file upload form + private static final String MEDIA_UPLOAD_PAGE = "roller-ui/authoring/mediaFileAdd.rol?weblog=" + BLOG_HANDLE; + private static final String MEDIA_FILE_INPUT = "#fileControl0"; + private static final String MEDIA_UPLOAD_SUBMIT = "#uploadButton"; + private static final String MEDIA_THUMBNAIL = "img.mediaFileImage"; + + // OPML bookmark import form + private static final String BOOKMARKS_IMPORT_PAGE = "roller-ui/authoring/bookmarksImport.rol?weblog=" + BLOG_HANDLE; + private static final String OPML_FILE_INPUT = "input[name='opmlFile']"; + private static final String OPML_IMPORT_SUBMIT = "input.btn-primary[type='submit']"; + + @Test + @DisplayName("registers an account, creates a weblog, publishes an entry, uploads a media file, and imports bookmarks") + void firstUserCanRegisterAndPublish() { + goTo(LOGIN_PAGE); + assumeTrue(page.locator(LOGIN_USERNAME).count() > 0, + "Roller is not using its own user database"); + + register(); + signIn(); + createWeblog(); + publishEntry(BLOG_HANDLE, ENTRY_TITLE, ENTRY_TEXT); + assertEntryOnBlog(BLOG_HANDLE, ENTRY_TITLE, ENTRY_TEXT); + enableUploads(); + uploadMediaFile(); + importBookmarks(); + } + + private void register() { + goTo(REGISTER_PAGE); + assertThat(page).hasTitle(Pattern.compile("New User Registration")); + + // on a fresh install registration must be open; against a developer's + // long-running instance (no expected auth declared) skip instead + boolean registrationOpen = page.locator(REGISTER_USERNAME).count() > 0; + if (expectedAuth().isEmpty()) { + assumeTrue(registrationOpen, + "registration is closed: this journey needs a fresh install with no users yet"); + } else { + Assertions.assertTrue(registrationOpen, + "registration is closed, but a fresh install was expected to accept its first user"); + } + + page.locator(REGISTER_USERNAME).fill(USERNAME); + page.locator(REGISTER_SCREEN_NAME).fill(USERNAME); + page.locator(REGISTER_FULL_NAME).fill(FULL_NAME); + page.locator(REGISTER_EMAIL).fill(EMAIL); + page.locator(REGISTER_PASSWORD).fill(PASSWORD); + page.locator(REGISTER_PASSWORD_CONFIRM).fill(PASSWORD); + + // the form enables its submit button from an onkeyup handler, which + // fill() does not fire, so send a real key press to trigger validation + page.locator(REGISTER_PASSWORD_CONFIRM).press("End"); + assertThat(page.locator(REGISTER_SUBMIT)).isEnabled(); + page.locator(REGISTER_SUBMIT).click(); + } + + private void signIn() { + goTo(LOGIN_PAGE); + page.locator(LOGIN_USERNAME).fill(USERNAME); + page.locator(LOGIN_PASSWORD).fill(PASSWORD); + page.locator(LOGIN_SUBMIT).first().click(); + + goTo(MENU_PAGE); + assertThat(page).hasTitle(Pattern.compile("Your Weblogs")); + } + + private void createWeblog() { + goTo(CREATE_WEBLOG_PAGE); + assertThat(page).hasTitle(Pattern.compile("Create Weblog")); + + page.locator(WEBLOG_NAME).fill(BLOG_NAME); + page.locator(WEBLOG_HANDLE).fill(BLOG_HANDLE); + page.locator(WEBLOG_EMAIL).fill(EMAIL); + page.locator(WEBLOG_SUBMIT).click(); + + // the new weblog is now listed on the main menu + goTo(MENU_PAGE); + assertThat(page.getByText(BLOG_NAME).first()).isVisible(); + } + + private void enableUploads() { + goTo(GLOBAL_CONFIG_PAGE); + page.locator(UPLOADS_ENABLED).check(); + page.locator(GLOBAL_CONFIG_SAVE).click(); + assertThat(page.locator(UPLOADS_ENABLED)).isChecked(); + } + + private void uploadMediaFile() { + goTo(MEDIA_UPLOAD_PAGE); + page.locator(MEDIA_FILE_INPUT).setInputFiles(pngFile()); + page.locator(MEDIA_UPLOAD_SUBMIT).click(); + assertThat(page).hasTitle(Pattern.compile("Media File Upload Complete")); + + // the uploaded image the success page shows is really served back + String thumbnail = page.locator(MEDIA_THUMBNAIL).first().getAttribute("src"); + String mediaUrl = URI.create(page.url()).resolve(thumbnail.replace("?t=true", "")).toString(); + APIResponse media = page.request().get(mediaUrl); + Assertions.assertEquals(200, media.status()); + Assertions.assertEquals("image/png", media.headers().get("content-type")); + } + + private Path pngFile() { + try { + Path png = Files.createTempFile("roller-upload", ".png"); + ImageIO.write(new BufferedImage(8, 8, BufferedImage.TYPE_INT_RGB), "png", png.toFile()); + return png; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void importBookmarks() { + goTo(BOOKMARKS_IMPORT_PAGE); + page.locator(OPML_FILE_INPUT).setInputFiles(opmlFile()); + page.locator(OPML_IMPORT_SUBMIT).click(); + + // a successful import lands on the blogroll page with a new + // imported- blogroll to switch to; poll rather than + // sampling the content once, the redirect can still be rendering + assertThat(page).hasTitle(Pattern.compile("Blogroll")); + page.waitForFunction("() => /imported-\\d+/.test(document.body.innerHTML)"); + } + + private Path opmlFile() { + try { + Path opml = Files.createTempFile("roller-bookmarks", ".opml"); + Files.writeString(opml, """ + + + Journey Blogroll + + + + + + """); + return opml; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/it-playwright/src/test/java/org/apache/roller/playwright/OidcLoginIT.java b/it-playwright/src/test/java/org/apache/roller/playwright/OidcLoginIT.java new file mode 100644 index 0000000000..7fbd48c37b --- /dev/null +++ b/it-playwright/src/test/java/org/apache/roller/playwright/OidcLoginIT.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ +package org.apache.roller.playwright; + +import java.util.regex.Pattern; + +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Signing in through an external identity provider. + * + *

Skipped unless Roller is running with an OIDC provider configured, so the + * same suite is usable against a database-authenticated instance. + */ +@DisplayName("OIDC login") +class OidcLoginIT extends BaseIT { + + private static final String ADMIN_USER = "admin"; + private static final String REGULAR_USER = "user"; + private static final String BLOG_HANDLE = "oidcblog"; + private static final String BLOG_NAME = "OIDC Blog"; + private static final String ENTRY_TITLE = "Signed in with OIDC"; + private static final String ENTRY_TEXT = "This entry was published by an OIDC-authenticated user."; + + // pages + private static final String LOGIN_PAGE = "roller-ui/login.rol"; + private static final String MENU_PAGE = "roller-ui/menu.rol"; + private static final String ADMIN_PAGE = "roller-ui/admin/globalConfig.rol"; + private static final String CREATE_WEBLOG_PAGE = "roller-ui/createWeblog.rol"; + + // create-weblog form + private static final String WEBLOG_NAME = "#createWeblog_bean_name"; + private static final String WEBLOG_HANDLE = "#createWeblog_bean_handle"; + private static final String WEBLOG_EMAIL = "#createWeblog_bean_emailAddress"; + private static final String WEBLOG_SUBMIT = "#createWeblog_0"; + + // Roller's login page + private static final String PROVIDER_BUTTON = "a[href*='/oauth2/authorization/']"; + private static final String SERVER_ADMIN_LINK = "Server administration"; + + // the provider's own login form + private static final Pattern PROVIDER_USERNAME_LABEL = + Pattern.compile("username", Pattern.CASE_INSENSITIVE); + private static final String PROVIDER_PASSWORD = "input[type='password']"; + private static final String PROVIDER_SUBMIT = "input[type='submit'], button[type='submit']"; + + @BeforeEach + void requireOidcProvider() { + goTo(LOGIN_PAGE); + assumeTrue(providerButton().count() > 0, + "Roller is not configured with an OIDC provider"); + } + + @Test + @DisplayName("an administrator lands on the main menu and can reach server administration") + void administratorSignsIn() { + signIn(ADMIN_USER, ADMIN_USER); + + assertThat(page).hasURL(baseUrl + MENU_PAGE); + Locator serverAdmin = page.getByRole(AriaRole.LINK, + new Page.GetByRoleOptions().setName(SERVER_ADMIN_LINK)); + assertThat(serverAdmin).isVisible(); + + serverAdmin.click(); + assertThat(page).hasURL(baseUrl + ADMIN_PAGE); + assertThat(page).hasTitle(Pattern.compile("Roller Configuration")); + } + + @Test + @DisplayName("a non-administrator is denied server administration") + void nonAdministratorIsDeniedAdmin() { + signIn(REGULAR_USER, REGULAR_USER); + + assertThat(page).hasURL(baseUrl + MENU_PAGE); + assertThat(page.getByRole(AriaRole.LINK, + new Page.GetByRoleOptions().setName(SERVER_ADMIN_LINK))).hasCount(0); + + // hiding the link is not the control; the URL itself must be refused + var response = page.navigate(ADMIN_PAGE); + assertThat(page).hasTitle(Pattern.compile("Access Denied")); + Assertions.assertEquals(403, response.status(), + "direct navigation to the admin page should be forbidden"); + } + + /** + * The authoring journey as an OIDC user: create a weblog, publish an entry + * and read it back on the blog. Also a regression test: rendered weblog + * pages resolve the signed-in user from the principal name, which for OIDC + * users was the provider's opaque subject ID; every weblog page then + * failed with a Velocity error. + */ + @Test + @DisplayName("an OIDC user can create a weblog, publish an entry and read it while signed in") + void oidcUserCanPublish() { + signIn(ADMIN_USER, ADMIN_USER); + + // create the weblog unless an earlier run against this instance already did + if (page.navigate(BLOG_HANDLE + "/").status() == 404) { + goTo(CREATE_WEBLOG_PAGE); + page.locator(WEBLOG_NAME).fill(BLOG_NAME); + page.locator(WEBLOG_HANDLE).fill(BLOG_HANDLE); + page.locator(WEBLOG_EMAIL).fill(ADMIN_USER + "@example.com"); + page.locator(WEBLOG_SUBMIT).click(); + } + + publishEntry(BLOG_HANDLE, ENTRY_TITLE, ENTRY_TEXT); + + var response = page.navigate(BLOG_HANDLE + "/"); + Assertions.assertEquals(200, response.status()); + assertThat(page.locator("body")).not().containsText("Velocity template error"); + assertEntryOnBlog(BLOG_HANDLE, ENTRY_TITLE, ENTRY_TEXT); + } + + /** Clicks through Roller's provider button and the provider's own login form. */ + private void signIn(String username, String password) { + goTo(LOGIN_PAGE); + providerButton().first().click(); + + page.getByLabel(PROVIDER_USERNAME_LABEL).first().fill(username); + page.locator(PROVIDER_PASSWORD).first().fill(password); + page.locator(PROVIDER_SUBMIT).first().click(); + } + + private Locator providerButton() { + return page.locator(PROVIDER_BUTTON); + } +} diff --git a/it-playwright/src/test/java/org/apache/roller/playwright/WebServicesIT.java b/it-playwright/src/test/java/org/apache/roller/playwright/WebServicesIT.java new file mode 100644 index 0000000000..61f13deea1 --- /dev/null +++ b/it-playwright/src/test/java/org/apache/roller/playwright/WebServicesIT.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ +package org.apache.roller.playwright; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.regex.Pattern; + +import com.microsoft.playwright.APIResponse; +import com.microsoft.playwright.options.RequestOptions; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Smoke tests for the web service endpoints whose servlets were forked from + * javax-only libraries during the Jakarta migration: the Blogger/MetaWeblog + * XML-RPC API and the Atom Publishing Protocol. Nothing else exercises that + * forked code, so these prove the endpoints still answer. + * + *

Runs after {@link NewUserJourneyIT} (alphabetical run order) and reuses + * the account and weblog it created. Both APIs ship disabled, so the test + * first turns them on from the server admin page. Skips on an instance + * without form login, because both APIs authenticate against database + * accounts. + */ +@DisplayName("Web services smoke") +class WebServicesIT extends BaseIT { + + private static final String USERNAME = "bsmith"; + private static final String PASSWORD = "roller123"; + private static final String BLOG_HANDLE = "bobsblog"; + + private static final String LOGIN_USERNAME = "input[name='j_username']"; + private static final String LOGIN_PASSWORD = "input[name='j_password']"; + private static final String LOGIN_SUBMIT = "#loginForm input[type='submit'], #loginForm button[type='submit']"; + + private static final String GLOBAL_CONFIG_PAGE = "roller-ui/admin/globalConfig.rol"; + private static final String XMLRPC_ENABLED = "input[name='webservices.enableXmlRpc']"; + private static final String ATOMPUB_ENABLED = "input[name='webservices.enableAtomPub']"; + private static final String GLOBAL_CONFIG_SAVE = "#saveButton"; + + private static final String XMLRPC_ENDPOINT = "roller-services/xmlrpc"; + private static final String ATOMPUB_ENDPOINT = "roller-services/app"; + + @BeforeEach + void enableWebServices() { + goTo("roller-ui/login.rol"); + boolean formLogin = page.locator(LOGIN_USERNAME).count() > 0; + if ("oidc".equals(expectedAuth()) || expectedAuth().isEmpty()) { + assumeTrue(formLogin, + "no form login: the XML-RPC and AtomPub APIs authenticate against database accounts"); + } else { + Assertions.assertTrue(formLogin, + "form login expected for auth method " + expectedAuth() + " but the login page has none"); + } + + page.locator(LOGIN_USERNAME).fill(USERNAME); + page.locator(LOGIN_PASSWORD).fill(PASSWORD); + page.locator(LOGIN_SUBMIT).first().click(); + goTo("roller-ui/menu.rol"); + boolean signedIn = page.title().contains("Your Weblogs"); + if (expectedAuth().isEmpty()) { + assumeTrue(signedIn, + "the journey's account is not available: this smoke test needs the fresh install NewUserJourneyIT sets up"); + } else { + Assertions.assertTrue(signedIn, "could not sign in as the account NewUserJourneyIT registered"); + } + + // both APIs ship disabled; the journey's first user is the admin + goTo(GLOBAL_CONFIG_PAGE); + page.locator(XMLRPC_ENABLED).check(); + page.locator(ATOMPUB_ENABLED).check(); + page.locator(GLOBAL_CONFIG_SAVE).click(); + assertThat(page.locator(XMLRPC_ENABLED)).isChecked(); + } + + @Test + @DisplayName("XML-RPC lists the user's weblogs and MetaWeblog publishes an entry") + void xmlRpcCanListBlogsAndPost() { + APIResponse listing = postXml(bloggerGetUsersBlogs()); + Assertions.assertEquals(200, listing.status(), listing.text()); + String body = listing.text(); + Assertions.assertFalse(body.contains("faultCode"), body); + Assertions.assertTrue(body.contains(BLOG_HANDLE), body); + + String title = "Posted via MetaWeblog"; + APIResponse posted = postXml(metaWeblogNewPost(title, "This entry arrived over XML-RPC.")); + Assertions.assertEquals(200, posted.status(), posted.text()); + Assertions.assertFalse(posted.text().contains("faultCode"), posted.text()); + + // the entry it created is the newest one rendered on the blog + goTo(BLOG_HANDLE + "/"); + assertThat(page.locator("p.entryTitle, .entryTitle").first()).containsText(title); + } + + @Test + @DisplayName("AtomPub serves the account's service document over basic auth") + void atomPubServesServiceDocument() { + String credentials = Base64.getEncoder() + .encodeToString((USERNAME + ":" + PASSWORD).getBytes(StandardCharsets.UTF_8)); + APIResponse response = page.request().get(baseUrl + ATOMPUB_ENDPOINT, + RequestOptions.create().setHeader("Authorization", "Basic " + credentials)); + Assertions.assertEquals(200, response.status(), response.text()); + + String body = response.text(); + Assertions.assertTrue(Pattern.compile("<(\\w+:)?service").matcher(body).find(), + "expected an AtomPub service document, got: " + body); + Assertions.assertTrue(body.contains(BLOG_HANDLE), body); + } + + private APIResponse postXml(String methodCall) { + return page.request().post(baseUrl + XMLRPC_ENDPOINT, RequestOptions.create() + .setHeader("Content-Type", "text/xml") + .setData(methodCall)); + } + + private static String bloggerGetUsersBlogs() { + return """ + + + blogger.getUsersBlogs + + ignored + %s + %s + + + """.formatted(USERNAME, PASSWORD); + } + + private static String metaWeblogNewPost(String title, String description) { + return """ + + + metaWeblog.newPost + + %s + %s + %s + + title%s + description%s + + 1 + + + """.formatted(BLOG_HANDLE, USERNAME, PASSWORD, title, description); + } +} diff --git a/it-selenium/src/test/resources/roller-jettyrun.properties b/it-playwright/src/test/resources/roller-jettyrun.properties similarity index 82% rename from it-selenium/src/test/resources/roller-jettyrun.properties rename to it-playwright/src/test/resources/roller-jettyrun.properties index a1edf99f42..d411eca771 100644 --- a/it-selenium/src/test/resources/roller-jettyrun.properties +++ b/it-playwright/src/test/resources/roller-jettyrun.properties @@ -23,13 +23,13 @@ hibernate.transaction.factory_class=org.hibernate.transaction.JDBCTransactionFac passwds.encryption.enabled=false # use src copy of themes for read-only access -themes.dir=target/war/work/org.apache.roller/roller-webapp/themes +themes.dir=${project.build.directory}/war/work/org.apache.roller/roller-webapp/themes # put work in work dir -search.index.dir =target/work/search-index -uploads.dir =target/work/uploadsdir -mediafiles.storage.dir =target/work/mediafiles -log4j.appender.roller.File=target/work/roller.log +search.index.dir =${project.build.directory}/work/search-index +uploads.dir =${project.build.directory}/work/uploadsdir +mediafiles.storage.dir =${project.build.directory}/work/mediafiles +log4j.appender.roller.File=${project.build.directory}/work/roller.log # don't auto migrate during tests uploads.migrate.auto=false diff --git a/it-selenium/nbactions.xml b/it-selenium/nbactions.xml deleted file mode 100644 index e0ead434bd..0000000000 --- a/it-selenium/nbactions.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - run - clean install with geckos - - clean - install - - - - - debug - debug clean install with geckos - - clean - install - - - maven - - - - diff --git a/it-selenium/pom.xml b/it-selenium/pom.xml deleted file mode 100644 index a1880ce44f..0000000000 --- a/it-selenium/pom.xml +++ /dev/null @@ -1,211 +0,0 @@ - - - 4.0.0 - - - org.apache.roller - roller-project - 6.1.5 - ../pom.xml - - - Roller Selenium tests - roller-selenium-tests - war - - - - - - org.seleniumhq.selenium - selenium-java - 4.28.0 - test - - - - org.seleniumhq.selenium - selenium-firefox-driver - 4.28.0 - - - jakarta.servlet - jakarta.servlet-api - - - xerces - xercesImpl - - - xalan - xalan - - - test - - - - jakarta.xml.bind - jakarta.xml.bind-api - 4.0.2 - test - - - - - - - junit - junit - 4.13.2 - test - - - - - org.apache.roller - roller-webapp - ${project.version} - war - - - - - - - - - - maven-war-plugin - 3.4.0 - - - - - maven-failsafe-plugin - 3.5.2 - - - - integration-test - verify - - - - - - - - org.eclipse.jetty.ee11 - jetty-ee11-maven-plugin - - selenium - 9999 - - /roller - - target/roller-selenium-tests-${project.version} - - ${project.parent.basedir}/app/src/test/resources/jetty.xml - - - - src/test/resources/roller-jettyrun.properties - - - - - - start-jetty - pre-integration-test - - start - - - - false - - - - stop-jetty - post-integration-test - - stop - - - - - - org.apache.roller - db-utils - 6.1.5 - - - org.apache.commons - commons-dbcp2 - 2.13.0 - - - org.apache.derby - derby - ${derby.version} - - - org.apache.derby - derbyclient - ${derby.version} - - - jakarta.mail - jakarta.mail-api - 2.1.3 - - - org.eclipse.angus - angus-mail - 2.0.3 - - - - - - - diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/AbstractRollerPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/AbstractRollerPage.java deleted file mode 100644 index 53a76996f3..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/AbstractRollerPage.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium; - -import java.time.Duration; -import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.Select; -import org.openqa.selenium.support.ui.WebDriverWait; - -/** - * Abstract class holding functionality common to Selenium Page Objects - * used in Roller. - */ -public abstract class AbstractRollerPage { - - protected WebDriver driver; - - protected void verifyPageTitle(String waitForElementId, String pageTitle) { - - WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); - wait.until( ExpectedConditions.visibilityOf( driver.findElement(By.id(waitForElementId)))); - - verifyPageTitle(pageTitle); - } - - protected void verifyPageTitle(String pageTitle) { - - if (!driver.getTitle().equals(pageTitle)) { - throw new IllegalStateException("This is not the " + pageTitle + ", current page is: " + driver.getTitle()); - } - System.out.println("Verified page title: " + pageTitle); - } - - /* - * Alternative method of identifying a page, by an HTML ID uniquely on it. - * Use when multiple views share the same page title. This method will require - * adding an id to an element specific to that page if one not already available. - */ - protected void verifyIdOnPage(String idOnPage) { - try { - driver.findElement(By.id(idOnPage)); - } catch (NoSuchElementException e) { - throw new IllegalStateException("HTML ID: " + idOnPage + " not found."); - } - } - - protected void setFieldValue(String fieldId, String value) { - WebElement field = driver.findElement(By.id(fieldId)); - field.clear(); - field.sendKeys(value); - } - - protected void clickById(String buttonId) { - WebElement element = driver.findElement(By.id(buttonId)); - System.out.println("clicking element " + element.getTagName() + " id:" + element.getAttribute("id")); - element.click(); - } - - protected void clickByLinkText(String buttonText) { - WebElement element = driver.findElement(By.linkText(buttonText)); - System.out.println("clicking element " + element.getTagName() + " id:" + element.getAttribute("id")); - element.click(); - } - - protected String getTextByCSS(String cssSelector) { - return driver.findElement(By.cssSelector(cssSelector)).getText(); - } - - protected String getTextById(String fieldId) { - return driver.findElement(By.id(fieldId)).getText(); - } - - protected void selectOptionByVisibleText(String selectId, String visibleText) { - Select select = new Select(driver.findElement(By.id(selectId))); - select.selectByVisibleText(visibleText); - } -} \ No newline at end of file diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/InitialLoginTestIT.java b/it-selenium/src/test/java/org/apache/roller/selenium/InitialLoginTestIT.java deleted file mode 100644 index c766c15b7e..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/InitialLoginTestIT.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium; - -import java.awt.GraphicsEnvironment; -import java.util.concurrent.TimeUnit; -import org.apache.roller.selenium.core.CreateWeblogPage; -import org.apache.roller.selenium.core.LoginPage; -import org.apache.roller.selenium.core.MainMenuPage; -import org.apache.roller.selenium.core.RegisterPage; -import org.apache.roller.selenium.core.SetupPage; -import org.apache.roller.selenium.core.WelcomePage; -import org.apache.roller.selenium.editor.EntryAddPage; -import org.apache.roller.selenium.editor.EntryEditPage; -import org.apache.roller.selenium.view.BlogHomePage; -import org.apache.roller.selenium.view.SingleBlogEntryPage; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.Alert; -import org.openqa.selenium.By; -import org.openqa.selenium.NoAlertPresentException; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.firefox.FirefoxDriver; -import org.openqa.selenium.firefox.FirefoxOptions; -import org.openqa.selenium.firefox.FirefoxProfile; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class InitialLoginTestIT { - private WebDriver driver; - private String baseUrl; - private boolean acceptNextAlert = true; - private StringBuffer verificationErrors = new StringBuffer(); - - @Before - public void setUp() throws Exception { - FirefoxProfile profile = new FirefoxProfile(); - profile.setPreference("intl.accept_languages", "en_US"); - - FirefoxOptions options = new FirefoxOptions(); - if (GraphicsEnvironment.isHeadless()) { - options.addArguments("-headless"); - } - options.setProfile(profile); - - driver = new FirefoxDriver(options); - driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS) - .pageLoadTimeout(5, TimeUnit.SECONDS) - .setScriptTimeout(5, TimeUnit.SECONDS); - baseUrl = "http://localhost:8080/roller/"; - } - - @Test - public void testInitialLogin() throws Exception { - // create new user and first blog - driver.get(baseUrl); - SetupPage sp = new SetupPage(driver); - RegisterPage rp = sp.createNewUser(); - WelcomePage wp = rp.submitUserRegistration("bsmith", "Bob Smith", "bsmith@email.com", "roller123"); - - LoginPage lp = wp.doRollerLogin(); - MainMenuPage mmp = lp.loginToRoller("bsmith", "roller123"); - - CreateWeblogPage cwp = mmp.createWeblog(); - cwp.createWeblog("Bob's Blog", "bobsblog", "bsmith@email.com"); - - // set bobsblog as the front page blog - driver.get(baseUrl); - sp = new SetupPage(driver); - driver.navigate().refresh(); - BlogHomePage bhp = sp.chooseFrontPageBlog(); - - // create and read first blog entry - String blogEntryTitle = "My First Blog Entry"; - String blogEntryContent = "Welcome to my blog!"; - EntryAddPage eap = bhp.createNewBlogEntry(); - eap.setTitle(blogEntryTitle); - eap.setText(blogEntryContent); - EntryEditPage eep = eap.postBlogEntry(); - - SingleBlogEntryPage sbep = eep.viewBlogEntry(); - System.out.println("title/text: " + sbep.getBlogTitle() + " / " + sbep.getBlogText()); - assertEquals(blogEntryTitle, sbep.getBlogTitle()); - assertEquals(blogEntryContent, sbep.getBlogText()); - } - - - @After - public void tearDown() throws Exception { - String verificationErrorString = verificationErrors.toString(); - if (!"".equals(verificationErrorString)) { - fail(verificationErrorString); - } - } - - private boolean isElementPresent(By by) { - try { - driver.findElement(by); - return true; - } catch (NoSuchElementException e) { - return false; - } - } - - private boolean isAlertPresent() { - try { - driver.switchTo().alert(); - return true; - } catch (NoAlertPresentException e) { - return false; - } - } - - private String closeAlertAndGetItsText() { - try { - Alert alert = driver.switchTo().alert(); - String alertText = alert.getText(); - if (acceptNextAlert) { - alert.accept(); - } else { - alert.dismiss(); - } - return alertText; - } finally { - acceptNextAlert = true; - } - } -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/core/CreateWeblogPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/core/CreateWeblogPage.java deleted file mode 100644 index 3bd6b81d86..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/core/CreateWeblogPage.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.core; - -import org.apache.roller.selenium.AbstractRollerPage; -import org.openqa.selenium.WebDriver; - -/** - * represents core/CreateWeblog.jsp - */ -public class CreateWeblogPage extends AbstractRollerPage { - - // additional properties needed: locale, timezone, theme - - public CreateWeblogPage(WebDriver driver) { - this.driver = driver; - String pageTitle = "Front Page: Create Weblog"; - verifyPageTitle("createWeblog", pageTitle); - } - - public MainMenuPage createWeblog() { - clickById("createWeblog_0"); - return new MainMenuPage(driver); - } - - public MainMenuPage createWeblog(String name, String handle, String email) { - setName(name); - setHandle(handle); - setEmail(email); - setLocale("English"); - return createWeblog(); - } - - public void setName(String value) { - setFieldValue("createWeblog_bean_name", value); - } - - public void setDescription(String value) { - setFieldValue("createWeblog_bean_description", value); - } - - public void setHandle(String value) { - setFieldValue("createWeblog_bean_handle", value); - } - - public void setEmail(String value) { - setFieldValue("createWeblog_bean_emailAddress", value); - } - - public void setLocale(String value) { - selectOptionByVisibleText("createWeblog_bean_locale", value); - } - -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/core/LoginPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/core/LoginPage.java deleted file mode 100644 index 91c0168533..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/core/LoginPage.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.core; - -import org.apache.roller.selenium.AbstractRollerPage; -import org.openqa.selenium.WebDriver; - -/** - * represents core/login.jsp - * Page Object that handles user login to Roller - */ -public class LoginPage extends AbstractRollerPage { - - public LoginPage(WebDriver driver) { - this.driver = driver; - String pageTitle = "Front Page: Welcome to Roller"; - verifyPageTitle("loginForm", pageTitle); - } - - public MainMenuPage loginToRoller() { - clickById("login"); - return new MainMenuPage(driver); - } - - public MainMenuPage loginToRoller(String username, String password) { - setUsername(username); - setPassword(password); - return loginToRoller(); - } - - public void setUsername(String username) { - setFieldValue("j_username", username); - } - - public void setPassword(String password) { - setFieldValue("j_password", password); - } - -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/core/MainMenuPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/core/MainMenuPage.java deleted file mode 100644 index 33c9b6aa95..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/core/MainMenuPage.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.core; - -import org.apache.roller.selenium.AbstractRollerPage; -import org.apache.roller.selenium.editor.EntryAddPage; -import org.openqa.selenium.WebDriver; - -/** - * represents core/MainMenu.jsp - * Post-login page object to create, choose, configure blogs. - */ -public class MainMenuPage extends AbstractRollerPage { - String pageTitle; - - public MainMenuPage(WebDriver driver) { - this.driver = driver; - pageTitle = "Front Page: Your Weblogs"; - } - - public CreateWeblogPage createWeblog() { - verifyPageTitle("createWeblogLink", pageTitle); - clickByLinkText("create one?"); - return new CreateWeblogPage(driver); - } - - public EntryAddPage createNewBlogEntry() { - verifyPageTitle("createWeblogLink", pageTitle); - clickByLinkText("New Entry"); - return new EntryAddPage(driver); - } - -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/core/RegisterPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/core/RegisterPage.java deleted file mode 100644 index ea50a9c5bf..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/core/RegisterPage.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.core; - -import java.time.Duration; -import org.apache.roller.selenium.AbstractRollerPage; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; - -/** - * represents core/Register.jsp - * index page that does step #1 of setup.jsp, the adding of a new user - */ -public class RegisterPage extends AbstractRollerPage { - - public RegisterPage(WebDriver driver) { - this.driver = driver; - String pageTitle = "Front Page: New User Registration"; - - verifyPageTitle("register", pageTitle); - } - - public WelcomePage submitUserRegistration() { - clickById("submit"); - - WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); - wait.until( ExpectedConditions.visibilityOf( driver.findElement(By.id("a_clickHere")))); - driver.findElement(By.id("a_clickHere")).click(); - - return new WelcomePage(driver); - } - - public WelcomePage submitUserRegistration(String userAndScreenName, String fullName, String email, - String password) { - setUserName(userAndScreenName); - setScreenName(userAndScreenName); - setFullName(fullName); - setEmail(email); - setPassword(password); - setPasswordConfirm(password); - return submitUserRegistration(); - } - - public void setUserName(String value) { - setFieldValue("register_bean_userName", value); - } - - public void setScreenName(String value) { - setFieldValue("register_bean_screenName", value); - } - - public void setFullName(String value) { - setFieldValue("register_bean_fullName", value); - } - - public void setEmail(String value) { - setFieldValue("register_bean_emailAddress", value); - } - - public void setPassword(String value) { - setFieldValue("register_bean_passwordText", value); - } - - public void setPasswordConfirm(String value) { - setFieldValue("register_bean_passwordConfirm", value); - } -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/core/SetupPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/core/SetupPage.java deleted file mode 100644 index 8359dff863..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/core/SetupPage.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.core; - -import org.apache.roller.selenium.AbstractRollerPage; -import org.apache.roller.selenium.view.BlogHomePage; -import org.openqa.selenium.WebDriver; - -/** - * represents core/Setup.jsp - * Page Object encapsulates the Setup.jsp index page - * that appears on initial startup of Roller, after - * the database tables were created but before the - * first user account and blog made. - */ -public class SetupPage extends AbstractRollerPage { - - public SetupPage(WebDriver driver) { - this.driver = driver; - } - - public RegisterPage createNewUser() { - verifyPageTitle("Front Page: Welcome to Roller!"); - clickById("a_createUser"); - return new RegisterPage(driver); - } - - public BlogHomePage chooseFrontPageBlog() { - verifyPageTitle("setup_0", "Front Page: Welcome to Roller!"); - clickById("setup_0"); - return new BlogHomePage(driver); - } -} \ No newline at end of file diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/core/WelcomePage.java b/it-selenium/src/test/java/org/apache/roller/selenium/core/WelcomePage.java deleted file mode 100644 index 5a5fa34116..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/core/WelcomePage.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.core; - -import org.apache.roller.selenium.AbstractRollerPage; -import org.openqa.selenium.WebDriver; - -/** - * represents core/Welcome.jsp - * Page Object after creation of a new user account (RegisterPage) - * Provides a button to proceed to the login page or notification that new - * account must be confirmed via email first. - */ -public class WelcomePage extends AbstractRollerPage { - - public WelcomePage(WebDriver driver) { - this.driver = driver; - String pageTitle = "Front Page: Welcome to Roller"; - verifyPageTitle("loginForm", pageTitle); - } - - public LoginPage doRollerLogin() { - return new LoginPage(driver); - } - -} - diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/editor/AbstractEntryPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/editor/AbstractEntryPage.java deleted file mode 100644 index 9f87782d25..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/editor/AbstractEntryPage.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.editor; - -import org.apache.roller.selenium.AbstractRollerPage; -import org.openqa.selenium.By; - -/** - * Base class for the new/edit entry pages - */ -public abstract class AbstractEntryPage extends AbstractRollerPage { - - public void setTitle(String value) { - setFieldValue("entry_bean_title", value); - } - - public void setText(String value) { - setFieldValue("edit_content", value); - } - - public void setSummary(String value) { - setFieldValue("entry_bean_summary", value); - } - - public EntryEditPage postBlogEntry() { - // the publish button is the only success-styled submit on the page; - // its generated id changed between form-theme versions - driver.findElement(By.cssSelector("input.btn-success[type='submit']")).click(); - return new EntryEditPage(driver); - } -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/editor/EntryAddPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/editor/EntryAddPage.java deleted file mode 100644 index 9639802d5f..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/editor/EntryAddPage.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.editor; - -import org.openqa.selenium.WebDriver; - -/** - * represents editor/EntryAddPage.jsp - * Page for adding a new blog entry - */ -public class EntryAddPage extends AbstractEntryPage { - - public EntryAddPage(WebDriver driver) { - this.driver = driver; - verifyPageTitle("entry", "Front Page: New Entry"); - } - -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/editor/EntryEditPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/editor/EntryEditPage.java deleted file mode 100644 index 1abeeb111d..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/editor/EntryEditPage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.editor; - -import org.apache.roller.selenium.view.SingleBlogEntryPage; -import org.openqa.selenium.WebDriver; - -/** - * represents editor/EntryEditPage.jsp - * Page for editing an already saved (draft or posted) blog entry - */ -public class EntryEditPage extends AbstractEntryPage { - - // need to read fields on screen. - - public EntryEditPage(WebDriver driver) { - this.driver = driver; - verifyPageTitle("messages", "Front Page: Edit Entry"); - } - - public SingleBlogEntryPage viewBlogEntry() { - clickById("entry_bean_permalink"); - return new SingleBlogEntryPage(driver); - } -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/view/BlogHomePage.java b/it-selenium/src/test/java/org/apache/roller/selenium/view/BlogHomePage.java deleted file mode 100644 index 82b307a768..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/view/BlogHomePage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.view; - -import org.apache.roller.selenium.AbstractRollerPage; -import org.apache.roller.selenium.editor.EntryAddPage; -import org.openqa.selenium.WebDriver; - -/** - * Represents a URL for the home page of a blog - * (URL similar to http://localhost:8080/roller/myblog) - */ -public class BlogHomePage extends AbstractRollerPage { - - public BlogHomePage(WebDriver driver) { - this.driver = driver; - verifyPageTitle("searchForm", "Bob's Blog"); - } - - public EntryAddPage createNewBlogEntry() { - clickByLinkText("New Entry"); - return new EntryAddPage(driver); - } - -} diff --git a/it-selenium/src/test/java/org/apache/roller/selenium/view/SingleBlogEntryPage.java b/it-selenium/src/test/java/org/apache/roller/selenium/view/SingleBlogEntryPage.java deleted file mode 100644 index e96a76202a..0000000000 --- a/it-selenium/src/test/java/org/apache/roller/selenium/view/SingleBlogEntryPage.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. The ASF licenses this file to You - * under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. For additional information regarding - * copyright in this work, please see the NOTICE file in the top level - * directory of this distribution. - */ -package org.apache.roller.selenium.view; - -import org.apache.roller.selenium.AbstractRollerPage; -import org.openqa.selenium.WebDriver; - -/** - * represents a URL that displays a single blog entry - * (URL similar to http://localhost:8080/roller/myblog/entry/my_blog_entry) - */ -public class SingleBlogEntryPage extends AbstractRollerPage { - - public SingleBlogEntryPage(WebDriver driver) { - this.driver = driver; - String pageTitle = "Single blog entry view"; - /* id_permalink added to basic template's permalink.vm only to distinguish - this page from by-month or by-day views of blog entries */ - verifyIdOnPage("id_permalink"); - } - - public String getBlogTitle() { - return getTextByCSS("p.entryTitle"); - } - - public String getBlogText() { - return getTextByCSS("p.entryContent"); - } - -} diff --git a/pom.xml b/pom.xml index 5f3e9e918e..cc3f9e6f01 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,8 @@ limitations under the License. app db-utils - +