Commit 9f8767fe authored by Tor Martin Frøberg Wang's avatar Tor Martin Frøberg Wang
Browse files

Completed system tests of leaderboards. Fixed bug where an empty leaderboard...

Completed system tests of leaderboards. Fixed bug where an empty leaderboard was visible on the 'create new exercise' page
parent dfbe270e
This diff is collapsed.
This diff is collapsed.
......@@ -148,6 +148,9 @@ MEDIA_URL = "/media/"
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
# Increased the page size to 100, because the front-end doesn't fetch more workouts
# than what the page size is here
"PAGE_SIZE": 100,
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
......
import unittest
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from webdriver_manager.firefox import GeckoDriverManager
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from datetime import datetime
# <---------- IMPORTANT --------->
# MUST HAVE FIREFOX, SELENIUM AND WEBDRIVER INSTALLED TO RUN THESE TESTS:
# pip install selenium
# pip install webdriver-manager
# Before running this test, remember to host the application on http://localhost:9090 using docker-compose up --build
# in the main project folder
# Test is run by: python seleniumLeaderboardIntegrationTest.py (when in the workouts-folder)
class TestExerciseLeaderboard(unittest.TestCase):
# Creates two unique usernames to be used in the tests;
# by appending the current exact time to the username, we will always have unique usernames
uniqueUsername1 = "LikeTestUser1-" + datetime.utcnow().strftime("%m-%d-%Y-%H-%M-%S.%f")
uniqueUsername2 = "LikeTestUser2-" + datetime.utcnow().strftime("%m-%d-%Y-%H-%M-%S.%f")
# Runs before each test
def setUp(self):
self.driver = webdriver.Firefox(executable_path=GeckoDriverManager().install())
# Sets an implicit wait of 10 seconds (Test will wait for up to 10 seconds for an expected DOM element)
self.driver.implicitly_wait(10)
# Tests that a user auto-likes his own workout
def test_is_on_leaderboard(self):
driver = self.driver
# Opens the web browser, and logs out just in case someone was already logged in
driver.get("http://localhost:9090/logout.html")
time.sleep(2)
# Registers a new user
self.registerUser(self.__class__.uniqueUsername1)
# Find and clicks the button to go to the exercise page
exerciseNavButton = driver.find_element_by_id("nav-exercises")
exerciseNavButton.click()
time.sleep(2)
# Find and clicks the button to go the the "create new exercise" page
createExerciseButton = driver.find_element_by_id("btn-create-exercise")
createExerciseButton.click()
time.sleep(2)
# Finds the input fields
exerciseNameField = driver.find_element_by_id("inputName")
exerciseDescriptionField = driver.find_element_by_id("inputDescription")
exerciseUnitField = driver.find_element_by_id("inputUnit")
# Inputs values into the input fields
exerciseNameField.send_keys("TestLeaderboardExercise")
exerciseDescriptionField.send_keys("This exercise is only meant for testing purposes")
exerciseUnitField.send_keys("Test")
time.sleep(1)
# Submits the new exerice
submitExerciseButton = driver.find_element_by_id("btn-ok-exercise")
submitExerciseButton.click()
time.sleep(2)
self.scroll_down()
# Inspects the newest exercise
exercise = driver.find_elements_by_css_selector("a.list-group-item")[-1]
exercise.click()
time.sleep(2)
# All the rows in the leaderboard
leaderboardRows = driver.find_elements_by_tag_name("tr")
# Tests that the leaderboard has 2 rows; one is the header, and the other should be the user's entry
self.assertEqual(2, len(leaderboardRows))
print([ele.text for ele in leaderboardRows])
leaderboard = []
# Converts the text into a list of dictionaries for easy testing purposes
for i in range(1, len(leaderboardRows)):
row = leaderboardRows[i].text.split(" ")
leaderboard.append({"rank": row[0], "username": row[1], "score": row[2]})
# Tests that the rank is correct in the leaderboard
self.assertEqual("1", leaderboard[0]["rank"])
# Tests that the username is correct in the leaderboard
self.assertEqual(self.__class__.uniqueUsername1, leaderboard[0]["username"])
#Tests that the score is correct in the leaderboard
self.assertEqual("0", leaderboard[0]["score"])
time.sleep(100)
# <---------Deletes the newest exercise-------->
# Find and clicks the button to go to the exercise page
exerciseNavButton = driver.find_element_by_id("nav-exercises")
exerciseNavButton.click()
time.sleep(2)
self.scroll_down()
# Inspects the newest exercise
exerciseToDelete = driver.find_elements_by_css_selector("a.list-group-item")[-1]
exerciseToDelete.click()
time.sleep(2)
# Starts editing the exercise
editButton = driver.find_element_by_id("btn-edit-exercise")
editButton.click()
time.sleep(1)
# Deletes the exercise
deleteWorkoutButton = driver.find_element_by_id("btn-delete-exercise")
deleteWorkoutButton.click()
time.sleep(1)
# >---------------------------<
# Function used to register a new user
def registerUser(self, uniqueUsername):
print(uniqueUsername)
driver = self.driver
# Logs out, just in case a user is already logged in
driver.get("http://localhost:9090/logout.html")
time.sleep(2)
# Goes to the register page
driver.get("http://localhost:9090/register.html")
time.sleep(2)
# Finds all the input fields in the register form
usernameField = driver.find_element_by_name('username')
emailField = driver.find_element_by_name('email')
passwordField = driver.find_element_by_name('password')
repeatPasswordField = driver.find_element_by_name('password1')
phoneNumberField = driver.find_element_by_name('phone_number')
countryField = driver.find_element_by_name('country')
cityField = driver.find_element_by_name('city')
streetAddressField = driver.find_element_by_name('street_address')
# Inputs values in all the registration fields
usernameField.send_keys(uniqueUsername)
emailField.send_keys(uniqueUsername + "@test.test")
passwordField.send_keys("123")
repeatPasswordField.send_keys("123")
phoneNumberField.send_keys("12312312")
countryField.send_keys("Norway")
cityField.send_keys("Narvik")
streetAddressField.send_keys("Kvartslia")
# Finds and clicks the button that creates the account
createAccountButton = driver.find_element_by_id("btn-create-account")
createAccountButton.click()
time.sleep(2)
# Runs after running the tests
def tearDown(self):
self.driver.close()
# Code for scrolling to the end of a dynamically loading page;
# from https://stackoverflow.com/questions/48850974/selenium-scroll-to-end-of-page-in-dynamically-loading-webpage
def scroll_down(self):
"""A method for scrolling the page."""
# Get scroll height.
last_height = self.driver.execute_script("return document.body.scrollHeight")
while True:
# Scroll down to the bottom.
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# Wait to load the page.
time.sleep(2)
# Calculate new scroll height and compare with last scroll height.
new_height = self.driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
if __name__ == "__main__":
unittest.main()
This diff is collapsed.
......@@ -17,7 +17,7 @@ from datetime import datetime
# Before running this test, remember to host the application on http://localhost:9090 using docker-compose up --build
# in the main project folder
# Test is run by: python seleniumLikeIntegrationTest.py (when in the workouts-folder and while website is live with docker)
# Test is run by: python seleniumLikeSystemTest.py (when in the workouts-folder and while website is live with docker)
class TestWorkoutLikes(unittest.TestCase):
# Creates two unique usernames to be used in the tests;
......@@ -25,6 +25,9 @@ class TestWorkoutLikes(unittest.TestCase):
uniqueUsername1 = "LikeTestUser1-" + datetime.utcnow().strftime("%m-%d-%Y-%H-%M-%S.%f")
uniqueUsername2 = "LikeTestUser2-" + datetime.utcnow().strftime("%m-%d-%Y-%H-%M-%S.%f")
# Simple password for test users
userPassword = "ABCD1234"
# Runs before each test
def setUp(self):
......@@ -33,8 +36,9 @@ class TestWorkoutLikes(unittest.TestCase):
# Sets an implicit wait of 10 seconds (Test will wait for up to 10 seconds for an expected DOM element)
self.driver.implicitly_wait(10)
# Tests that a user auto-likes his own workout
def test_auto_liked_own_workout_and_only_one_like(self):
def test01_auto_liked_own_workout_and_only_one_like(self):
driver = self.driver
# Opens the web browser, and logs out just in case someone was already logged in
......@@ -60,8 +64,8 @@ class TestWorkoutLikes(unittest.TestCase):
# Inputs values in all the registration fields
usernameField.send_keys(uniqueUsername)
emailField.send_keys(uniqueUsername+"@test.test")
passwordField.send_keys("123")
repeatPasswordField.send_keys("123")
passwordField.send_keys(self.__class__.userPassword)
repeatPasswordField.send_keys(self.__class__.userPassword)
phoneNumberField.send_keys("12312312")
countryField.send_keys("Norway")
cityField.send_keys("Narvik")
......@@ -73,7 +77,7 @@ class TestWorkoutLikes(unittest.TestCase):
# The "new workout" button sometimes doesn't registers clicks even though it has been loaded into the DOM.
# Therefore, we wait 1 second before clicking it
time.sleep(1)
time.sleep(0.5)
# Finds and clicks the button that opens the page for creating a new workout
newWorkoutButton = driver.find_element_by_id("btn-create-workout")
......@@ -85,31 +89,28 @@ class TestWorkoutLikes(unittest.TestCase):
workoutNotesField = driver.find_element_by_id("inputNotes")
# Waits until fields become editable
time.sleep(2)
time.sleep(1)
# Inputs values into fields
workoutNameField.send_keys("TestWorkout")
workoutDateField.clear();
workoutDateField.send_keys("1111-01-01 00:01");
workoutNotesField.send_keys("This is an auto-generated workout meant for testing")
time.sleep(1)
time.sleep(0.5)
# Finds and clicks the button that publishes the new workout
publishWorkoutButton = driver.find_element_by_id("btn-ok-workout")
publishWorkoutButton.click()
time.sleep(2)
time.sleep(0.5)
# Scrolls to the bottom of the page; a 'problem' (due to dynamic loading) with not every workout being
# loaded into the DOM appears when we have too many workouts. Scrolling to the bottom fixes this.
self.scroll_down()
time.sleep(1)
# Finds and clicks the button that views the user's own workouts
myWorkoutsButton = driver.find_element_by_id("list-my-workouts-list")
myWorkoutsButton.click()
time.sleep(2)
time.sleep(1)
# Finds the like button and the like amount of the newly-created workout
likeButton = driver.find_elements_by_css_selector("a.like-button")[-1]
......@@ -135,7 +136,7 @@ class TestWorkoutLikes(unittest.TestCase):
time.sleep(1)
likeButton.click()
time.sleep(2)
time.sleep(1)
# Gets the newest like number that is in the DOM
likeNumber = driver.find_elements_by_css_selector("td.like-amount")[-1]
......@@ -146,17 +147,16 @@ class TestWorkoutLikes(unittest.TestCase):
# Refresh the site so that we can be sure that we fetch the newest like amounts, and that the workout wasn't
# actually re-liked by the owner (that no change happened in the database)
driver.refresh()
time.sleep(2)
time.sleep(1)
# Scrolls to the bottom of the page; a 'problem' (due to dynamic loading) with not every workout being
# loaded into the DOM appears when we have too many workouts. Scrolling to the bottom fixes this.
self.scroll_down()
time.sleep(1)
# Finds and clicks the button that views the user's own workouts
myWorkoutsButton = driver.find_element_by_id("list-my-workouts-list")
myWorkoutsButton.click()
time.sleep(2)
time.sleep(1)
# Finds the like button and the like amount of the newly-created workout
likeButton = driver.find_elements_by_css_selector("a.like-button")[-1]
......@@ -169,11 +169,9 @@ class TestWorkoutLikes(unittest.TestCase):
# Tests that the like button is still active (already liked by the owner)
self.assertEqual("like-button active", likeButton.get_attribute("class"))
time.sleep(1)
# Tests that a user can like another user's public workout *once*
def test_liked_by_other_user_and_only_one_like(self):
def test02_liked_by_other_user_and_only_one_like(self):
driver = self.driver
# Opens the web browser, and logs out just in case someone was already logged in
......@@ -199,8 +197,8 @@ class TestWorkoutLikes(unittest.TestCase):
# Inputs values in all the registration fields
usernameField.send_keys(uniqueUsername)
emailField.send_keys(uniqueUsername + "@test.test")
passwordField.send_keys("123")
repeatPasswordField.send_keys("123")
passwordField.send_keys(self.__class__.userPassword)
repeatPasswordField.send_keys(self.__class__.userPassword)
phoneNumberField.send_keys("12312312")
countryField.send_keys("Norway")
cityField.send_keys("Narvik")
......@@ -209,13 +207,11 @@ class TestWorkoutLikes(unittest.TestCase):
# Finds and clicks the button that creates the account
createAccountButton = driver.find_element_by_id("btn-create-account")
createAccountButton.click()
time.sleep(1)
time.sleep(0.5)
# Scrolls to the bottom of the page; a 'problem' (due to dynamic loading) with not every workout being
# loaded into the DOM appears when we have too many workouts. Scrolling to the bottom fixes this.
self.scroll_down()
time.sleep(1)
# Finds the like button and the like amount of the newly-created workout
likeButton = driver.find_elements_by_css_selector("a.like-button")[-1]
......@@ -229,7 +225,7 @@ class TestWorkoutLikes(unittest.TestCase):
# Clicks the like button
likeButton.click()
time.sleep(2)
time.sleep(1)
# Gets the newest like number that is in the DOM
likeNumber = driver.find_elements_by_css_selector("td.like-amount")[-1]
......@@ -245,7 +241,6 @@ class TestWorkoutLikes(unittest.TestCase):
# Scrolls to the bottom of the page; a 'problem' (due to dynamic loading) with not every workout being
# loaded into the DOM appears when we have too many workouts. Scrolling to the bottom fixes this.
self.scroll_down()
time.sleep(1)
# Finds the like button and the like amount of the newly-created workout
likeButton = driver.find_elements_by_css_selector("a.like-button")[-1]
......@@ -257,7 +252,7 @@ class TestWorkoutLikes(unittest.TestCase):
# Tests that the like button is still active (already liked)
self.assertEqual("like-button active", likeButton.get_attribute("class"))
time.sleep(1)
time.sleep(0.5)
# Tries to re-click the like button to unlike; this should not be possible
try:
......@@ -273,7 +268,7 @@ class TestWorkoutLikes(unittest.TestCase):
time.sleep(1)
likeButton.click()
time.sleep(2)
time.sleep(1)
# Gets the newest like number that is in the DOM
likeNumber = driver.find_elements_by_css_selector("td.like-amount")[-1]
......@@ -284,12 +279,11 @@ class TestWorkoutLikes(unittest.TestCase):
# Refresh the site so that we can be sure that we fetch the newest like amounts, and that the workout wasn't
# actually re-liked by the owner (that the change happened in the database as well)
driver.refresh()
time.sleep(2)
time.sleep(1)
# Scrolls to the bottom of the page; a 'problem' (due to dynamic loading) with not every workout being
# loaded into the DOM appears when we have too many workouts. Scrolling to the bottom fixes this.
self.scroll_down()
time.sleep(1)
# Finds the like button and the like amount of the newly-created workout
likeButton = driver.find_elements_by_css_selector("a.like-button")[-1]
......@@ -302,20 +296,18 @@ class TestWorkoutLikes(unittest.TestCase):
# Tests that the like button is still active (already liked)
self.assertEqual("like-button active", likeButton.get_attribute("class"))
time.sleep(1)
# *Not a test*, just a cleanup that deletes the workout that was created during the other tests. Tried using
# tearDownClass, but that did not let me access the website
def test_remove_created_workout(self):
def test99_remove_created_workout(self):
driver = self.driver
# Opens the web browser, and logs out just in case someone was already logged in
driver.get("http://localhost:9090/logout.html")
time.sleep(2)
time.sleep(1)
driver.get("http://localhost:9090/login.html")
time.sleep(2)
time.sleep(1)
# Finds all the input fields in the register form
usernameField = driver.find_element_by_name('username')
......@@ -326,33 +318,31 @@ class TestWorkoutLikes(unittest.TestCase):
# Inputs values in all the registration fields
usernameField.send_keys(uniqueUsername)
passwordField.send_keys("123")
passwordField.send_keys(self.__class__.userPassword)
logInButton = driver.find_element_by_id("btn-login")
logInButton.click()
time.sleep(2)
time.sleep(1)
# Scrolls to the bottom of the page; a 'problem' (due to dynamic loading) with not every workout being
# loaded into the DOM appears when we have too many workouts. Scrolling to the bottom fixes this.
self.scroll_down()
time.sleep(1)
# Finds and clicks the button that views the user's own workouts
myWorkoutsButton = driver.find_element_by_id("list-my-workouts-list")
myWorkoutsButton.click()
time.sleep(2)
time.sleep(0.5)
workout = driver.find_elements_by_css_selector("a.list-group-item")[-1]
workout.click()
time.sleep(2)
time.sleep(1)
editButton = driver.find_element_by_id("btn-edit-workout")
editButton.click()
time.sleep(1)
time.sleep(0.5)
deleteWorkoutButton = driver.find_element_by_id("btn-delete-workout")
deleteWorkoutButton.click()
time.sleep(1)
# Runs after running the tests
......
......@@ -45,7 +45,7 @@
</div>
</form>
<div class="row">
<div class="row" id="leaderboardTitle">
<div class="col-lg">
<h3 class="mt-3">Leaderboard</h3>
</div>
......
......@@ -135,10 +135,7 @@ async function fetchLeaderBoards(id) {
let table = document.getElementById("leaderboardstable");
let row, cell;
//The user's own score will always be placed last in the JSON response
let userIndex = data.length - 1;
for (let i = 0; i < data.length - 1; i++) {
for (let i = 0; i < Math.min(6, data.length); i++) {
row = table.insertRow();
cell = row.insertCell();
cell.textContent = data[i].rank;
......@@ -147,16 +144,6 @@ async function fetchLeaderBoards(id) {
cell = row.insertCell();
cell.textContent = data[i].value;
}
//If the user is not in top 5, the users score will also be rendered
if (data[userIndex].rank > 5) {
row = table.insertRow();
cell = row.insertCell();
cell.textContent = data[userIndex].rank;
cell = row.insertCell();
cell.textContent = data[userIndex].name;
cell = row.insertCell();
cell.textContent = data[userIndex].value;
}
}
return data;
......@@ -167,6 +154,8 @@ window.addEventListener("DOMContentLoaded", async () => {
okButton = document.querySelector("#btn-ok-exercise");
deleteButton = document.querySelector("#btn-delete-exercise");
editButton = document.querySelector("#btn-edit-exercise");
leaderboardTable = document.querySelector("#leaderboardstable");
leaderboardTitle = document.querySelector("#leaderboardTitle");
oldFormData = null;
const urlParams = new URLSearchParams(window.location.search);
......@@ -197,7 +186,9 @@ window.addEventListener("DOMContentLoaded", async () => {
okButton.addEventListener("click", async () => await createExercise());
cancelButton.addEventListener("click", handleCancelButtonDuringCreate);
}
leaderboardTable.style.display = "none";
leaderboardTitle.style.display = "none";
}
});
......@@ -54,7 +54,7 @@
<td>
Likes:
</td>
<td></td>
<td class="like-amount"></td>
<td>
<a href="#" class="like-button">
<?xml version="1.0" encoding="utf-8"?>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment