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.
153 lines
4.6 KiB
Nim
153 lines
4.6 KiB
Nim
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
|
|
result = code
|
|
|
|
# Simply inject obj into call
|
|
for i in 0 ..< result.len:
|
|
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.waitForElement(element, strategy)
|
|
el.get().click()
|
|
|
|
template sendKeys*(session: Session, element, keys: string) =
|
|
let el = session.waitForElement(element)
|
|
el.get().sendKeys(keys)
|
|
|
|
template clear*(session: Session, element: string) =
|
|
let el = session.waitForElement(element)
|
|
el.get().clear()
|
|
|
|
template sendKeys*(session: Session, element: string, keys: varargs[Key]) =
|
|
let el = session.waitForElement(element)
|
|
|
|
# focus
|
|
el.get().click()
|
|
for key in keys:
|
|
session.press(key)
|
|
|
|
template ensureExists*(session: Session, element: string, strategy=CssSelector) =
|
|
discard session.waitForElement(element, strategy)
|
|
|
|
template check*(session: Session, element: string, function: untyped) =
|
|
let el = session.waitForElement(element)
|
|
check function(el)
|
|
|
|
template check*(session: Session, element: string,
|
|
strategy: LocationStrategy, function: untyped) =
|
|
let el = session.waitForElement(element, strategy)
|
|
check function(el)
|
|
|
|
template checkIsNone*(session: Session, element: string, strategy=CssSelector) =
|
|
discard session.waitForElement(element, strategy, waitCondition=elementIsNone)
|
|
|
|
template checkText*(session: Session, element, expectedValue: string) =
|
|
let el = session.waitForElement(element)
|
|
check el.get().getText() == expectedValue
|
|
|
|
proc waitForElement*(
|
|
session: Session, selector: string, strategy=CssSelector,
|
|
timeout=20000, pollTime=50,
|
|
waitCondition=elementIsSome
|
|
): Option[Element] =
|
|
var waitTime = 0
|
|
|
|
when actionDelayMs > 0:
|
|
sleep(actionDelayMs)
|
|
|
|
while true:
|
|
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 setUserRank*(session: Session, baseUrl, user, rank: string) =
|
|
with session:
|
|
navigate(baseUrl & "profile/" & user)
|
|
|
|
click "#settings-tab"
|
|
|
|
click "#rank-field"
|
|
click("#rank-field option#rank-" & rank.toLowerAscii)
|
|
|
|
click "#save-btn"
|
|
|
|
proc logout*(session: Session) =
|
|
with session:
|
|
click "#profile-btn"
|
|
click "#profile-btn #logout-btn"
|
|
|
|
# Verify we have logged out by looking for the log in button.
|
|
ensureExists "#login-btn"
|
|
|
|
proc login*(session: Session, user, password: string) =
|
|
with session:
|
|
click "#login-btn"
|
|
|
|
clear "#login-form input[name='username']"
|
|
clear "#login-form input[name='password']"
|
|
|
|
sendKeys "#login-form input[name='username']", user
|
|
sendKeys "#login-form input[name='password']", password
|
|
|
|
sendKeys "#login-form input[name='password']", Key.Enter
|
|
|
|
# Verify that the user menu has been initialised properly.
|
|
click "#profile-btn"
|
|
checkText "#profile-btn #profile-name", user
|
|
click "#profile-btn"
|
|
|
|
proc register*(session: Session, user, password: string, verify = true) =
|
|
with session:
|
|
click "#signup-btn"
|
|
|
|
clear "#signup-form input[name='email']"
|
|
clear "#signup-form input[name='username']"
|
|
clear "#signup-form input[name='password']"
|
|
|
|
sendKeys "#signup-form input[name='email']", user & "@" & user & ".com"
|
|
sendKeys "#signup-form input[name='username']", user
|
|
sendKeys "#signup-form input[name='password']", password
|
|
|
|
click "#signup-modal .create-account-btn"
|
|
|
|
if verify:
|
|
with session:
|
|
# Verify that the user menu has been initialised properly.
|
|
click "#profile-btn"
|
|
checkText "#profile-btn #profile-name", user
|
|
# close menu
|
|
click "#profile-btn"
|
|
|
|
proc createThread*(session: Session, title, content: string) =
|
|
with session:
|
|
click "#new-thread-btn"
|
|
|
|
sendKeys "#thread-title", title
|
|
sendKeys "#reply-textarea", content
|
|
|
|
click "#create-thread-btn"
|
|
|
|
checkText "#thread-title .title-text", title
|
|
checkText ".original-post div.post-content", content
|