From b0d45e01d3d97e1225869d31c1ceb004367ae83a Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 7 Jun 2024 12:53:32 +0200 Subject: [PATCH 1/3] Log the race condition fix --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72c7e2b..3e82f1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ Dropping a requirement of a major version of a dependency is a new contract. ## [Unreleased] [Unreleased]: https://github.com/atlassian/docker-infrastructure/compare/release-0.3.8...master +### Fixed +- Fix race condition between screen recording and stopping container. + ## [0.3.8] - 2024-01-12 [0.3.8]: https://github.com/atlassian/docker-infrastructure/compare/release-0.3.7...release-0.3.8 From 4db23bf29b47560c9f8da456c23927d51d57ef40 Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 7 Jun 2024 13:11:27 +0200 Subject: [PATCH 2/3] Wait for sample project key to be filled in before clicking submit Sometimes WD was too fast, filling in the project name and clicking submit, before Jira frontend filled in the project key. --- CHANGELOG.md | 1 + .../dockerinfrastructure/jira/SetUpFromScratchAction.kt | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e82f1d..ec7b2be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Dropping a requirement of a major version of a dependency is a new contract. ### Fixed - Fix race condition between screen recording and stopping container. +- Wait for sample project key to be filled in before clicking submit. ## [0.3.8] - 2024-01-12 [0.3.8]: https://github.com/atlassian/docker-infrastructure/compare/release-0.3.7...release-0.3.8 diff --git a/src/main/kotlin/com/atlassian/performance/tools/dockerinfrastructure/jira/SetUpFromScratchAction.kt b/src/main/kotlin/com/atlassian/performance/tools/dockerinfrastructure/jira/SetUpFromScratchAction.kt index f3ca54a..7f200e7 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/dockerinfrastructure/jira/SetUpFromScratchAction.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/dockerinfrastructure/jira/SetUpFromScratchAction.kt @@ -118,9 +118,11 @@ faV9ulfLz1K+3g9j1YCFDeq7aYROMQbwMIvHimNt7/bJCCIX02nj""" waitAndClick(By.id("sampleData")) waitAndClick(By.className("create-project-dialog-create-button")) - val projectNameInputLocator = By.id("name") - driver.wait(Duration.ofMinutes(1), ExpectedConditions.visibilityOfElementLocated(projectNameInputLocator)) + driver.wait(Duration.ofSeconds(10), ExpectedConditions.visibilityOfElementLocated(By.id("add-project-form"))) + val projectNameInputLocator = By.cssSelector("[name='name']") + driver.wait(Duration.ofMinutes(1), ExpectedConditions.elementToBeClickable(projectNameInputLocator)) driver.findElement(projectNameInputLocator).sendKeys("Sample") + driver.wait(Duration.ofMinutes(1), ExpectedConditions.elementToBeClickable(By.cssSelector("[name='key']"))) val addProjectButtonLocator = By.className("add-project-dialog-create-button") driver.wait(Duration.ofMinutes(10), ExpectedConditions.elementToBeClickable(addProjectButtonLocator)) From 9b2b3d96e0e78729cba1e55de46d6bea04dd484e Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 7 Jun 2024 13:38:04 +0200 Subject: [PATCH 3/3] Refactor `SetUpFromScratchAction` --- .../jira/SetUpFromScratchAction.kt | 77 ++++++++----------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/src/main/kotlin/com/atlassian/performance/tools/dockerinfrastructure/jira/SetUpFromScratchAction.kt b/src/main/kotlin/com/atlassian/performance/tools/dockerinfrastructure/jira/SetUpFromScratchAction.kt index 7f200e7..4498b25 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/dockerinfrastructure/jira/SetUpFromScratchAction.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/dockerinfrastructure/jira/SetUpFromScratchAction.kt @@ -5,10 +5,12 @@ import org.openqa.selenium.ElementNotInteractableException import org.openqa.selenium.StaleElementReferenceException import org.openqa.selenium.WebDriver import org.openqa.selenium.support.ui.ExpectedCondition -import org.openqa.selenium.support.ui.ExpectedConditions +import org.openqa.selenium.support.ui.ExpectedConditions.* import org.openqa.selenium.support.ui.WebDriverWait import java.net.URI import java.time.Duration +import java.time.Duration.ofMinutes +import java.time.Duration.ofSeconds /** @@ -35,16 +37,15 @@ internal class SetUpFromScratchAction( private fun waitForJira() { driver.wait( condition = ExpectedCondition { - it!! - return@ExpectedCondition try { - it.navigate().to(uri.toURL()) - it.findElements(By.id("jira")).firstOrNull() + try { + it?.navigate()?.to(uri.toURL()) + it?.findElements(By.id("jira"))?.firstOrNull() } catch (e: Exception) { null } }, - timeout = Duration.ofMinutes(15), - precision = Duration.ofSeconds(4) + timeout = ofMinutes(15), + precision = ofSeconds(4) ) } @@ -66,7 +67,7 @@ internal class SetUpFromScratchAction( * from https://developer.atlassian.com/platform/marketplace/timebomb-licenses-for-testing-server-apps/ */ private fun setupLicense() { - val timebombLicense = """ + val timeBombLicense = """ AAAB8w0ODAoPeNp9Uk2P2jAQvedXWOoNydmELVKLFKlL4u7SLglKQj+27cEkA3gb7GjssMu/rwnQl s9DDvHMvPfmvXmTN0BGfE08n3jdftfv927J/SgnXc9/58wRQC5UXQO6j6IAqYGVwgglAxbnLB2nw 4w5cbOcAiaziQbUge85oZKGFybmSwjKmiMKvfjATcW1Fly6hVo64waLBdcQcQPBhot6Per5zo4lX @@ -78,27 +79,20 @@ dayEU7kb6lepJOxOLAf7XneFmkfCuCp95nh+LdwhfegL8E5l0LzNo4IVlApi0Vy0GZvs9O6b+vHZ xzBv0toB3Yuk5lCwuualHs8fSD0/3NqdZ48nBd+5bjYilfNdokZr6zmP7TmY5YwLAIUNq8MbmR8G faV9ulfLz1K+3g9j1YCFDeq7aYROMQbwMIvHimNt7/bJCCIX02nj""" .trimIndent() - val licenseKeyLocator = By.id("licenseKey") - val licenceKeyInput = - driver.wait(Duration.ofMinutes(2), ExpectedConditions.elementToBeClickable(licenseKeyLocator)) - licenceKeyInput.click() - licenceKeyInput.sendKeys(timebombLicense) + driver.wait(ofMinutes(2), elementToBeClickable(By.id("licenseKey"))).let { + it.click() + it.sendKeys(timeBombLicense) + } clickAndAwaitTransition(By.className("aui-button-primary")) } private fun setupAdministratorAccount() { - val fullnameLocator = By.cssSelector("input[name='fullname']") - val emailLocator = By.cssSelector("input[name='email']") - val usernameLocator = By.cssSelector("input[name='username']") - val passwordLocator = By.cssSelector("input[name='password']") - val confirmLocator = By.cssSelector("input[name='confirm']") - - driver.wait(Duration.ofMinutes(3), ExpectedConditions.visibilityOfElementLocated(fullnameLocator)) - driver.findElement(fullnameLocator).sendKeys("Admin Fixer") - driver.findElement(emailLocator).sendKeys("admin@fixer.com") - driver.findElement(usernameLocator).sendKeys("admin") - driver.findElement(passwordLocator).sendKeys("admin") - driver.findElement(confirmLocator).sendKeys("admin") + driver.wait(ofMinutes(3), visibilityOfElementLocated(By.cssSelector("input[name='fullname']"))) + driver.findElement(By.cssSelector("input[name='fullname']")).sendKeys("Admin Fixer") + driver.findElement(By.cssSelector("input[name='email']")).sendKeys("admin@fixer.com") + driver.findElement(By.cssSelector("input[name='username']")).sendKeys("admin") + driver.findElement(By.cssSelector("input[name='password']")).sendKeys("admin") + driver.findElement(By.cssSelector("input[name='confirm']")).sendKeys("admin") driver.findElement(By.id("jira-setupwizard-submit")).click() } @@ -118,28 +112,24 @@ faV9ulfLz1K+3g9j1YCFDeq7aYROMQbwMIvHimNt7/bJCCIX02nj""" waitAndClick(By.id("sampleData")) waitAndClick(By.className("create-project-dialog-create-button")) - driver.wait(Duration.ofSeconds(10), ExpectedConditions.visibilityOfElementLocated(By.id("add-project-form"))) - val projectNameInputLocator = By.cssSelector("[name='name']") - driver.wait(Duration.ofMinutes(1), ExpectedConditions.elementToBeClickable(projectNameInputLocator)) - driver.findElement(projectNameInputLocator).sendKeys("Sample") - driver.wait(Duration.ofMinutes(1), ExpectedConditions.elementToBeClickable(By.cssSelector("[name='key']"))) + driver.wait(ofSeconds(10), visibilityOfElementLocated(By.cssSelector("form#add-project-form"))) + driver.wait(ofMinutes(1), elementToBeClickable(By.cssSelector("input[name='name']"))).sendKeys("Sample") + driver.wait(ofMinutes(1), elementToBeClickable(By.cssSelector("input[name='key']"))) - val addProjectButtonLocator = By.className("add-project-dialog-create-button") - driver.wait(Duration.ofMinutes(10), ExpectedConditions.elementToBeClickable(addProjectButtonLocator)) - driver.findElement(addProjectButtonLocator).click() + waitAndClick(By.className("add-project-dialog-create-button"), ofMinutes(10)) cleanErrorMessages() } private fun cleanErrorMessages() { - driver.wait(Duration.ofMinutes(10), ExpectedConditions.visibilityOfElementLocated(By.className("subnavigator-title"))) + driver.wait(ofMinutes(10), visibilityOfElementLocated(By.className("subnavigator-title"))) val slideOutTime = Duration.ofMillis(1000) - for (i in 1..10) { + repeat(10) { val closeButtons = driver .findElements(By.className("icon-close")) .filter { it.isDisplayed && it.isEnabled } if (closeButtons.isEmpty()) { - break + return@repeat } else { try { closeButtons.first().click() @@ -150,17 +140,16 @@ faV9ulfLz1K+3g9j1YCFDeq7aYROMQbwMIvHimNt7/bJCCIX02nj""" } } - private fun waitAndClick(by: By, timeout: Duration = Duration.ofMinutes(5)) { - driver.wait(timeout, ExpectedConditions.elementToBeClickable(by)) - .click() + private fun waitAndClick(by: By, timeout: Duration = ofMinutes(5)) { + driver.wait(timeout, elementToBeClickable(by)).click() } - private fun clickAndAwaitTransition(by: By, timeout: Duration = Duration.ofMinutes(5)) { - val clickable = driver.wait(timeout, ExpectedConditions.elementToBeClickable(by)) + private fun clickAndAwaitTransition(by: By, timeout: Duration = ofMinutes(5)) { + val clickable = driver.wait(timeout, elementToBeClickable(by)) clickable.click() - val initialTimeout = Duration.ofSeconds(10) + val initialTimeout = ofSeconds(10) try { - driver.wait(initialTimeout, ExpectedConditions.stalenessOf(clickable)) + driver.wait(initialTimeout, stalenessOf(clickable)) } catch (e: Exception) { try { //this both resends the click and executes a staleness check better than stalenessOf @@ -169,7 +158,7 @@ faV9ulfLz1K+3g9j1YCFDeq7aYROMQbwMIvHimNt7/bJCCIX02nj""" // click can happen on a stale element return } - driver.wait(timeout, ExpectedConditions.stalenessOf(clickable)) + driver.wait(timeout, stalenessOf(clickable)) } } }