Speedup and simplify tests

This drastically speeds up tests and simplifies test writing by making
it so that no explicit calls for waiting are needed. Elements that are
queried for now implicitly waits for them to be available.

On my machine, tests used to take 3-4 minutes to complete. Now they take
~1 minute to complete.
This commit is contained in:
Joey Yakimowich-Payne 2020-02-16 19:19:21 -07:00 committed by Andreas Rumpf
commit 5a4f44b4ee
7 changed files with 40 additions and 83 deletions

View file

@ -55,7 +55,7 @@ task blankdb, "Creates a blank DB":
task test, "Runs tester":
exec "nimble c -y src/forum.nim"
exec "nimble c -y -r tests/browsertester"
exec "nimble c -y -r -d:actionDelayMs=0 tests/browsertester"
task fasttest, "Runs tester without recompiling backend":
exec "nimble c -r tests/browsertester"
exec "nimble c -r -d:actionDelayMs=0 tests/browsertester"

View file

@ -149,4 +149,4 @@ proc render(): VNode =
])
window.onPopState = onPopState
setRenderer render
setRenderer render

View file

@ -18,25 +18,21 @@ proc categoriesUserTests(session: Session, baseUrl: string) =
with session:
navigate baseUrl
wait()
login "user", "user"
setup:
with session:
navigate baseUrl
wait()
test "no category add available":
with session:
click "#new-thread-btn"
wait()
checkIsNone "#add-category"
test "can create category thread":
with session:
click "#new-thread-btn"
wait()
sendKeys "#thread-title", title
@ -45,12 +41,10 @@ proc categoriesUserTests(session: Session, baseUrl: string) =
sendKeys "#reply-textarea", content
click "#create-thread-btn"
wait()
checkText "#thread-title .category", "Fun"
navigate baseUrl
wait()
ensureExists title, LinkTextSelector
@ -65,18 +59,15 @@ proc categoriesAdminTests(session: Session, baseUrl: string) =
suite "admin tests":
with session:
navigate baseUrl
wait()
login "admin", "admin"
test "can create category":
with session:
click "#new-thread-btn"
wait()
ensureExists "#add-category"
click "#add-category .plus-btn"
wait()
clear "#add-category input[name='name']"
clear "#add-category input[name='color']"
@ -88,7 +79,6 @@ proc categoriesAdminTests(session: Session, baseUrl: string) =
sendKeys "#add-category input[name='description']", description
click "#add-category #add-category-btn"
wait()
checkText "#category-selection .selected-category", name
@ -96,10 +86,8 @@ proc categoriesAdminTests(session: Session, baseUrl: string) =
proc test*(session: Session, baseUrl: string) =
session.navigate(baseUrl)
session.wait()
categoriesUserTests(session, baseUrl)
categoriesAdminTests(session, baseUrl)
session.navigate(baseUrl)
session.wait()
session.navigate(baseUrl)

View file

@ -2,6 +2,9 @@ import os, options, unittest, strutils
import webdriver
import macros
const actionDelayMs {.intdefine.} = 0
## Inserts a delay in milliseconds between automated actions. Useful for debugging tests
macro with*(obj: typed, code: untyped): untyped =
## Execute a set of statements with an object
expectKind code, nnkStmtList
@ -12,24 +15,28 @@ macro with*(obj: typed, code: untyped): untyped =
if result[i].kind in {nnkCommand, nnkCall}:
result[i].insert(1, obj)
proc elementIsSome(element: Option[Element]): bool =
return element.isSome
proc elementIsNone(element: Option[Element]): bool =
return element.isNone
proc waitForElement*(session: Session, selector: string, strategy=CssSelector, timeout=20000, pollTime=50, waitCondition=elementIsSome): Option[Element]
template click*(session: Session, element: string, strategy=CssSelector) =
let el = session.findElement(element, strategy)
check el.isSome()
let el = session.waitForElement(element, strategy)
el.get().click()
template sendKeys*(session: Session, element, keys: string) =
let el = session.findElement(element)
check el.isSome()
let el = session.waitForElement(element)
el.get().sendKeys(keys)
template clear*(session: Session, element: string) =
let el = session.findElement(element)
check el.isSome()
let el = session.waitForElement(element)
el.get().clear()
template sendKeys*(session: Session, element: string, keys: varargs[Key]) =
let el = session.findElement(element)
check el.isSome()
let el = session.waitForElement(element)
# focus
el.get().click()
@ -37,47 +44,47 @@ template sendKeys*(session: Session, element: string, keys: varargs[Key]) =
session.press(key)
template ensureExists*(session: Session, element: string, strategy=CssSelector) =
let el = session.findElement(element, strategy)
check el.isSome()
discard session.waitForElement(element, strategy)
template check*(session: Session, element: string, function: untyped) =
let el = session.findElement(element)
let el = session.waitForElement(element)
check function(el)
template check*(session: Session, element: string,
strategy: LocationStrategy, function: untyped) =
let el = session.findElement(element, strategy)
let el = session.waitForElement(element, strategy)
check function(el)
template checkIsNone*(session: Session, element: string, strategy=CssSelector) =
let el = session.findElement(element, strategy)
check el.isNone()
discard session.waitForElement(element, strategy, waitCondition=elementIsNone)
template checkText*(session: Session, element, expectedValue: string) =
let el = session.findElement(element)
check el.isSome()
let el = session.waitForElement(element)
check el.get().getText() == expectedValue
proc waitForLoad*(session: Session, timeout=20000) =
proc waitForElement*(
session: Session, selector: string, strategy=CssSelector,
timeout=20000, pollTime=50,
waitCondition=elementIsSome
): Option[Element] =
var waitTime = 0
sleep(2000)
when actionDelayMs > 0:
sleep(actionDelayMs)
while true:
let loading = session.findElement(".loading")
if loading.isNone: return
sleep(1000)
waitTime += 1000
let loading = session.findElement(selector, strategy)
if waitCondition(loading):
return loading
sleep(pollTime)
waitTime += pollTime
if waitTime > timeout:
doAssert false, "Wait for load time exceeded"
proc wait*(session: Session, msTimeout: int = 5000) =
session.waitForLoad(msTimeout)
proc setUserRank*(session: Session, baseUrl, user, rank: string) =
with session:
navigate(baseUrl & "profile/" & user)
wait()
click "#settings-tab"
@ -85,13 +92,11 @@ proc setUserRank*(session: Session, baseUrl, user, rank: string) =
click("#rank-field option#rank-" & rank.toLowerAscii)
click "#save-btn"
wait()
proc logout*(session: Session) =
with session:
click "#profile-btn"
click "#profile-btn #logout-btn"
wait()
# Verify we have logged out by looking for the log in button.
ensureExists "#login-btn"
@ -108,8 +113,6 @@ proc login*(session: Session, user, password: string) =
sendKeys "#login-form input[name='password']", Key.Enter
wait()
# Verify that the user menu has been initialised properly.
click "#profile-btn"
checkText "#profile-btn #profile-name", user
@ -128,7 +131,6 @@ proc register*(session: Session, user, password: string, verify = true) =
sendKeys "#signup-form input[name='password']", password
click "#signup-modal .create-account-btn"
wait()
if verify:
with session:
@ -141,13 +143,11 @@ proc register*(session: Session, user, password: string, verify = true) =
proc createThread*(session: Session, title, content: string) =
with session:
click "#new-thread-btn"
wait()
sendKeys "#thread-title", title
sendKeys "#reply-textarea", content
click "#create-thread-btn"
wait()
checkText "#thread-title .title-text", title
checkText ".original-post div.post-content", content

View file

@ -5,8 +5,6 @@ import webdriver
proc test*(session: Session, baseUrl: string) =
session.navigate(baseUrl)
waitForLoad(session)
test "can see banned posts":
with session:
register("issue181", "issue181")
@ -19,7 +17,6 @@ proc test*(session: Session, baseUrl: string) =
login("issue181", "issue181")
navigate(baseUrl)
wait()
const title = "Testing issue 181."
createThread(title, "Test for issue #181")
@ -33,7 +30,6 @@ proc test*(session: Session, baseUrl: string) =
# Make sure the banned user's thread is still visible.
navigate(baseUrl)
wait()
ensureExists("tr.banned")
checkText("tr.banned .thread-title > a", title)
logout()

View file

