Skip to content

Instantly share code, notes, and snippets.

@loxal
Created May 5, 2021 14:29
Show Gist options
  • Save loxal/1d9486a8d3a0c66ad1e766e7f39890c7 to your computer and use it in GitHub Desktop.
Save loxal/1d9486a8d3a0c66ad1e766e7f39890c7 to your computer and use it in GitHub Desktop.
Testing web UI via HTML DOM
#!/usr/bin/env kotlin
/*
* Copyright 2021 Alexander Orlov <alexander.orlov@loxal.net>. All rights reserved.
*
* 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
*
* https://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.
*/
@file:DependsOn("org.jetbrains.kotlin:kotlin-test-junit5:1.4.32")
@file:DependsOn("io.ktor:ktor-http-jvm:1.5.4")
@file:DependsOn("org.slf4j:slf4j-nop:1.7.30")
@file:DependsOn("com.squareup.okhttp3:okhttp:5.0.0-alpha.2")
@file:DependsOn("org.seleniumhq.selenium:selenium-chrome-driver:4.0.0-beta-3")
@file:DependsOn("org.seleniumhq.selenium:selenium-java:4.0.0-beta-3")
import io.ktor.http.*
import okhttp3.OkHttpClient
import okhttp3.Request
import org.openqa.selenium.*
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import java.time.Duration
import java.util.concurrent.TimeUnit
import kotlin.system.exitProcess
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
println("TENANT: ${System.getenv("TENANT")}")
var targetUrl: String
var crawlUrl: String
println("args.size: ${args.size}")
if (args.isNotEmpty()) {
println("args: ${args.joinToString()}")
targetUrl = args[0]
crawlUrl = args[1]
println("targetUrl: $targetUrl")
println("crawlUrl: $crawlUrl")
} else {
println("targetUrl required")
exitProcess(1)
}
private val browserOptions = ChromeOptions()
.setHeadless(true).addArguments("--no-sandbox")
val browser: WebDriver = ChromeDriver(browserOptions)
val site = browser.get("$targetUrl/app/gadget/main.html")
val gadgetDefaultSiteId = "a2e8d60b-0696-47ea-bc48-982598ee35bd"
val integrationCode: WebElement = browser.findElement(By.cssSelector("#integrationCode"))
val integrationCodeLength = 627
assertNotNull(site)
private val jsExec = browser as JavascriptExecutor
fun curlTest() {
val httpClient = OkHttpClient.Builder()
.followRedirects(false)
.followSslRedirects(false)
.build()
val request = Request.Builder()
.url(targetUrl)
.build()
httpClient.newCall(request).execute().use { response ->
assertEquals(HttpStatusCode.OK.value, response.code)
val content = response.body?.string()
assertTrue(content!!.contains("<title>About</title>"))
assertTrue(content.contains("<script defer src=\"/app/client/webpack-bundle.min.js\"></script>"))
assertTrue(1000 < content.length)
}
val adminUiRequest = Request.Builder()
.url("$targetUrl/imprint.html")
.build()
httpClient.newCall(adminUiRequest).execute().use { response ->
assertEquals(HttpStatusCode.OK.value, response.code)
val content = response.body?.string()
assertTrue(3_000 < content!!.length)
}
val searchUiRequest = Request.Builder()
.url("$targetUrl/app/gadget/main.html")
.build()
httpClient.newCall(searchUiRequest).execute().use { response ->
assertEquals(HttpStatusCode.OK.value, response.code)
val content = response.body?.string()
assertTrue(11_000 < content!!.length)
}
println("=== curlTest ===")
}
fun sisEvaluationJourney() {
val notification = browser.findElement(By.cssSelector("#notificationCenter"))
val url = browser.findElement(By.cssSelector("#url"))
val resetEvaluationView = browser.findElement(By.cssSelector("#resetEvaluationView"))
val triggerButton = browser.findElement(By.cssSelector("#triggerButton"))
val termsAccepted = browser.findElement(By.cssSelector("#termsAccepted"))
val email = browser.findElement(By.cssSelector("#email"))
val integrationUrl = browser.findElement(By.cssSelector("#integrationUrl"))
val setupUrl = browser.findElement(By.cssSelector("#setupUrl"))
fun beforeCrawling() {
url.clear()
url.sendKeys(crawlUrl)
email.sendKeys("ops@loxal.net")
assertEquals("ops@loxal.net", email.getAttribute("value"))
assertTrue(setupUrl.getAttribute("value").isEmpty())
assertFalse(resetEvaluationView.isDisplayed)
assertTrue(triggerButton.isEnabled)
assertFalse(triggerButton.isSelected)
assertTrue(triggerButton.isDisplayed)
assertEquals(
"Read and accept the Terms & Conditions above to continue",
triggerButton.getAttribute("textContent").trim()
)
assertTrue(15 < integrationUrl.getAttribute("href").length)
assertFalse(integrationUrl.getAttribute("href").contains("siteId="))
assertEquals(integrationCodeLength, integrationCode.getAttribute("value").length)
assertTrue(integrationCode.getAttribute("value").contains("$gadgetDefaultSiteId\" defer="))
assertFalse(email.isSelected)
assertTrue(email.isEnabled)
assertTrue(email.isDisplayed)
termsAccepted.click()
}
beforeCrawling()
fun afterCrawling() {
assertEquals(
"Crawl your website and search it as it is being crawled!",
triggerButton.getAttribute("textContent").trim()
)
triggerButton.click()
assertFalse(email.isSelected)
assertTrue(email.isEnabled)
assertTrue(email.isDisplayed)
WebDriverWait(browser, Duration.ofSeconds(15)).until(
ExpectedConditions.attributeContains(
email, "readOnly", "true"
)
)
assertTrue(url.getAttribute("readOnly").toBoolean())
assertEquals("ops@loxal.net", email.getAttribute("value"))
val notificationProgress = WebDriverWait(browser, Duration.ofSeconds(15)).until(
ExpectedConditions.attributeContains(
notification,
"textContent",
"Crawler is running... please give us just a minute or two."
)
)
WebDriverWait(browser, Duration.ofSeconds(15)).until(
ExpectedConditions.elementToBeClickable(resetEvaluationView)
)
assertTrue(resetEvaluationView.isDisplayed)
assertEquals(
"Reset evaluation view and crawl another site",
resetEvaluationView.getAttribute("textContent").trim()
)
val triggerButtonStatus = WebDriverWait(browser, Duration.ofSeconds(15))
.until(
ExpectedConditions.attributeContains(
triggerButton,
"textContent",
"Search your website below or crawl another site above"
)
)
assertFalse(triggerButton.isEnabled) // true/false flipping
assertFalse(triggerButton.isSelected)
assertFalse(triggerButton.isDisplayed)
val notificationSuccess = WebDriverWait(browser, Duration.ofSeconds(15))
.until(
ExpectedConditions.attributeContains(
notification,
"textContent",
"1 pages from https://example.com have been crawled."
)
)
assertTrue(notification.isDisplayed)
notification.click()
assertFalse(notification.isDisplayed)
assertTrue(setupUrl.getAttribute("value").startsWith("https://loxal.net/app/gadget/main.html?siteId="))
assertFalse(setupUrl.getAttribute("value").contains(gadgetDefaultSiteId))
assertEquals(154, setupUrl.getAttribute("value").length)
assertTrue(notificationProgress.and(notificationSuccess).and(triggerButtonStatus))
assertEquals(crawlUrl, url.getAttribute("value"))
assertTrue(150 < integrationUrl.getAttribute("href").length)
assertTrue(integrationUrl.getAttribute("href").contains("siteId="))
assertTrue(integrationUrl.getAttribute("href").contains("&url=$crawlUrl"))
assertEquals(integrationCodeLength - 9, integrationCode.getAttribute("value").length)
assertFalse(integrationCode.getAttribute("value").contains(gadgetDefaultSiteId))
}
afterCrawling()
}
fun testPageFinder(
finderShadowRootHostId: String = "finder-searchbar",
finderPlaceholder: String = "\uD83D\uDD0D Enter * to search for anything",
autocompleteFinding: String = "Example Domain This domain is for…",
searchFinding: String = "Example DomainExample Domain This domain is for use in illustrative examples in documents.https://example.com/"
) {
val finderShadowRootHost = browser.findElement(By.cssSelector("#${finderShadowRootHostId}"))
assertEquals("div", finderShadowRootHost.tagName)
assertEquals(finderShadowRootHostId, finderShadowRootHost.getAttribute("id"))
assertTrue(finderShadowRootHost.isEnabled)
assertTrue(finderShadowRootHost.isDisplayed)
assertFalse(finderShadowRootHost.isSelected)
val finderShadowRoot = jsExec.executeScript(
"return arguments[0].shadowRoot.querySelector('#sitesearch-page-finder > input[type=search]')",
finderShadowRootHost
) as WebElement
println(finderShadowRoot.getAttribute("placeholder"))
assertEquals(finderPlaceholder, finderShadowRoot.getAttribute("placeholder"))
assertTrue(finderShadowRoot.getAttribute("value").isEmpty())
finderShadowRoot.sendKeys("example")
assertEquals("example", finderShadowRoot.getAttribute("value"))
TimeUnit.SECONDS.sleep(1)
val finderShadowAutocomplete = jsExec.executeScript(
"return arguments[0].shadowRoot.querySelector('#sitesearch-page-finder > dl')",
finderShadowRootHost
) as WebElement
WebDriverWait(browser, Duration.ofSeconds(15)).until(
ExpectedConditions.attributeContains(
finderShadowAutocomplete, "textContent", autocompleteFinding
)
)
finderShadowRoot.sendKeys(Keys.ENTER)
TimeUnit.SECONDS.sleep(1)
val finderShadowSearch = jsExec.executeScript(
"return arguments[0].shadowRoot.querySelector('#sitesearch-page-finder > dl > div')",
finderShadowRootHost
) as WebElement
assertTrue(
finderShadowSearch.getAttribute("textContent").startsWith(searchFinding),
finderShadowSearch.getAttribute("textContent")
)
println("=== testPageFinder for #${finderShadowRootHostId} ===")
}
fun validateGadget() {
assertTrue(browser.currentUrl.endsWith("/app/gadget/main.html"))
val subscriptionLink = browser.findElement(By.cssSelector("#sis-gadget-choose-subscription"))
subscriptionLink.click()
assertTrue(browser.currentUrl.endsWith("/pricing-site-search.html"))
}
fun afterRevisiting() {
browser.navigate().refresh()
// browser.get("$targetUrl/app/gadget/main.html")
// TimeUnit.SECONDS.sleep(15)
val integrationUrl = browser.findElement(By.cssSelector("#integrationUrl"))
val integrationCode = browser.findElement(By.cssSelector("#integrationCode"))
val setupUrl = browser.findElement(By.cssSelector("#setupUrl"))
assertFalse(integrationCode.getAttribute("value").contains(gadgetDefaultSiteId))
assertEquals(integrationCodeLength - 9, integrationCode.getAttribute("value").length)
assertTrue(integrationUrl.getAttribute("href").startsWith("https://loxal.net/app/gadget/main.html?siteId="))
assertFalse(integrationUrl.getAttribute("href").contains(gadgetDefaultSiteId))
assertTrue(setupUrl.getAttribute("value").startsWith("https://loxal.net/app/gadget/main.html?siteId="))
assertFalse(setupUrl.getAttribute("value").contains(gadgetDefaultSiteId))
val resetEvaluationView = browser.findElement(By.cssSelector("#resetEvaluationView"))
val termsAccepted = browser.findElement(By.cssSelector("#termsAccepted"))
assertEquals("Reset evaluation view and crawl another site", resetEvaluationView.text)
assertTrue(termsAccepted.isDisplayed)
assertFalse(termsAccepted.isEnabled)
assertTrue(termsAccepted.isSelected)
assertTrue(termsAccepted.getAttribute("checked").toBoolean())
}
//val testPageFinderSearchFinding = "News that move the IT, tomorrow."
val testPageFinderSearchFinding = "Effective pinpoint solutions that just work}"
try {
curlTest()
sisEvaluationJourney()
testPageFinder(
finderShadowRootHostId = "searchbar",
finderPlaceholder = "\uD83D\uDD0DFind...",
autocompleteFinding = "acrobat-example.pdf",
searchFinding = testPageFinderSearchFinding
) // Site, before gadget triggered
testPageFinder() // Gadget
afterRevisiting()
testPageFinder(
finderShadowRootHostId = "searchbar",
finderPlaceholder = "\uD83D\uDD0DFind...",
autocompleteFinding = "acrobat-example.pdf",
searchFinding = testPageFinderSearchFinding
) // Site, after gadget triggered
validateGadget()
} finally {
browser.quit()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment