使用Java Selenium选择下拉选项后提交按钮未启用问题
问题描述
我用Java Selenium做前端自动化测试,页面有两个下拉菜单(whiteplayername和blackplayername)和一个提交按钮(submit-button),规则是两个下拉都选完选项后,提交按钮才会启用。手动操作页面功能正常,但跑Selenium测试时,明明选了两个下拉选项,提交按钮却一直没启用。我怀疑是选择操作没触发页面事件,初始测试代码如下:
@Test @DisplayName("When whitePlayerName and blackPlayerName is selected, submit button is active") void tesSubmitPage_whenNameIsSelectedFromBothDropDownMenus_submitButtonIsEnabled() { List<WebElement> elements = driver.findElements(By.className("material-icons")); elements.get(1).click(); WebElement dropdownWhite = driver.findElement(By.id("whiteplayername")); Select selectWhite = new Select(dropdownWhite); selectWhite.selectByValue("Hywel Griffiths"); WebElement dropdownBlack = driver.findElement(By.id("blackplayername")); Select selectBlack = new Select(dropdownBlack); selectBlack.selectByValue("Hywel Griffiths"); WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); wait.until(ExpectedConditions.elementToBeClickable(By.id("submit-button"))); WebElement button = driver.findElement(By.id("submit-button")); assertTrue("", button.isEnabled()); }
页面关联的JavaScript代码:
<script type="module"> document.getElementById("submit-button").disabled = true; const form = document.getElementById("form"); const submitButton = document.getElementById("submit-button"); const whitePlayerName = document.getElementById("whiteplayername"); const blackPlayerName = document.getElementById("blackplayername"); const firstNameBlack = document.getElementById("firstnameblack"); const firstNameWhite = document.getElementById("firstnamewhite"); const lastNameWhite = document.getElementById("lastnamewhite"); const lastNameBlack = document.getElementById("lastnameblack"); form.addEventListener('change', () => { const whiteNameSelected = whitePlayerName.value; // Disable white name inputs if white name is selected in the dropdown firstNameWhite.disabled = whiteNameSelected; lastNameWhite.disabled = whiteNameSelected; // Determine if the white name is either selected or typed in the inputs const validWhiteName = whiteNameSelected || (firstNameWhite.value && lastNameWhite.value); const blackNameSelected = blackPlayerName.value; // Disable black name inputs if black name is selected in the dropdown firstNameBlack.disabled = blackNameSelected; lastNameBlack.disabled = blackNameSelected; // Determine if the black name is either selected or typed in the inputs const validBlackName = blackNameSelected || (firstNameBlack.value && lastNameBlack.value); const submitAvailable = validWhiteName && validBlackName && validSpaces; submitButton.disabled = !submitAvailable; if (!validWhiteName) { document.getElementById("warningtext").textContent = "Please select at least one white name."; } else if (!validBlackName) { document.getElementById("warningtext").textContent = "Please select at least one black name."; } else if (!validSpaces) { document.getElementById("warningtext").textContent = "Please do not included spaces in the names."; } else { document.getElementById("warningtext").textContent = ""; } }); form.addEventListener('input', () => { const whiteNameSelected = whitePlayerName.value; // Disable white name inputs if white name is selected in the dropdown firstNameWhite.disabled = whiteNameSelected; lastNameWhite.disabled = whiteNameSelected; // Determine if the white name is either selected or typed in the inputs const validWhiteName = whiteNameSelected || (firstNameWhite.value && lastNameWhite.value); const blackNameSelected = blackPlayerName.value; // Disable black name inputs if black name is selected in the dropdown firstNameBlack.disabled = blackNameSelected; lastNameBlack.disabled = blackNameSelected; // Determine if the black name is either selected or typed in the inputs const validBlackName = blackNameSelected || (firstNameBlack.value && lastNameBlack.value); const validSpaces = !lastNameWhite.value.match(/\s+/g) && !firstNameWhite.value.match(/\s+/g) && !lastNameBlack.value.match(/\s+/g) && !firstNameBlack.value.match(/\s+/g); const submitAvailable = validWhiteName && validBlackName && validSpaces; submitButton.disabled = !submitAvailable; if (!validWhiteName) { document.getElementById("warningtext").textContent = "Please select at least one white name."; } else if (!validBlackName) { document.getElementById("warningtext").textContent = "Please select at least one black name."; } else if (!validSpaces) { document.getElementById("warningtext").textContent = "Please do not included spaces in the names."; } else { document.getElementById("warningtext").textContent = ""; } }); </script>
对应的Thymeleaf HTML代码:
<body style="background-color:rgb(68, 57, 57);"> <div class="warning"> <p id="warningtext"></p><br> </div> <div class="topnav"> <a th:href="@{main.html}"><i class="material-icons" style="border:2px;font-size:60px;color:rgb(0, 0, 0);">arrow_back</i></a> </div> <div class="formformat"> <form th:object="${game}" th:action="@{/addgame}" th:method="post" id="form"> <label for="whiteplayername">Select white player:</label> <select name="whiteplayername" id="whiteplayername" th:object="${names}" th:field="${game.whitePlayerName}"> <option th:value="null" th:selected="${game.name == null}"></option> <th:block th:each="name : ${names}"> <option th:value="${name.name}" th:text="${name.name}"></option> </th:block> </select> <label for="blackplayername">Select black player:</label> <select name="blackplayername" id="blackplayername" th:object="${names}" th:field="${game.blackPlayerName}"> <option th:value="null" th:selected="${game.name == null}"></option> <th:block th:each="name : ${names}"> <option th:value="${name.name}" th:text="${name.name}"></option> </th:block> </select><br><br> <label for="firstnamewhite">First name white:</label> <input type="text" id="firstnamewhite" th:field="*{firstNameWhite}"/> <label for="firstnameblack">First name black:</label> <input type="text" id="firstnameblack" th:field="*{firstNameBlack}"/><br><br> <label for="lastnamewhite">Last name white:</label> <input type="text" id="lastnamewhite" th:field="*{lastNameWhite}"/> <label for="lastnameblack">Last name black:</label> <input type="text" id="lastnameblack" th:field="*{lastNameBlack}"/><br><br> <label for="date">Date:</label><br> <input type="date" id="date" th:field="*{date}"> <table> <tr> <th>Move</th> <th>White</th> <th>Black</th> </tr> <tr> <td>1</td> <td><input type="text" id="white1" th:field="*{moves}"></td> <td><input type="text" id="black1" th:field="*{moves}"></td> </tr> </table> <input type="submit" value="Submit" id="submit-button"> </form>
我试过修改测试代码,用JavascriptExecutor触发表单的change事件,但问题还是没解决,修改后的测试代码如下:
@Test @DisplayName("When whitePlayerName and blackPlayerName is selected, submit button is active") void tesSubmitPage_whenNameIsSelectedFromBothDropDownMenus_submitButtonIsEnabled() throws InterruptedException { List<WebElement> elements = driver.findElements(By.className("material-icons")); elements.get(1).click(); JavascriptExecutor js = (JavascriptExecutor) driver; WebElement whitePlayerNameDropdown = driver.findElement(By.id("whiteplayername")); WebElement form = driver.findElement(By.id("form")); Select whitePlayerNameSelect = new Select(whitePlayerNameDropdown); whitePlayerNameSelect.selectByValue("Hywel Griffiths"); js.executeScript("arguments[0].dispatchEvent(new Event('change'))", form); WebElement blackPlayerNameDropdown = driver.findElement(By.id("blackplayername")); Select blackPlayerNameSelect = new Select(blackPlayerNameDropdown); blackPlayerNameSelect.selectByValue("Hywel Griffiths"); js.executeScript("arguments[0].dispatchEvent(new Event('change'))", form); WebElement button = driver.findElement(By.id("submit-button")); assertTrue("", button.isEnabled()); }
需要找出问题并解决。
问题原因及解决办法
1. 核心问题:validSpaces变量未定义导致逻辑中断
看你提供的JavaScript代码,form的change事件监听器里用到了validSpaces变量,但这个变量只在input事件监听器里定义过,change事件里完全没声明它。手动操作时,可能因为触发过输入框的input事件,变量被初始化;但Selenium只操作下拉菜单,不会触发输入框的input事件,导致validSpaces为undefined,validWhiteName && validBlackName && validSpaces结果为false,提交按钮因此一直处于禁用状态。
2. 解决步骤
步骤一:修复JavaScript代码的变量定义问题
把验证逻辑抽成独立函数,让两个事件监听器都调用这个函数,既避免重复代码,也解决变量未定义问题:
<script type="module"> document.getElementById("submit-button").disabled = true; const form = document.getElementById("form"); const submitButton = document.getElementById("submit-button"); const whitePlayerName = document.getElementById("whiteplayername"); const blackPlayerName = document.getElementById("blackplayername"); const firstNameBlack = document.getElementById("firstnameblack"); const firstNameWhite = document.getElementById("firstnamewhite"); const lastNameWhite = document.getElementById("lastnamewhite"); const lastNameBlack = document.getElementById("lastnameblack"); // 抽离验证逻辑为独立函数 function validateForm() { const whiteNameSelected = whitePlayerName.value; firstNameWhite.disabled = whiteNameSelected; lastNameWhite.disabled = whiteNameSelected; const validWhiteName = whiteNameSelected || (firstNameWhite.value && lastNameWhite.value); const blackNameSelected = blackPlayerName.value; firstNameBlack.disabled = blackNameSelected; lastNameBlack.disabled = blackNameSelected; const validBlackName = blackNameSelected || (firstNameBlack.value && lastNameBlack.value); const validSpaces = !lastNameWhite.value.match(/\s+/g) && !firstNameWhite.value.match(/\s+/g) && !lastNameBlack.value.match(/\s+/g) && !firstNameBlack.value.match(/\s+/g); const submitAvailable = validWhiteName && validBlackName && validSpaces; submitButton.disabled = !submitAvailable; if (!validWhiteName) { document.getElementById("warningtext").textContent = "Please select at least one white name."; } else if (!validBlackName) { document.getElementById("warningtext").textContent = "Please select at least one black name."; } else if (!validSpaces) { document.getElementById("warningtext").textContent = "Please do not include spaces in the names."; } else { document.getElementById("warningtext").textContent = ""; } } // 两个事件都调用同一验证函数 form.addEventListener('change', validateForm); form.addEventListener('input', validateForm); </script>
步骤二:优化Selenium测试代码
- 直接触发下拉元素的
change事件,更贴近用户实际操作; - 保留显式等待,确保按钮状态更新后再断言:
@Test @DisplayName("When whitePlayerName and blackPlayerName is selected, submit button is active") void tesSubmitPage_whenNameIsSelectedFromBothDropDownMenus_submitButtonIsEnabled() { List<WebElement> elements = driver.findElements(By.className("material-icons")); elements.get(1).click(); JavascriptExecutor js = (JavascriptExecutor) driver; // 处理白方下拉 WebElement whiteDropdown = driver.findElement(By.id("whiteplayername")); Select whiteSelect = new Select(whiteDropdown); whiteSelect.selectByValue("Hywel Griffiths"); // 触发下拉元素的change事件(带冒泡,确保父元素能捕获) js.executeScript("arguments[0].dispatchEvent(new Event('change', {bubbles: true}))", whiteDropdown); // 处理黑方下拉 WebElement blackDropdown = driver.findElement(By.id("blackplayername")); Select blackSelect = new Select(blackDropdown); blackSelect.selectByValue("Hywel Griffiths"); js.executeScript("arguments[0].dispatchEvent(new Event('change', {bubbles: true}))", blackDropdown); // 显式等待按钮启用 WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); WebElement submitButton = wait.until(driver -> { WebElement btn = driver.findElement(By.id("submit-button")); return btn.isEnabled() ? btn : null; }); assertTrue(submitButton.isEnabled()); }
3. 额外注意点
- HTML中Thymeleaf的
th:selected="${game.name == null}"可能有误,应该对应game.whitePlayerName == null和game.blackPlayerName == null,不过这和当前问题无关,但可能影响初始状态; - 手动操作时触发过输入框
input事件,掩盖了change事件里的变量未定义问题,Selenium的操作刚好暴露了这个代码漏洞。
内容的提问来源于stack exchange,提问作者Hywel Griffiths
相关产品推荐
相关产品推荐

