From 1df40e5882b963ccaa44ff5e4c586a50a8517cb8 Mon Sep 17 00:00:00 2001 From: alanv Date: Wed, 10 Jun 2026 12:05:10 -0500 Subject: [PATCH 01/10] LabKeySiteWrapper: add attemptReauth --- src/org/labkey/test/LabKeySiteWrapper.java | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/LabKeySiteWrapper.java b/src/org/labkey/test/LabKeySiteWrapper.java index 90a67cf466..d84c3952eb 100644 --- a/src/org/labkey/test/LabKeySiteWrapper.java +++ b/src/org/labkey/test/LabKeySiteWrapper.java @@ -388,10 +388,17 @@ public void attemptSignIn(String email) public void attemptSignIn(String email, String password) { - if (isSignedIn()) + attemptSignIn(email, password, false); + } + + public void attemptSignIn(String email, String password, boolean isReAuth) + { + if (!isReAuth && isSignedIn()) throw new IllegalStateException("You need to be logged out to log in. Please log out to log in."); - if (!getDriver().getTitle().contains("Sign In")) + boolean onLoginPage = getDriver().getTitle().contains("Sign In"); + + if (!onLoginPage && !isReAuth) { try { @@ -403,13 +410,17 @@ public void attemptSignIn(String email, String password) throw new IllegalStateException("Unable to find \"Sign In\" link on current page.", error); } } + else if (!onLoginPage) + { + throw new IllegalStateException("Unable to Sign In, not already on the Sign In page"); + } assertTitleContains("Sign In"); assertElementPresent(Locator.tagWithName("form", "login")); setFormElement(Locator.id("email"), email); setFormElement(Locator.id("password"), password); - WebElement signInButton = Locator.button("Sign In").findElement(getDriver()); + WebElement signInButton = Locator.byClass("signin-btn").findElement(getDriver()); doAndMaybeWaitForPageToLoad(10_000, () -> { signInButton.click(); shortWait().until(ExpectedConditions.invisibilityOfElementLocated(Locator.byClass("signing-in-msg"))); @@ -420,6 +431,11 @@ public void attemptSignIn(String email, String password) }); } + public void attemptReauth() + { + attemptSignIn(PasswordUtil.getUsername(), PasswordUtil.getPassword(), true); + } + public void signInShouldFail(String email, String password, String... expectedMessages) { attemptSignIn(email, password); From d7ebf6578c47eabad16e83200e27d86fd112e53d Mon Sep 17 00:00:00 2001 From: alanv Date: Wed, 10 Jun 2026 16:34:11 -0500 Subject: [PATCH 02/10] Add SignInPage, use in LabKeySiteWrapper. Remove attemptReauth. --- src/org/labkey/test/LabKeySiteWrapper.java | 36 ++-------- .../test/pages/core/login/SignInPage.java | 70 +++++++++++++++++++ 2 files changed, 74 insertions(+), 32 deletions(-) create mode 100644 src/org/labkey/test/pages/core/login/SignInPage.java diff --git a/src/org/labkey/test/LabKeySiteWrapper.java b/src/org/labkey/test/LabKeySiteWrapper.java index d84c3952eb..41b752d1e6 100644 --- a/src/org/labkey/test/LabKeySiteWrapper.java +++ b/src/org/labkey/test/LabKeySiteWrapper.java @@ -54,6 +54,7 @@ import org.labkey.test.components.ui.navigation.UserMenu; import org.labkey.test.pages.core.admin.CustomizeSitePage; import org.labkey.test.pages.core.admin.ShowAdminPage; +import org.labkey.test.pages.core.login.SignInPage; import org.labkey.test.pages.user.UserDetailsPage; import org.labkey.test.util.APIUserHelper; import org.labkey.test.util.ApiPermissionsHelper; @@ -388,17 +389,10 @@ public void attemptSignIn(String email) public void attemptSignIn(String email, String password) { - attemptSignIn(email, password, false); - } - - public void attemptSignIn(String email, String password, boolean isReAuth) - { - if (!isReAuth && isSignedIn()) + if (isSignedIn()) throw new IllegalStateException("You need to be logged out to log in. Please log out to log in."); - boolean onLoginPage = getDriver().getTitle().contains("Sign In"); - - if (!onLoginPage && !isReAuth) + if (!getDriver().getTitle().contains("Sign In")) { try { @@ -410,30 +404,8 @@ public void attemptSignIn(String email, String password, boolean isReAuth) throw new IllegalStateException("Unable to find \"Sign In\" link on current page.", error); } } - else if (!onLoginPage) - { - throw new IllegalStateException("Unable to Sign In, not already on the Sign In page"); - } - assertTitleContains("Sign In"); - - assertElementPresent(Locator.tagWithName("form", "login")); - setFormElement(Locator.id("email"), email); - setFormElement(Locator.id("password"), password); - WebElement signInButton = Locator.byClass("signin-btn").findElement(getDriver()); - doAndMaybeWaitForPageToLoad(10_000, () -> { - signInButton.click(); - shortWait().until(ExpectedConditions.invisibilityOfElementLocated(Locator.byClass("signing-in-msg"))); - shortWait().until(ExpectedConditions.or( - ExpectedConditions.stalenessOf(signInButton), // Successful login - ExpectedConditions.presenceOfElementLocated(Locators.labkeyError.withText()))); // Error during sign-in - return ExpectedConditions.stalenessOf(signInButton).apply(null); - }); - } - - public void attemptReauth() - { - attemptSignIn(PasswordUtil.getUsername(), PasswordUtil.getPassword(), true); + new SignInPage(getDriver()).signIn(email, password); } public void signInShouldFail(String email, String password, String... expectedMessages) diff --git a/src/org/labkey/test/pages/core/login/SignInPage.java b/src/org/labkey/test/pages/core/login/SignInPage.java new file mode 100644 index 0000000000..df3c77156c --- /dev/null +++ b/src/org/labkey/test/pages/core/login/SignInPage.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2026 LabKey Corporation + * + * Licensed 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. + */ +package org.labkey.test.pages.core.login; + +import org.labkey.test.Locator; +import org.labkey.test.Locators; +import org.labkey.test.pages.LabKeyPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; + +/** + * Page object for the LabKey "Sign In" page. This object assumes the browser is already on the Sign In page; it does + * not handle navigating to it (e.g. clicking a "Sign In" link). + */ +public class SignInPage extends LabKeyPage +{ + public SignInPage(WebDriver driver) + { + super(driver); + } + + @Override + protected void waitForPage() + { + waitFor(() -> getDriver().getTitle().contains("Sign In") && isElementPresent(Locator.tagWithName("form", "login")), + "Sign In page did not load in time.", WAIT_FOR_PAGE); + } + + public void signIn(String userName, String password) + { + setFormElement(elementCache().emailInput, userName); + setFormElement(elementCache().passwordInput, password); + WebElement signInButton = elementCache().signInButton; + doAndMaybeWaitForPageToLoad(10_000, () -> { + signInButton.click(); + shortWait().until(ExpectedConditions.invisibilityOfElementLocated(Locator.byClass("signing-in-msg"))); + shortWait().until(ExpectedConditions.or( + ExpectedConditions.stalenessOf(signInButton), // Successful login + ExpectedConditions.presenceOfElementLocated(Locators.labkeyError.withText()))); // Error during sign-in + return ExpectedConditions.stalenessOf(signInButton).apply(null); + }); + } + + @Override + protected ElementCache newElementCache() + { + return new ElementCache(); + } + + protected class ElementCache extends LabKeyPage.ElementCache + { + WebElement emailInput = Locator.id("email").findWhenNeeded(getDriver()); + WebElement passwordInput = Locator.id("password").findWhenNeeded(getDriver()); + WebElement signInButton = Locator.byClass("signin-btn").findWhenNeeded(getDriver()); + } +} From 856da77059d37bb4cf35ddc15c319447c24365d6 Mon Sep 17 00:00:00 2001 From: alanv Date: Tue, 16 Jun 2026 21:09:38 -0500 Subject: [PATCH 03/10] ReactDateTimePicker: use refindWhenNeeded in order to prevent issues with stale elements --- src/org/labkey/test/components/react/ReactDateTimePicker.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/components/react/ReactDateTimePicker.java b/src/org/labkey/test/components/react/ReactDateTimePicker.java index 782fb6cdb4..c02b87ef93 100644 --- a/src/org/labkey/test/components/react/ReactDateTimePicker.java +++ b/src/org/labkey/test/components/react/ReactDateTimePicker.java @@ -201,8 +201,8 @@ protected ElementCache newElementCache() protected class ElementCache extends Component.ElementCache { WebElement inputContainer = Locator.tagWithClass("div", "react-datepicker__input-container") - .findElement(this); - public Input input = new Input(Locator.tag("input").findWhenNeeded(inputContainer), getDriver()); + .refindWhenNeeded(this); + public Input input = new Input(Locator.tag("input").refindWhenNeeded(inputContainer), getDriver()); WebElement popup = Locator.xpath(".").followingSibling("div").withClass("react-datepicker__tab-loop") .refindWhenNeeded(this); From 10ff78f6e3b5364b009b4ff2894e5296ec6e13fc Mon Sep 17 00:00:00 2001 From: alanv Date: Wed, 17 Jun 2026 09:07:43 -0500 Subject: [PATCH 04/10] SignInPage: PR Feedback --- src/org/labkey/test/pages/core/login/SignInPage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/pages/core/login/SignInPage.java b/src/org/labkey/test/pages/core/login/SignInPage.java index df3c77156c..3454790bee 100644 --- a/src/org/labkey/test/pages/core/login/SignInPage.java +++ b/src/org/labkey/test/pages/core/login/SignInPage.java @@ -61,7 +61,7 @@ protected ElementCache newElementCache() return new ElementCache(); } - protected class ElementCache extends LabKeyPage.ElementCache + protected class ElementCache extends LabKeyPage.ElementCache { WebElement emailInput = Locator.id("email").findWhenNeeded(getDriver()); WebElement passwordInput = Locator.id("password").findWhenNeeded(getDriver()); From 5f6e43383ba9d69ba1ba2c7406a0b2c718109a50 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 17 Jun 2026 20:51:38 -0700 Subject: [PATCH 05/10] Fix button text --- src/org/labkey/test/LabKeySiteWrapper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/LabKeySiteWrapper.java b/src/org/labkey/test/LabKeySiteWrapper.java index 41b752d1e6..02a3c82713 100644 --- a/src/org/labkey/test/LabKeySiteWrapper.java +++ b/src/org/labkey/test/LabKeySiteWrapper.java @@ -157,7 +157,7 @@ public void simpleSignIn() } else { - fillSignInFormAndSubmit(); + fillSignInFormAndSubmit("Sign In"); // verify we're signed in now if (!waitFor(() -> @@ -198,14 +198,14 @@ else if (errors.contains("log in and approve the terms of use.")) WebTestHelper.saveSession(PasswordUtil.getUsername(), getDriver()); } - public void fillSignInFormAndSubmit() + public void fillSignInFormAndSubmit(String buttonText) { - log("Signing in as " + PasswordUtil.getUsername()); + log(buttonText + " as " + PasswordUtil.getUsername()); assertElementPresent(Locator.tagWithName("form", "login")); setFormElement(Locator.name("email"), PasswordUtil.getUsername()); setFormElement(Locator.name("password"), PasswordUtil.getPassword()); acceptTermsOfUse(null, false); - clickButton("Sign In", 0); + clickButton(buttonText, 0); } /** From a97013c7405ebb4f5bc57226d652744f1c6e1ef8 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Fri, 19 Jun 2026 16:47:14 -0700 Subject: [PATCH 06/10] Add AbstractReauthTest and implement for DB auth --- src/org/labkey/test/LabKeySiteWrapper.java | 16 +- .../test/pages/test/TestReauthPage.java | 103 +++++++++++++ .../labkey/test/tests/AbstractReauthTest.java | 139 ++++++++++++++++++ .../test/tests/core/login/DbReauthTest.java | 57 +++++++ 4 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 src/org/labkey/test/pages/test/TestReauthPage.java create mode 100644 src/org/labkey/test/tests/AbstractReauthTest.java create mode 100644 src/org/labkey/test/tests/core/login/DbReauthTest.java diff --git a/src/org/labkey/test/LabKeySiteWrapper.java b/src/org/labkey/test/LabKeySiteWrapper.java index 02a3c82713..26d3eb52f3 100644 --- a/src/org/labkey/test/LabKeySiteWrapper.java +++ b/src/org/labkey/test/LabKeySiteWrapper.java @@ -200,12 +200,20 @@ else if (errors.contains("log in and approve the terms of use.")) public void fillSignInFormAndSubmit(String buttonText) { - log(buttonText + " as " + PasswordUtil.getUsername()); + fillSignInFormAndSubmit(buttonText, PasswordUtil.getUsername(), PasswordUtil.getPassword()); + } + + public void fillSignInFormAndSubmit(String buttonText, String username, String password) + { + log(buttonText + " as " + username); assertElementPresent(Locator.tagWithName("form", "login")); - setFormElement(Locator.name("email"), PasswordUtil.getUsername()); - setFormElement(Locator.name("password"), PasswordUtil.getPassword()); + setFormElement(Locator.name("email"), username); + setFormElement(Locator.name("password"), password); acceptTermsOfUse(null, false); - clickButton(buttonText, 0); + WebElement signInButton = Locator.byClass("signin-btn").findElement(getDriver()); + if (buttonText != null) + assertEquals("Wrong sign-in button text", buttonText, signInButton.getText()); + signInButton.click(); } /** diff --git a/src/org/labkey/test/pages/test/TestReauthPage.java b/src/org/labkey/test/pages/test/TestReauthPage.java new file mode 100644 index 0000000000..a9ef1a9b71 --- /dev/null +++ b/src/org/labkey/test/pages/test/TestReauthPage.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2026 LabKey Corporation + * + * Licensed 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. + */ +package org.labkey.test.pages.test; + +import org.apache.hc.core5.http.HttpStatus; +import org.labkey.test.Locator; +import org.labkey.test.WebDriverWrapper; +import org.labkey.test.WebTestHelper; +import org.labkey.test.pages.LabKeyPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.Map; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class TestReauthPage extends LabKeyPage +{ + public TestReauthPage(WebDriver driver) + { + super(driver); + } + + public static TestReauthPage beginAt(WebDriverWrapper webDriverWrapper) + { + webDriverWrapper.beginAt(WebTestHelper.buildURL("test", "home", "testReauth")); + return new TestReauthPage(webDriverWrapper.getDriver()); + } + + public static TestReauthPage beginAt(WebDriverWrapper webDriverWrapper, String reauthToken) + { + webDriverWrapper.beginAt(WebTestHelper.buildURL("test", "home", "testReauth", Map.of("reauthToken", reauthToken))); + return new TestReauthPage(webDriverWrapper.getDriver()); + } + + public String getDescription() + { + if (elementCache().description.isDisplayed()) + return elementCache().description.getText(); + else + return ""; + } + + public void clickReauth() + { + clickAndWait(elementCache().reauthLink); + clearCache(); + } + + public String getReauthToken() + { + return elementCache().reauthTokenInput() + .map(el -> el.getDomProperty("value")).orElse(""); + } + + public void validateToken() + { + clickAndWait(elementCache().validateButton); + clearCache(); + assertNoLabKeyErrors(); + assertEquals("Response code", HttpStatus.SC_OK, getResponseCode()); + } + + public void validateTokenExpectingError() + { + clickAndWait(elementCache().validateButton); + clearCache(); + assertNotEquals("Response code", HttpStatus.SC_OK, getResponseCode()); + } + + + @Override + protected ElementCache newElementCache() + { + return new ElementCache(); + } + + protected class ElementCache extends LabKeyPage.ElementCache + { + final WebElement description = Locator.id("description").findWhenNeeded(this); + final WebElement reauthLink = Locator.id("link").findWhenNeeded(this); + final Optional reauthTokenInput() + { + return Locator.name("reauthToken").findOptionalElement(this); + } + final WebElement validateButton = Locator.tagWithAttribute("input", "value", "Sign!").findWhenNeeded(this); + } +} diff --git a/src/org/labkey/test/tests/AbstractReauthTest.java b/src/org/labkey/test/tests/AbstractReauthTest.java new file mode 100644 index 0000000000..508f70b3bf --- /dev/null +++ b/src/org/labkey/test/tests/AbstractReauthTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2018-2026 LabKey Corporation + * + * Licensed 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. + */ +package org.labkey.test.tests; + +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.Locator; +import org.labkey.test.Locators; +import org.labkey.test.pages.test.TestReauthPage; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +public abstract class AbstractReauthTest extends BaseWebDriverTest +{ + public record User(String email, String password) { } + + private final User user1; + private final User user2; + + protected AbstractReauthTest(User user1, User user2) + { + this.user1 = user1; + this.user2 = user2; + } + + protected abstract void clickSignIn(); + protected abstract void authenticate(String email, String password); + protected abstract void authenticateExpectingError(String email, String password); + + @Test + public void testReauth() + { + signInAs(user1); + + TestReauthPage testReauthPage = TestReauthPage.beginAt(this); + testReauthPage.clickReauth(); + authenticate(user1.email, user1.password); + testReauthPage.validateToken(); + + // Reauth again as the same user to ensure newer token is recognized + testReauthPage = TestReauthPage.beginAt(this); + testReauthPage.clickReauth(); + authenticate(user1.email, user1.password); + testReauthPage.validateToken(); + } + + @Test + public void testReuseReauthToken() + { + signInAs(user1); + + TestReauthPage testReauthPage = TestReauthPage.beginAt(this); + testReauthPage.clickReauth(); + authenticate(user1.email, user1.password); + String reauthToken = testReauthPage.getReauthToken(); + testReauthPage.validateToken(); + + testReauthPage = TestReauthPage.beginAt(this, reauthToken); + testReauthPage.validateTokenExpectingError(); + assertElementPresent(Locator.byClass("labkey-error-heading").withText("Reauthentication validation failed!")); + } + + @Test + public void testReauthAsWrongUser() + { + signInAs(user1); + + TestReauthPage testReauthPage = TestReauthPage.beginAt(this); + testReauthPage.clickReauth(); + authenticate(user2.email, user2.password); + assertElementPresent(Locators.labkeyError.containing("wrong user reauthenticated")); + + testReauthPage.clickReauth(); // Try again + authenticate(user1.email, user1.password); + testReauthPage.validateToken(); + } + + /** + * Test that reauth works when fixing the password after logging in with the wrong password + */ + @Test + public void testReauthWithBadPassword() + { + signInAs(user1); + + TestReauthPage testReauthPage = TestReauthPage.beginAt(this); + testReauthPage.clickReauth(); + authenticateExpectingError(user1.email, user1.password + "wrong"); + + authenticate(user1.email, user1.password); + testReauthPage.validateToken(); + } + + private void signInAs(User user) + { + signOut(); + clickSignIn(); + authenticate(user.email, user.password); + assertSignedInAs(user); + } + + private void assertSignedInAs(User user) + { + Assert.assertEquals("Signed in as", user.email, getCurrentUser()); + } + + @Override + protected String getProjectName() + { + return null; + } + + @Override + public List getAssociatedModules() + { + return Arrays.asList(); + } +} diff --git a/src/org/labkey/test/tests/core/login/DbReauthTest.java b/src/org/labkey/test/tests/core/login/DbReauthTest.java new file mode 100644 index 0000000000..40d0ac4567 --- /dev/null +++ b/src/org/labkey/test/tests/core/login/DbReauthTest.java @@ -0,0 +1,57 @@ +package org.labkey.test.tests.core.login; + +import org.junit.BeforeClass; +import org.labkey.test.Locator; +import org.labkey.test.tests.AbstractReauthTest; +import org.labkey.test.util.PasswordUtil; +import org.labkey.test.util.TestUser; + +public class DbReauthTest extends AbstractReauthTest +{ + private static final TestUser USER1 = new TestUser("db_user1@reauth.test"); + private static final TestUser USER2 = new TestUser("db_user2@reauth.test"); + + public DbReauthTest() + { + super(new User(USER1.getEmail(), PasswordUtil.getPassword()), new User(USER2.getEmail(), PasswordUtil.getPassword())); + } + + @Override + protected void clickSignIn() + { + clickAndWait(Locator.tagWithClass("a", "header-link").withText("Sign In")); + } + + @Override + protected void authenticate(String email, String password) + { + doAndWaitForPageToLoad(() -> fillSignInFormAndSubmit(null, email, password)); + } + + @Override + protected void authenticateExpectingError(String email, String password) + { + fillSignInFormAndSubmit(null, email, password); + } + + @Override + protected void doCleanup(boolean afterTest) + { + _userHelper.deleteUsers(afterTest, USER1, USER2); + } + + @BeforeClass + public static void setupProject() throws Exception + { + DbReauthTest init = getCurrentTest(); + + init.doSetup(); + } + + private void doSetup() + { + USER1.create(this).setInitialPassword(); + USER2.create(this).setInitialPassword(); + } + +} From 91e3074801a8b6dd5ed047c93ae8b757c2f23432 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 22 Jun 2026 15:37:46 -0700 Subject: [PATCH 07/10] Reauth test improvements --- .../test/pages/test/TestReauthPage.java | 25 +++++++++++++++---- .../labkey/test/tests/AbstractReauthTest.java | 23 +++++++++-------- .../test/tests/core/login/DbReauthTest.java | 9 +++++++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/org/labkey/test/pages/test/TestReauthPage.java b/src/org/labkey/test/pages/test/TestReauthPage.java index a9ef1a9b71..d9008599f5 100644 --- a/src/org/labkey/test/pages/test/TestReauthPage.java +++ b/src/org/labkey/test/pages/test/TestReauthPage.java @@ -17,6 +17,7 @@ import org.apache.hc.core5.http.HttpStatus; import org.labkey.test.Locator; +import org.labkey.test.Locators; import org.labkey.test.WebDriverWrapper; import org.labkey.test.WebTestHelper; import org.labkey.test.pages.LabKeyPage; @@ -50,10 +51,7 @@ public static TestReauthPage beginAt(WebDriverWrapper webDriverWrapper, String r public String getDescription() { - if (elementCache().description.isDisplayed()) - return elementCache().description.getText(); - else - return ""; + return elementCache().description.getText(); } public void clickReauth() @@ -65,7 +63,7 @@ public void clickReauth() public String getReauthToken() { return elementCache().reauthTokenInput() - .map(el -> el.getDomProperty("value")).orElse(""); + .map(el -> el.getDomProperty("value")).orElse(null); } public void validateToken() @@ -83,6 +81,17 @@ public void validateTokenExpectingError() assertNotEquals("Response code", HttpStatus.SC_OK, getResponseCode()); } + public String getReauthError() + { + if (elementCache().reauthLink.isDisplayed()) + { + return elementCache().reauthLink.getText(); + } + else + { + return ""; + } + } @Override protected ElementCache newElementCache() @@ -92,12 +101,18 @@ protected ElementCache newElementCache() protected class ElementCache extends LabKeyPage.ElementCache { + public ElementCache() + { + waitFor(description::isDisplayed, "Page failed to load", 5_000); + } + final WebElement description = Locator.id("description").findWhenNeeded(this); final WebElement reauthLink = Locator.id("link").findWhenNeeded(this); final Optional reauthTokenInput() { return Locator.name("reauthToken").findOptionalElement(this); } + final WebElement reauthError = Locators.labkeyError.findWhenNeeded(this); final WebElement validateButton = Locator.tagWithAttribute("input", "value", "Sign!").findWhenNeeded(this); } } diff --git a/src/org/labkey/test/tests/AbstractReauthTest.java b/src/org/labkey/test/tests/AbstractReauthTest.java index 508f70b3bf..eec4f517e1 100644 --- a/src/org/labkey/test/tests/AbstractReauthTest.java +++ b/src/org/labkey/test/tests/AbstractReauthTest.java @@ -15,21 +15,17 @@ */ package org.labkey.test.tests; -import org.junit.Assert; -import org.junit.Assume; -import org.junit.BeforeClass; -import org.junit.Before; +import org.assertj.core.api.Assertions; import org.junit.Test; -import org.junit.experimental.categories.Category; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; -import org.labkey.test.Locators; import org.labkey.test.pages.test.TestReauthPage; import java.util.Arrays; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; public abstract class AbstractReauthTest extends BaseWebDriverTest { @@ -47,6 +43,7 @@ protected AbstractReauthTest(User user1, User user2) protected abstract void clickSignIn(); protected abstract void authenticate(String email, String password); protected abstract void authenticateExpectingError(String email, String password); + protected abstract String getAuthDescription(); @Test public void testReauth() @@ -54,14 +51,19 @@ public void testReauth() signInAs(user1); TestReauthPage testReauthPage = TestReauthPage.beginAt(this); + assertEquals("Authentication method", getAuthDescription(), testReauthPage.getDescription()); testReauthPage.clickReauth(); authenticate(user1.email, user1.password); + String reauthToken1 = testReauthPage.getReauthToken(); + assertEquals("Authentication method", getAuthDescription(), testReauthPage.getDescription()); testReauthPage.validateToken(); - // Reauth again as the same user to ensure newer token is recognized + // Reauth again as the same user to ensure a new token is generated testReauthPage = TestReauthPage.beginAt(this); testReauthPage.clickReauth(); authenticate(user1.email, user1.password); + String reauthToken2 = testReauthPage.getReauthToken(); + assertNotEquals("Reauth should generate a new token each time", reauthToken1, reauthToken2); testReauthPage.validateToken(); } @@ -89,7 +91,7 @@ public void testReauthAsWrongUser() TestReauthPage testReauthPage = TestReauthPage.beginAt(this); testReauthPage.clickReauth(); authenticate(user2.email, user2.password); - assertElementPresent(Locators.labkeyError.containing("wrong user reauthenticated")); + Assertions.assertThat(testReauthPage.getReauthError()).as("Reauth error").contains("wrong user reauthenticated"); testReauthPage.clickReauth(); // Try again authenticate(user1.email, user1.password); @@ -122,7 +124,8 @@ private void signInAs(User user) private void assertSignedInAs(User user) { - Assert.assertEquals("Signed in as", user.email, getCurrentUser()); + if (!waitFor(() -> getCurrentUser().equals(user.email), 1_000)) + assertEquals("Signed in as", user.email, getCurrentUser()); } @Override diff --git a/src/org/labkey/test/tests/core/login/DbReauthTest.java b/src/org/labkey/test/tests/core/login/DbReauthTest.java index 40d0ac4567..5092a7df40 100644 --- a/src/org/labkey/test/tests/core/login/DbReauthTest.java +++ b/src/org/labkey/test/tests/core/login/DbReauthTest.java @@ -1,11 +1,14 @@ package org.labkey.test.tests.core.login; import org.junit.BeforeClass; +import org.junit.experimental.categories.Category; import org.labkey.test.Locator; +import org.labkey.test.categories.Daily; import org.labkey.test.tests.AbstractReauthTest; import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.TestUser; +@Category({Daily.class}) public class DbReauthTest extends AbstractReauthTest { private static final TestUser USER1 = new TestUser("db_user1@reauth.test"); @@ -34,6 +37,12 @@ protected void authenticateExpectingError(String email, String password) fillSignInFormAndSubmit(null, email, password); } + @Override + protected String getAuthDescription() + { + return "Standard database authentication"; + } + @Override protected void doCleanup(boolean afterTest) { From e469958d7cc079043b9e42c062b9e4bd4bc704f6 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 22 Jun 2026 17:12:12 -0700 Subject: [PATCH 08/10] Add CASReauthTest --- src/org/labkey/test/tests/AbstractReauthTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/org/labkey/test/tests/AbstractReauthTest.java b/src/org/labkey/test/tests/AbstractReauthTest.java index eec4f517e1..6fd6883d55 100644 --- a/src/org/labkey/test/tests/AbstractReauthTest.java +++ b/src/org/labkey/test/tests/AbstractReauthTest.java @@ -16,6 +16,7 @@ package org.labkey.test.tests; import org.assertj.core.api.Assertions; +import org.junit.Assume; import org.junit.Test; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; @@ -86,6 +87,8 @@ public void testReuseReauthToken() @Test public void testReauthAsWrongUser() { + Assume.assumeTrue("Authentication method doesn't have two logins available; skipping wrong-user reauth test", user2 != null); + signInAs(user1); TestReauthPage testReauthPage = TestReauthPage.beginAt(this); From 63826a488a98692df2233ff94024e4c3f2a03364 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 22 Jun 2026 17:39:09 -0700 Subject: [PATCH 09/10] Add LdapReauthTest --- src/org/labkey/test/pages/test/TestReauthPage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/pages/test/TestReauthPage.java b/src/org/labkey/test/pages/test/TestReauthPage.java index d9008599f5..f32afc79a8 100644 --- a/src/org/labkey/test/pages/test/TestReauthPage.java +++ b/src/org/labkey/test/pages/test/TestReauthPage.java @@ -83,9 +83,9 @@ public void validateTokenExpectingError() public String getReauthError() { - if (elementCache().reauthLink.isDisplayed()) + if (elementCache().reauthError.isDisplayed()) { - return elementCache().reauthLink.getText(); + return elementCache().reauthError.getText(); } else { From 06b0f7d3334ba0e3a7fb11d9826df066f294d71d Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 22 Jun 2026 17:44:28 -0700 Subject: [PATCH 10/10] Share some code --- .../tests/AbstractLoginFormReauthTest.java | 48 +++++++++++++++++++ .../test/tests/core/login/DbReauthTest.java | 25 +--------- 2 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 src/org/labkey/test/tests/AbstractLoginFormReauthTest.java diff --git a/src/org/labkey/test/tests/AbstractLoginFormReauthTest.java b/src/org/labkey/test/tests/AbstractLoginFormReauthTest.java new file mode 100644 index 0000000000..738456f646 --- /dev/null +++ b/src/org/labkey/test/tests/AbstractLoginFormReauthTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026 LabKey Corporation + * + * Licensed 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. + */ +package org.labkey.test.tests; + +import org.labkey.test.Locator; + +/** + * Base class for reauth tests that authenticate via the standard LabKey username/password form, + * as opposed to SSO providers (SAML, CAS) that redirect to an external identity provider. + */ +public abstract class AbstractLoginFormReauthTest extends AbstractReauthTest +{ + protected AbstractLoginFormReauthTest(User user1, User user2) + { + super(user1, user2); + } + + @Override + protected void clickSignIn() + { + clickAndWait(Locator.tagWithClass("a", "header-link").withText("Sign In")); + } + + @Override + protected void authenticate(String email, String password) + { + doAndWaitForPageToLoad(() -> fillSignInFormAndSubmit(null, email, password)); + } + + @Override + protected void authenticateExpectingError(String email, String password) + { + fillSignInFormAndSubmit(null, email, password); + } +} diff --git a/src/org/labkey/test/tests/core/login/DbReauthTest.java b/src/org/labkey/test/tests/core/login/DbReauthTest.java index 5092a7df40..56f078005a 100644 --- a/src/org/labkey/test/tests/core/login/DbReauthTest.java +++ b/src/org/labkey/test/tests/core/login/DbReauthTest.java @@ -2,14 +2,13 @@ import org.junit.BeforeClass; import org.junit.experimental.categories.Category; -import org.labkey.test.Locator; import org.labkey.test.categories.Daily; -import org.labkey.test.tests.AbstractReauthTest; +import org.labkey.test.tests.AbstractLoginFormReauthTest; import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.TestUser; @Category({Daily.class}) -public class DbReauthTest extends AbstractReauthTest +public class DbReauthTest extends AbstractLoginFormReauthTest { private static final TestUser USER1 = new TestUser("db_user1@reauth.test"); private static final TestUser USER2 = new TestUser("db_user2@reauth.test"); @@ -19,24 +18,6 @@ public DbReauthTest() super(new User(USER1.getEmail(), PasswordUtil.getPassword()), new User(USER2.getEmail(), PasswordUtil.getPassword())); } - @Override - protected void clickSignIn() - { - clickAndWait(Locator.tagWithClass("a", "header-link").withText("Sign In")); - } - - @Override - protected void authenticate(String email, String password) - { - doAndWaitForPageToLoad(() -> fillSignInFormAndSubmit(null, email, password)); - } - - @Override - protected void authenticateExpectingError(String email, String password) - { - fillSignInFormAndSubmit(null, email, password); - } - @Override protected String getAuthDescription() { @@ -53,7 +34,6 @@ protected void doCleanup(boolean afterTest) public static void setupProject() throws Exception { DbReauthTest init = getCurrentTest(); - init.doSetup(); } @@ -62,5 +42,4 @@ private void doSetup() USER1.create(this).setInitialPassword(); USER2.create(this).setInitialPassword(); } - }