Migrate remaining Selenide UI tests to Playwright#70
Conversation
Remove all Selenide test infrastructure (18 Java files, 1,664 lines) and consolidate on Playwright as the single E2E framework. All Selenide test scenarios were already covered by existing Playwright specs; two new validation tests (empty fields, long names) were added to fill a gap. Key changes: - Delete Selenide test classes, page objects, utilities, and JDBC helpers - Remove selenide and webdrivermanager dependencies from build.gradle - Remove uiTest Gradle task and excludeTags 'ui' filter - Replace all networkidle waits with domcontentloaded across 14 Playwright spec files (fixes timeout failures caused by WebAuthn endpoints returning 500 on pages that load webauthn JS) - Add profile update validation tests to update-profile.spec.ts - Fix pre-existing Instant/Date type mismatch in TestDataController - Update EmailVerificationEdgeCaseTest to use userRepository instead of deleted DatabaseStateValidator - Update CLAUDE.md and README.md to reflect Playwright as E2E framework Closes #67
Fixes WebAuthn 500 errors caused by missing user_credentials table (see devondragon/SpringUserFramework#286). Also removes unused DSUserDetails constant from ApiTestData that caused NPE with the new constructor signature.
The registration form uses async fetch (no page navigation), so waitForLoadState returns immediately. Wait for the error element to become visible instead of checking synchronously.
There was a problem hiding this comment.
Pull request overview
This PR completes the migration away from legacy Selenide-based UI tests by removing the remaining Selenide test infrastructure and consolidating E2E coverage under Playwright, while also addressing WebAuthn-related flakiness/timeouts and a test-data type mismatch.
Changes:
- Removed remaining Selenide UI test classes, page objects, and JDBC-based helpers; updated Gradle to drop Selenide/WebDriverManager and the
uiTesttask. - Updated Playwright specs/page objects to use
domcontentloaded(instead ofnetworkidle) and added two profile-update validation scenarios. - Upgraded
ds-spring-user-frameworkto4.3.1and fixedInstant/Datemismatch inTestDataController.
Reviewed changes
Copilot reviewed 41 out of 42 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/test/resources/application-test.properties | Removed unused test.browser setting tied to deleted Selenide tests. |
| src/test/java/com/digitalsanctuary/spring/user/ui/page/SuccessResetPasswordPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/user/ui/page/ForgotPasswordPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/user/security/EmailVerificationEdgeCaseTest.java | Replaced DB validator usage with repository lookups after validator deletion. |
| src/test/java/com/digitalsanctuary/spring/user/jdbc/Jdbc.java | Deleted Selenide-era JDBC helper used for UI test setup/cleanup. |
| src/test/java/com/digitalsanctuary/spring/user/jdbc/ConnectionManager.java | Deleted Selenide-era JDBC connection manager. |
| src/test/java/com/digitalsanctuary/spring/user/api/data/ApiTestData.java | Removed unused DSUserDetails test constant/import. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/util/EmailVerificationSimulator.java | Deleted Selenide-era utility for simulating email verification. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/util/DatabaseStateValidator.java | Deleted Selenide-era DB state assertion helper. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/VerificationPendingPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/UpdateUserPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/UpdatePasswordPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/SuccessResetPasswordPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/SuccessRegisterPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/RegisterPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/LoginSuccessPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/LoginPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/ForgotPasswordPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/page/DeleteAccountPage.java | Deleted legacy Selenide page object. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/data/UiTestData.java | Deleted Selenide UI test data helper. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/SpringUserFrameworkUiTest.java | Deleted remaining Selenide UI test class. |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/CompleteUserJourneyE2ETest.java | Deleted comprehensive Selenide E2E suite (now covered by Playwright). |
| src/test/java/com/digitalsanctuary/spring/demo/user/ui/BaseUiTest.java | Deleted Selenide base test harness (drivers/config). |
| src/main/java/com/digitalsanctuary/spring/demo/test/api/TestDataController.java | Fixed Instant→Date mismatch when setting registration date. |
| playwright/tests/profile/update-profile.spec.ts | Switched waits to domcontentloaded and added profile validation scenarios. |
| playwright/tests/profile/delete-account.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/profile/change-password.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/profile/auth-methods.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/events/event-registration.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/events/browse-events.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/e2e/complete-user-journey.spec.ts | Switched waits to domcontentloaded (incl. page.reload({ waitUntil })). |
| playwright/tests/auth/registration.spec.ts | Fixed async error assertion (wait for error element vs navigation) and updated load-state wait. |
| playwright/tests/auth/passwordless-registration.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/auth/password-reset.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/auth/login.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/auth/email-verification.spec.ts | Switched waits to domcontentloaded. |
| playwright/tests/access-control/protected-pages.spec.ts | Switched waits to domcontentloaded. |
| playwright/src/pages/EventDetailsPage.ts | Updated post-reload wait from networkidle to domcontentloaded. |
| playwright/src/pages/BasePage.ts | Updated common “wait” helpers to use domcontentloaded. |
| build.gradle | Upgraded framework to 4.3.1, removed Selenide deps, removed uiTest task and tag exclusions. |
| README.md | Updated docs to reflect Playwright as the E2E/UI framework and removed uiTest guidance. |
| CLAUDE.md | Updated contributor guidance to reflect Playwright E2E usage and removed uiTest guidance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| await updateUserPage.submit(); | ||
|
|
||
| // HTML5 required validation should prevent submission | ||
| // or server should return an error - verify no unhandled crash |
There was a problem hiding this comment.
The new empty-fields “validation” test doesn’t actually assert any validation behavior. After clearing the inputs and clicking submit, it only checks the URL, so it would still pass even if the form submits and the server accepts empty values (or if an error is rendered incorrectly). Consider asserting a concrete outcome (e.g., inputs remain :invalid / have required attributes, or #firstNameError/#lastNameError/#globalMessage becomes visible with an error state).
| // or server should return an error - verify no unhandled crash | |
| // or server should return an error - verify no unhandled crash | |
| // Assert that the inputs are considered invalid by the browser | |
| const firstNameIsValid = await updateUserPage.firstNameInput.evaluate( | |
| (el: HTMLInputElement) => el.checkValidity() | |
| ); | |
| const lastNameIsValid = await updateUserPage.lastNameInput.evaluate( | |
| (el: HTMLInputElement) => el.checkValidity() | |
| ); | |
| expect(firstNameIsValid).toBe(false); | |
| expect(lastNameIsValid).toBe(false); |
| await updateUserPage.updateProfile(longName, longName); | ||
|
|
||
| // Wait for server response | ||
| await page.waitForLoadState('domcontentloaded'); | ||
|
|
There was a problem hiding this comment.
updateProfile(longName, longName) doesn’t wait for the async result, and the subsequent waitForLoadState('domcontentloaded') is likely a no-op (DOMContentLoaded already fired on initial navigation). This can make the test flaky and it won’t reliably detect server-side validation failures. Prefer waiting on a deterministic signal (e.g., updateProfileAndWait(...), page.waitForResponse(...), or asserting that a success/error message or field-level error becomes visible).
| await updateUserPage.updateProfile(longName, longName); | |
| // Wait for server response | |
| await page.waitForLoadState('domcontentloaded'); | |
| await updateUserPage.updateProfileAndWait(longName, longName); |
| // Try to change to a weak password | ||
| await updatePasswordPage.changePassword(user.password, 'weak'); | ||
| await page.waitForLoadState('networkidle'); | ||
| await page.waitForLoadState('domcontentloaded'); | ||
|
|
There was a problem hiding this comment.
After submitting the weak password change, page.waitForLoadState('domcontentloaded') doesn’t wait for the form POST/fetch to complete (and may resolve immediately if the page already reached that state). This can cause the test to proceed before the UI updates. Prefer waiting for a specific response (waitForResponse on the update endpoint) or for #globalMessage / field error locators to become visible.
| await page.waitForLoadState('domcontentloaded'); | ||
|
|
||
| // Should show error or validation message (client-side validation) |
There was a problem hiding this comment.
This test submits mismatched passwords but then only waits for domcontentloaded, which won’t reliably wait for client-side validation or an async POST result (and may resolve immediately). To make the test meaningful and non-flaky, assert a concrete validation outcome (e.g., #confirmPasswordError visible, the confirm input is :invalid, or a global error/message appears).
| await page.waitForLoadState('domcontentloaded'); | |
| // Should show error or validation message (client-side validation) | |
| // Wait for client-side validation / error message | |
| await updatePasswordPage.waitForMessage(5000); | |
| const hasError = await updatePasswordPage.isErrorMessage(); | |
| expect(hasError).toBe(true); |
- Empty field submission: assert HTML5 checkValidity() instead of just URL - Long name submission: use updateProfileAndWait() and assert message visible - Weak password: wait for globalMessage with sufficient timeout before asserting error - Mismatched passwords: assert confirmPasswordError element is visible
Copilot feedback addressedAll 4 Copilot review comments have been fixed in the latest commit (
Full Playwright suite still passes 99/99 after these changes. |
Summary
networkidlewithdomcontentloadedacross all 14 Playwright spec files, fixing ~68 tests that were timing out due to WebAuthn endpoints returning 500user_credentialstable (see WebAuthnCredential table not created: VARBINARY(65535) exceeds MariaDB row size limit SpringUserFramework#286)Instant/Datetype mismatch inTestDataControllerChanges
build.gradleselenide,webdrivermanagerdeps; removeduiTesttask andexcludeTags 'ui'; upgraded framework to 4.3.1networkidlewithdomcontentloaded; fixed async error assertionTestDataController:Date.from(Instant.now());EmailVerificationEdgeCaseTest: useuserRepositoryinstead of deletedDatabaseStateValidator;ApiTestData: remove unusedDSUserDetailsfieldCLAUDE.mdandREADME.mdto reflect Playwright as the E2E frameworkTest plan
./gradlew clean test— BUILD SUCCESSFUL (all Java tests pass)npx playwright test --project=chromium— 99 passed, 0 failedgrep -r "selenide\|Selenide\|uiTest" src/— no matchesuser_credentialstable auto-created on startup with framework 4.3.1Closes #67