@ -5,8 +5,6 @@ import webdriver
proc test*(session: Session, baseUrl: string) =
session.navigate(baseUrl)
waitForLoad(session)
# Sanity checks
test "shows sign up":
session.checkText("#signup-btn", "Sign up")
@ -38,10 +36,8 @@ proc test*(session: Session, baseUrl: string) =
logout()
navigate baseUrl
wait()
register "TEst1", "test1", verify = false
ensureExists "#signup-form .has-error"
navigate baseUrl
wait()
navigate baseUrl

View file

@ -1,4 +1,4 @@
import unittest, options, os, common
import unittest, options, common
import webdriver
@ -27,18 +27,15 @@ proc userTests(session: Session, baseUrl: string) =
setup:
session.navigate(baseUrl)
session.wait()
test "can create thread":
with session:
click "#new-thread-btn"
wait()
sendKeys "#thread-title", userTitleStr
sendKeys "#reply-textarea", userContentStr
click "#create-thread-btn"
wait()
checkText "#thread-title .title-text", userTitleStr
checkText ".original-post div.post-content", userContentStr
@ -50,7 +47,6 @@ proc anonymousTests(session: Session, baseUrl: string) =
suite "anonymous user tests":
with session:
navigate baseUrl
wait()
test "can view banned thread":
with session:
@ -58,25 +54,21 @@ proc anonymousTests(session: Session, baseUrl: string) =
with session:
navigate baseUrl
wait()
proc bannedTests(session: Session, baseUrl: string) =
suite "banned user thread tests":
with session:
navigate baseUrl
wait()
login "banned", "banned"
test "can't start thread":
with session:
click "#new-thread-btn"
wait()
sendKeys "#thread-title", "test"
sendKeys "#reply-textarea", "test"
click "#create-thread-btn"
wait()
ensureExists "#new-thread p.text-error"
@ -88,7 +80,6 @@ proc adminTests(session: Session, baseUrl: string) =
setup:
session.navigate(baseUrl)
session.wait()
test "can view banned thread":
with session:
@ -97,13 +88,11 @@ proc adminTests(session: Session, baseUrl: string) =
test "can create thread":
with session:
click "#new-thread-btn"
wait()
sendKeys "#thread-title", adminTitleStr
sendKeys "#reply-textarea", adminContentStr
click "#create-thread-btn"
wait()
checkText "#thread-title .title-text", adminTitleStr
checkText ".original-post div.post-content", adminContentStr
@ -111,7 +100,6 @@ proc adminTests(session: Session, baseUrl: string) =
test "try create duplicate thread":
with session:
click "#new-thread-btn"
wait()
ensureExists "#new-thread"
sendKeys "#thread-title", adminTitleStr
@ -119,22 +107,17 @@ proc adminTests(session: Session, baseUrl: string) =
click "#create-thread-btn"
wait()
ensureExists "#new-thread p.text-error"
test "can edit post":
let modificationText = " and I edited it!"
with session:
click adminTitleStr, LinkTextSelector
wait()
click ".post-buttons .edit-button"
wait()
sendKeys ".original-post #reply-textarea", modificationText
click ".edit-buttons .save-button"
wait()
checkText ".original-post div.post-content", adminContentStr & modificationText
@ -143,7 +126,6 @@ proc adminTests(session: Session, baseUrl: string) =
with session:
click userTitleStr, LinkTextSelector
wait()
click ".post-buttons .like-button"
@ -152,14 +134,11 @@ proc adminTests(session: Session, baseUrl: string) =
test "can delete thread":
with session:
click adminTitleStr, LinkTextSelector
wait()
click ".post-buttons .delete-button"
wait()
# click delete confirmation
click "#delete-modal .delete-btn"
wait()
# Make sure the forum post is gone
checkIsNone adminTitleStr, LinkTextSelector
@ -168,7 +147,6 @@ proc adminTests(session: Session, baseUrl: string) =
proc test*(session: Session, baseUrl: string) =
session.navigate(baseUrl)
session.wait()
userTests(session, baseUrl)
@ -180,5 +158,4 @@ proc test*(session: Session, baseUrl: string) =
unBanUser(session, baseUrl)
session.navigate(baseUrl)
session.wait()
session.navigate(baseUrl)