Merge pull request #184 from jyapayne/rework_tests

Make tests nicer to write
This commit is contained in:
Dominik Picheta 2018-07-17 16:31:58 +01:00 • committed by GitHub
commit 93913d9b13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 245 additions and 148 deletions

View file

@ -94,7 +94,7 @@ when defined(js):
proc render*(state: DeleteModal): VNode =
result = buildHtml():
tdiv(class=class({"active": state.shown}, "modal modal-sm"),
id="login-modal"):
id="delete-modal"):
a(href="", class="modal-overlay", "aria-label"="close",
onClick=(ev: Event, n: VNode) => onClose(ev, n, state))
tdiv(class="modal-container"):
@ -122,11 +122,11 @@ when defined(js):
button(class=class(
{"loading": state.loading},
"btn btn-primary"
"btn btn-primary delete-btn"
),
onClick=(ev: Event, n: VNode) => onDelete(ev, n, state)):
italic(class="fas fa-trash-alt")
text " Delete"
button(class="btn",
button(class="btn cancel-btn",
onClick=(ev: Event, n: VNode) => (state.shown = false)):
text "Cancel"
text "Cancel"

View file

@ -87,7 +87,7 @@ when defined(js):
text state.error.get().message
tdiv(class="edit-buttons"):
tdiv(class="reply-button"):
tdiv(class="cancel-button"):
button(class="btn btn-link",
onClick=(e: Event, n: VNode) => (state.onEditCancel())):
text " Cancel"
@ -95,4 +95,4 @@ when defined(js):
button(class=class({"loading": state.loading}, "btn btn-primary"),
onClick=(e: Event, n: VNode) => state.save()):
italic(class="fas fa-check")
text " Save"
text " Save"

View file

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

View file

@ -78,18 +78,19 @@ when defined(js):
"data-sitekey"=recaptchaSiteKey.get())
script(src="https://www.google.com/recaptcha/api.js")
tdiv(class="modal-footer"):
button(class=class({"loading": state.loading}, "btn btn-primary"),
onClick=(ev: Event, n: VNode) => onSignUpClick(ev, n, state)):
button(class=class({"loading": state.loading},
"btn btn-primary create-account-btn"),
onClick=(ev: Event, n: VNode) => onSignUpClick(ev, n, state)):
text "Create account"
button(class="btn",
button(class="btn login-btn",
onClick=(ev: Event, n: VNode) =>
(state.onLogIn(); state.shown = false)):
text "Log in"
p(class="license-text text-gray"):
text "By registering, you agree to the "
a(href=makeUri("/about/license"),
a(id="license", href=makeUri("/about/license"),
onClick=(ev: Event, n: VNode) =>
(state.shown = false; anchorCB(ev, n))):
text "content license"
text "."
text "."

View file

@ -24,7 +24,7 @@ when defined(js):
proc render*(state: UserMenu, user: User): VNode =
result = buildHtml():
tdiv():
tdiv(id="profile-btn"):
figure(class="avatar c-hand",
onClick=(e: Event, n: VNode) => onClick(e, n, state)):
img(src=user.avatarUrl, title=user.name)
@ -52,13 +52,15 @@ when defined(js):
tdiv(class="tile-icon"):
img(class="avatar", src=user.avatarUrl,
title=user.name)
tdiv(class="tile-content"):
tdiv(id="profile-name", class="tile-content"):
text user.name
li(class="divider")
li(class="menu-item"):
a(href=makeUri("/profile/" & user.name)):
a(id="myprofile-btn",
href=makeUri("/profile/" & user.name)):
text "My profile"
li(class="menu-item c-hand"):
a(onClick = (e: Event, n: VNode) =>
a(id="logout-btn",
onClick = (e: Event, n: VNode) =>
(state.shown=false; state.onLogout())):
text "Logout"
text "Logout"

View file

@ -22,10 +22,25 @@ proc backup(path: string, contents: Option[string]=none[string]()) =
echo(path, " already exists. Moving to ", backupPath)
moveFile(path, backupPath)
proc createUser(db: DbConn, user: tuple[username, password, email: string],
rank: Rank) =
assert user.username.len != 0
let salt = makeSalt()
let password = makePassword(user.password, salt)
exec(db, sql"""
INSERT INTO person(name, password, email, salt, status, lastOnline)
VALUES (?, ?, ?, ?, ?, DATETIME('now'))
""", user.username, password, user.email, salt, $rank)
proc initialiseDb(admin: tuple[username, password, email: string],
filename="nimforum.db") =
let path = getCurrentDir() / filename
if "-dev" notin filename and "-test" notin filename:
let
path = getCurrentDir() / filename
isTest = "-test" in filename
isDev = "-dev" in filename
if not isDev and not isTest:
backup(path)
removeFile(path)
@ -98,13 +113,16 @@ proc initialiseDb(admin: tuple[username, password, email: string],
db.exec sql"create index PersonStatusIdx on person(status);"
# Create default user.
if admin.username.len != 0:
let salt = makeSalt()
let password = makePassword(admin.password, salt)
db.exec(sql"""
insert into person (id, name, password, email, salt, status)
values (1, ?, ?, ?, ?, ?);
""", admin.username, password, admin.email, salt, $Admin)
db.createUser(admin, Admin)
# Create test users if test or development
if isTest or isDev:
for rank in Spammer..Moderator:
let rankLower = toLowerAscii($rank)
let user = (username: $rankLower,
password: $rankLower,
email: $rankLower & "@localhost.local")
db.createUser(user, rank)
# -- Post

View file

@ -1,5 +1,57 @@
import os, options
import os, options, unittest
import webdriver
import macros
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)
template click*(session: Session, element: string, strategy=CssSelector) =
let el = session.findElement(element, strategy)
check el.isSome()
el.get().click()
template sendKeys*(session: Session, element, keys: string) =
let el = session.findElement(element)
check el.isSome()
el.get().sendKeys(keys)
template sendKeys*(session: Session, element: string, keys: varargs[Key]) =
let el = session.findElement(element)
check el.isSome()
# focus
el.get().click()
for key in keys:
session.press(key)
template ensureExists*(session: Session, element: string) =
let el = session.findElement(element)
check el.isSome()
template check*(session: Session, element: string, function: untyped) =
let el = session.findElement(element)
check function(el)
template check*(session: Session, element: string,
strategy: LocationStrategy, function: untyped) =
let el = session.findElement(element, strategy)
check function(el)
template checkIsNone*(session: Session, element: string, strategy=CssSelector) =
let el = session.findElement(element, strategy)
check el.isNone()
template checkText*(session: Session, element, expectedValue: string) =
let el = session.findElement(element)
check el.isSome()
check el.get().getText() == expectedValue
proc waitForLoad*(session: Session, timeout=20000) =
var waitTime = 0
@ -14,29 +66,23 @@ proc waitForLoad*(session: Session, timeout=20000) =
if waitTime > timeout:
doAssert false, "Wait for load time exceeded"
proc wait*(session: Session, msTimeout: int = 5000) =
session.waitForLoad(msTimeout)
proc logout*(session: Session) =
# Check whether we can log out.
let logoutLink = session.findElement(
"Logout",
LinkTextSelector
).get()
logoutLink.click()
with session:
wait()
click "#profile-btn"
click "#profile-btn #logout-btn"
wait()
proc login*(session: Session, user, password: string) =
let logIn = session.findElement("#login-btn").get()
logIn.click()
with session:
click "#login-btn"
let usernameField = session.findElement(
"#login-form input[name='username']"
)
sendKeys "#login-form input[name='username']", user
sendKeys "#login-form input[name='password']", password
let passwordField = session.findElement(
"#login-form input[name='password']"
)
sendKeys "#login-form input[name='password']", Key.Enter
usernameField.get().sendKeys("admin")
passwordField.get().sendKeys("admin")
passwordField.get().click() # Focus field.
session.press(Key.Enter)
waitForLoad(session, 5000)
wait()

View file

@ -9,96 +9,51 @@ proc test*(session: Session, baseUrl: string) =
# Sanity checks
test "shows sign up":
let signUp = session.findElement("#signup-btn")
check signUp.get().getText() == "Sign up"
session.checkText("#signup-btn", "Sign up")
test "shows log in":
let logIn = session.findElement("#login-btn")
check logIn.get().getText() == "Log in"
session.checkText("#login-btn", "Log in")
test "is empty":
let thread = session.findElement("tr > td.thread-title")
check thread.isNone()
session.checkIsNone("tr > td.thread-title")
# Logging in
test "can login/logout":
let logIn = session.findElement("#login-btn").get()
logIn.click()
with session:
click "#login-btn"
let usernameField = session.findElement(
"#login-form input[name='username']"
)
check usernameField.isSome()
let passwordField = session.findElement(
"#login-form input[name='password']"
)
check passwordField.isSome()
sendKeys "#login-form input[name='username']", "admin"
sendKeys "#login-form input[name='password']", "admin"
usernameField.get().sendKeys("admin")
passwordField.get().sendKeys("admin")
passwordField.get().click() # Focus field.
session.press(Key.Enter)
sendKeys "#login-form input[name='password']", Key.Enter
wait()
waitForLoad(session, 5000)
# Verify that the user menu has been initialised properly.
click "#profile-btn"
checkText "#profile-btn #profile-name", "admin"
# Verify that the user menu has been initialised properly.
let profileButton = session.findElement(
"#main-navbar figure.avatar"
).get()
profileButton.click()
let profileName = session.findElement(
"#main-navbar .menu-right div.tile-content"
).get()
check profileName.getText() == "admin"
# Check whether we can log out.
let logoutLink = session.findElement(
"Logout",
LinkTextSelector
).get()
logoutLink.click()
# Verify we have logged out by looking for the log in button.
check session.findElement("#login-btn").isSome()
# Check whether we can log out.
click "#logout-btn"
# Verify we have logged out by looking for the log in button.
ensureExists "#login-btn"
test "can register":
let signup = session.findElement("#signup-btn").get()
signup.click()
with session:
click "#signup-btn"
let emailField = session.findElement(
"#signup-form input[name='email']"
).get()
let usernameField = session.findElement(
"#signup-form input[name='username']"
).get()
let passwordField = session.findElement(
"#signup-form input[name='password']"
).get()
sendKeys "#signup-form input[name='email']", "test@test.com"
sendKeys "#signup-form input[name='username']", "test"
sendKeys "#signup-form input[name='password']", "test"
emailField.sendKeys("test@test.com")
usernameField.sendKeys("test")
passwordField.sendKeys("test")
click "#signup-modal .create-account-btn"
wait()
let createAccount = session.findElement(
"#signup-modal .modal-footer .btn-primary"
).get()
# Verify that the user menu has been initialised properly.
click "#profile-btn"
checkText "#profile-btn #profile-name", "test"
# close menu
click "#profile-btn"
createAccount.click()
waitForLoad(session, 5000)
# Verify that the user menu has been initialised properly.
let profileButton = session.findElement(
"#main-navbar figure.avatar"
).get()
profileButton.click()
let profileName = session.findElement(
"#main-navbar .menu-right div.tile-content"
).get()
check profileName.getText() == "test"
logout(session)
session.navigate(baseUrl)
session.wait()
logout(session)

View file

@ -3,44 +3,119 @@ import unittest, options, os, common
import webdriver
proc test*(session: Session, baseUrl: string) =
session.navigate(baseUrl)
let
userTitleStr = "This is a user thread!"
userContentStr = "A user has filled this out"
waitForLoad(session)
adminTitleStr = "This is a thread title!"
adminContentStr = "This is content"
login(session, "admin", "admin")
suite "user thread tests":
session.navigate(baseUrl)
session.wait()
login(session, "user", "user")
test "can create thread":
let newThreadBtn = session.findElement("#new-thread-btn").get()
newThreadBtn.click()
setup:
session.navigate(baseUrl)
session.wait()
waitForLoad(session)
test "can create thread":
with session:
click "#new-thread-btn"
wait()
let newThread = session.findElement("#new-thread")
check newThread.isSome()
sendKeys "#thread-title", userTitleStr
sendKeys "#reply-textarea", userContentStr
let createThreadBtn = session.findElement("#create-thread-btn")
check createThreadBtn.isSome()
click "#create-thread-btn"
wait()
checkText "#thread-title", userTitleStr
checkText ".original-post div.post-content", userContentStr
let threadTitle = session.findElement("#thread-title")
check threadTitle.isSome()
session.navigate(baseUrl)
session.wait()
logout(session)
let replyBox = session.findElement("#reply-textarea")
check replyBox.isSome()
suite "admin thread tests":
session.navigate(baseUrl)
session.wait()
login(session, "admin", "admin")
threadTitle.get().sendKeys("This is a thread title!")
replyBox.get().sendKeys("This is content.")
setup:
session.navigate(baseUrl)
session.wait()
createThreadBtn.get().click()
test "can create thread":
with session:
click "#new-thread-btn"
wait()
waitForLoad(session)
sendKeys "#thread-title", adminTitleStr
sendKeys "#reply-textarea", adminContentStr
let newThreadTitle = session.findElement("#thread-title")
check newThreadTitle.isSome()
click "#create-thread-btn"
wait()
check newThreadTitle.get().getText() == "This is a thread title!"
checkText "#thread-title", adminTitleStr
checkText ".original-post div.post-content", adminContentStr
let content = session.findElement(".original-post div.post-content")
check content.isSome()
test "try create duplicate thread":
with session:
click "#new-thread-btn"
wait()
ensureExists "#new-thread"
check content.get().getText() == "This is content."
sendKeys "#thread-title", adminTitleStr
sendKeys "#reply-textarea", adminContentStr
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
test "can like thread":
# Try to like the user thread above
with session:
click userTitleStr, LinkTextSelector
wait()
click ".post-buttons .like-button"
checkText ".post-buttons .like-button .like-count", "1"
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
session.navigate(baseUrl)
session.wait()
logout(session)