Commit 20647618 authored by Esther Vogt's avatar Esther Vogt
Browse files

Merge branch 'development' into 'master'

Deploy final version

See merge request !11
parents d8736029 6e5f73a2
Pipeline #171591 passed with stage
in 30 seconds
import pytest
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from urllib.parse import urljoin
import yaml
import string
import random
from tests.utils import *
from math import ceil, floor
def generate_name(length=10, charset=string.ascii_letters + string.digits):
return ''.join(random.choice(charset) for i in range(length))
def generate_description(length=10, charset=string.ascii_letters + string.digits):
return ''.join(random.choice(charset) for i in range(length))
def generate_unit(length, charset=string.ascii_letters):
return ''.join(random.choice(charset) for i in range(length))
def generate_duration(length=None, charset=None):
return length
def generate_calories(length=None, charset=None):
return length
def generate_bva_input():
"""
Perform boundary value analysis (bva) on the registration page: min, min+, nom, max-, max
-> Input: use boundaries set in test_register_bva.yml
"""
# load the yaml file containing the bva values
with open("tests/test_exercises/test_exercises_bva.yml") as f:
bva = yaml.load(f, Loader=yaml.FullLoader)
# extract the norm value per field + split the duplicate keys
norms = dict()
bva_dict = dict()
for f, b in bva.items():
if "|" in f:
for f_sub in f.split("|"):
bva_dict[f_sub] = list(b.values())
norms[f_sub] = bva_dict[f_sub][2]
else:
bva_dict[f] = list(b.values())
norms[f] = bva_dict[f][2]
# loop over the boundary values
input_lst = []
idx_dict = {0: 'min-', 1: 'min', 2: 'min+', 3: 'norm', 4: 'max-', 5: 'max', 6: 'max+'}
for f, b in bva_dict.items():
input_lengths = norms.copy()
for i in range(len(b)):
input_lengths[f] = b[i]
# note: this additional loop is required to make sure the inputs are unique (i.e. w.r.t. email/username)
inputs = dict()
for k, v in input_lengths.items():
inputs[k] = eval(f'generate_{k}(length={v})')
# add remark on whether test is expected to pass or fail
if i in [0, 6]:
expected_result = f'fail.{idx_dict[i]}.{f}'
else:
expected_result = f'pass.{idx_dict[i]}.{f}'
input_lst.append({expected_result: inputs})
return input_lst
def create_exercise(driver, url, inputs):
# navigate to registration page
exercise_url = urljoin(url, 'exercise.html')
driver.get(exercise_url)
# get all elements required for form
content = driver.find_element(by=By.XPATH, value='//*[@id="form-exercise"]')
# generate defaults for those entries not given in yaml file
for input in content.find_elements(by=By.CLASS_NAME, value='form-control'):
field = input.get_attribute('name')
if field not in inputs.keys():
driver.find_element(by="name", value=field).send_keys(eval(f'generate_{field}()'))
# enter the remaining input values
for k, v in inputs.items():
driver.find_element(by="name", value=k).send_keys(v)
print(f'inputs: {inputs}')
# confirm registration
click_element(driver, '//*[@id="btn-ok-exercise"]')
# wait if registration was accepted
check_redirect(driver, url, next_url='exercises.html')
# note: use fixture to loop over lists of inputs and generate iterative calls to testing function
@pytest.fixture(params=generate_bva_input())
def inputs(request):
return request.param
# def test_create_test_user(remote_browser, base_url):
# user_data = {'username': 'testuser1',
# 'email': 'testuser1@domain.com',
# 'password': '1234',
# 'password1': '1234'}
# create_account(remote_browser, base_url, user_data)
def test_exercises_boundary_values(inputs, remote_browser, base_url):
login(remote_browser, base_url, 'testuser1', '1234')
expected_result, bv, var = list(inputs.keys())[0].split('.')
input_dict = list(inputs.values())[0]
# check if error was raised in case a failure is expected or passes if this is the expectated result otherwise
if expected_result == 'fail':
with pytest.raises(Exception) as e:
create_exercise(remote_browser, base_url, input_dict)
print(f'expected result == "{expected_result}" for {bv} of {var}: {e}')
else:
print(f'expected result == "{expected_result}" for {bv} of {var}')
create_exercise(remote_browser, base_url, input_dict)
unit:
min-: 0
min: 1
min+: 2
norm: 10
max-: 30
max: 31
max+: 50
duration:
min-: -1
min: 0
min+: 1
norm: 60
max-: 1000
max: 1440
max+: 1441
calories:
min-: -1
min: 0
min+: 1
norm: 200
max-: 1000
max: 4000
max+: 4001
\ No newline at end of file
import pytest
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from urllib.parse import urljoin
from tests.utils import login, create_account, click_element
from unittest import TestCase
class TestMyAthletes(TestCase):
def test_login(self, remote_browser, base_url):
# direct to login page
login_url = urljoin(base_url, 'login.html')
remote_browser.get(login_url)
# enter user details (testuser1 is included in seed_users.json)
username, password = 'testuser2', '1234'
remote_browser.find_element(by="name", value='username').send_keys(username)
remote_browser.find_element(by="name", value='password').send_keys(password)
click_element(remote_browser, '//*[@id="btn-login"]')
next_url = 'workouts.html'
WebDriverWait(remote_browser, 5).until(
lambda driver: driver.current_url == urljoin(base_url, next_url),
f'Not forwarded to {next_url}. Invalid inputs?')
import pytest
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from urllib.parse import urljoin
import yaml
from tests.utils import create_account
from tests.test_register.utils import *
def generate_bva_input():
"""
Perform boundary value analysis (bva) on the registration page: min, min+, nom, max-, max
-> Input: use boundaries set in test_register_bva.yml
"""
# load the yaml file containing the bva values
with open("tests/test_register/test_register_bva.yml") as f:
bva = yaml.load(f, Loader=yaml.FullLoader)
# extract the norm value per field + split the duplicate keys
norms = dict()
bva_dict = dict()
for f, b in bva.items():
if "|" in f:
for f_sub in f.split("|"):
bva_dict[f_sub] = list(b.values())
norms[f_sub] = bva_dict[f_sub][2]
else:
bva_dict[f] = list(b.values())
norms[f] = bva_dict[f][2]
# loop over the boundary values
input_lst = []
idx_dict = {0: 'min-', 1: 'min', 2: 'min+', 3: 'norm', 4: 'max-', 5: 'max', 6: 'max+'}
for f, b in bva_dict.items():
input_lengths = norms.copy()
for i in range(len(b)):
input_lengths[f] = b[i]
# note: this additional loop is required to make sure the inputs are unique (i.e. w.r.t. email/username)
inputs = dict()
for k, v in input_lengths.items():
inputs[k] = eval(f'generate_{k}(length={v})')
# add remark on whether test is expected to pass or fail
if i in [0, 6]:
expected_result = f'fail.{idx_dict[i]}.{f}'
else:
expected_result = f'pass.{idx_dict[i]}.{f}'
input_lst.append({expected_result: inputs})
return input_lst
# note: use fixture to loop over lists of inputs and generate iterative calls to testing function
@pytest.fixture(params=generate_bva_input())
def inputs(request):
return request.param
def test_registration_boundary_values(inputs, remote_browser, base_url):
expected_result, bv, var = list(inputs.keys())[0].split('.')
input_dict = list(inputs.values())[0]
# check if error was raised in case a failure is expected or passes if this is the expectated result otherwise
if expected_result == 'fail':
with pytest.raises(Exception) as e:
create_account(remote_browser, base_url, input_dict)
print(f'expected result == "{expected_result}" for {bv} of {var}: {e}')
else:
print(f'expected result == "{expected_result}" for {bv} of {var}')
create_account(remote_browser, base_url, input_dict)
username:
min-: 0
min: 1
min+: 2
norm: 10
max-: 30
max: 150
max+: 151
email:
min-: 5
min: 6
min+: 7
norm: 25
max-: 253
max: 254
max+: 255
# note: same value is used for password and password1
password:
min-: 0
min: 1
min+: 2
norm: 10
max-: 127
max: 128
max+: 4096
phone_number:
min-: 0
min: 7
min+: 8
norm: 10
max-: 14
max: 15
max+: 20
country:
min-: 0
min: 4
min+: 5
norm: 20
max-: 55
max: 56
max+: 57
city:
min-: 0
min: 1
min+: 2
norm: 20
max-: 84
max: 85
max+: 86
street_address:
min-: 0
min: 2
min+: 3
norm: 20
max-: 84
max: 85
max+: 86
import pytest
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from urllib.parse import urljoin
from tests.test_register.utils import *
from itertools import combinations, product
import pandas as pd
import string
from tests.utils import create_account
def pairs(*lists):
for t in combinations(lists, 2):
for pair in product(*t):
yield pair
def generate_ec_to_letter_map():
# define the equivalence classes
equivalence_classes = {
# note: test for uplicate username performed during test case creation
'username': {'fail': ['', '.', '%'], 'pass': ['B%9a.']},
# note: the test for duplicate email addresses will be performed implicitely as soon as the
# same mail address is used multiple times
'email': {'fail': ['', '@.com'], 'pass': ['name@domain.com']},
'password': {'fail': ['', '.', '%'], 'pass': ['a']},
'phone_number': {'fail': ['a', ''], 'pass': [1234567]},
'country': {'fail': ['999', '', '.', '%'], 'pass': ['Norway']},
'city': {'fail': ['999', '', '.', '%'], 'pass': ['Milton-Freewater']},
'street_address': {'fail': ['999', '', '.', '%'], 'pass': ['H9']}
}
# generate mapping for simplified generation of input combinations
expected_outcome_lst = list()
variable_lst = list(equivalence_classes.keys())
value_lst = list()
ec_lst = list()
for letter_idx, i in enumerate(list(equivalence_classes.values())):
letter = string.ascii_lowercase[letter_idx]
ec_lst.append([f'{letter}{ec_idx}' for ec_idx, ec in enumerate(sum(list(i.values()), []))])
expected_outcome_lst.append(sum([[k] * len(v) for k, v in i.items()], []))
value_lst.append([ec for ec_idx, ec in enumerate(sum(list(i.values()), []))])
expected_outcome_dict = dict(zip(sum(ec_lst, []), list(zip(sum(value_lst, []), sum(expected_outcome_lst, [])))))
return variable_lst, expected_outcome_dict, ec_lst
def generate_sequences(ec_lst):
# generate the pairs of combinations
pair_lst = list()
for pair in eval("pairs(" + ",".join([str(i) for i in ec_lst]) + ")"):
pair_lst.append(pair)
# print(f'# combinations: {len(pair_lst)}')
# combine pairs into min. sequences
sequences = list()
pairs_to_cover = pair_lst.copy()
while len(pairs_to_cover) > 0:
s1, s2 = pairs_to_cover[0]
# initialize sequence
test = [s1, s2]
# print(f's1, s2: {test}')
letters_covered = [s1[0], s2[0]]
# print(f'letters_covered: {letters_covered}')
# remove s1, s2 from pairs_to_cover
pairs_to_cover.remove((s1, s2))
# iteratively add elements to sequence
while len(letters_covered) < len(ec_lst):
# check the list of remaining pairs
next = [(n1, n2) for n1, n2 in pairs_to_cover if
((n1 in test) and not (n2[0] in letters_covered)) or
((n2 in test) and not (n1[0] in letters_covered))]
# print(f'\tnext (ptc): {next}')
# if all combinations have been covered already, check the initial list again
if next == []:
next = [(n1, n2) for n1, n2 in pair_lst if
((n1 in test) and not (n2[0] in letters_covered)) or
((n2 in test) and not (n1[0] in letters_covered))]
# print(f'\tnext (pl): {next}')
else:
# remove pair from list to cover
pairs_to_cover.remove(next[0])
# print(f'next: {next} -> {next[0] if next else ""}')
# add next pair to list
test = list(set(test + [next[0][0], next[0][1]]))
letters_covered = list(set(letters_covered + [next[0][0][0], next[0][1][0]]))
# print(f'\ttest (len = {len(test)}): {test}')
# print(f'\ttest (len = {len(test)}): {test}')
# remove all the pair to pair combinations
# print(f'\tpairs_to_cover (1) (len={len(pairs_to_cover)}): {pairs_to_cover}')
for used_pairs in eval("pairs(['" + "'],['".join([i for i in test]) + "'])"):
if used_pairs in pairs_to_cover:
pairs_to_cover.remove(used_pairs)
# print(f'\tadditionally removed: {used_pairs}')
# print(f'\tpairs_to_cover (2) (len={len(pairs_to_cover)}): {pairs_to_cover}')
# add the computed sequence to the final sequence
sequences.append(sorted(test))
pairs_to_cover = list(sorted(pairs_to_cover))
return sequences
def map_sequences_back_to_inputs(sequences, mapping, variable_lst):
ec_seq = []
for seq in sequences:
val_to_outcome = [mapping[i] for i in seq]
ec_seq.append(dict(zip(variable_lst, val_to_outcome)))
return ec_seq
def generate_inputs():
variable_lst, expected_outcome_dict, ec_lst = generate_ec_to_letter_map()
sequences = generate_sequences(ec_lst)
input_lst = map_sequences_back_to_inputs(sequences, expected_outcome_dict, variable_lst)
return input_lst
# note: use fixture to loop over lists of inputs and generate iterative calls to testing function
@pytest.fixture(params=generate_inputs())
def inputs(request):
return request.param
@pytest.fixture(scope="session")
def result_cache():
return {'username': 0, 'email': 0, 'password': 0, 'phone_number': 0, 'country': 0, 'city': 0, 'street_address': 0}
def test_registration_domain(inputs, remote_browser, base_url, result_cache):
# assume failure in case at least one attribute is invalid
if sum([i[1] == 'fail' for i in inputs.values()]) > 0:
expected_result = 'fail'
else:
expected_result = 'pass'
# reformat input
input_dict = {k: v[0] for k, v in inputs.items()}
# check if the same email was already used twice -> adjust in that case!
for variable in ['email', 'username']:
if inputs[variable][1] == 'pass':
if result_cache[variable] >= 2:
# replace last letter by next letter in alphabet
next_letter_idx = (string.ascii_lowercase * 3).index('m') + result_cache[variable]
input_dict[variable] = input_dict[variable][:-1] + (string.ascii_lowercase * 3)[next_letter_idx]
print(f'{variable} already used {result_cache[variable]} times. '
f'New {variable} set: {input_dict[variable]}.')
# count how many times the same valid (!) username and email were already used
result_cache[variable] = result_cache[variable] + 1
# check if error was raised in case a failure is expected or passes if this is the expectated result otherwise
if expected_result == 'fail':
with pytest.raises(Exception) as e:
print(f'expected result == "{expected_result}" for seq = {inputs}')
create_account(remote_browser, base_url, input_dict)
else:
print(f'expected result == "{expected_result}" for seq = {inputs}')
create_account(remote_browser, base_url, input_dict)
def test_result_cache(result_cache):
print(result_cache)
import string
import random
from math import floor
def generate_email(length, charset=string.ascii_letters + string.digits):
# Django Email Validator: https://github.com/django/django/blob/main/django/core/validators.py#L168-L180
# randomly choose suffix length
if length > 6:
suffix_length = random.randint(2, 3)
else:
suffix_length = 2
suffix = ''.join(random.choice(string.ascii_lowercase) for i in range(suffix_length))
rem = length - 2 - suffix_length
# max length for domain name labels is 63 characters per RFC 1034
domain_length = min(63, int(floor(rem / 2)))
domain = ''.join(random.choice(charset) for i in range(domain_length))
prefix = ''.join(random.choice(charset) for i in range(rem - domain_length))
return prefix + '@' + domain + '.' + suffix
def generate_username(length, charset=string.ascii_letters + string.digits + '_@+.-'):
return ''.join(random.choice(charset) for i in range(length))
def generate_password(length, charset=string.ascii_letters + string.digits + string.punctuation):
return ''.join(random.choice(charset) for i in range(length))
def generate_phone_number(length, charset=string.digits):
return ''.join(random.choice(charset) for i in range(length))
def generate_country(length, charset=string.ascii_letters):
return ''.join(random.choice(charset) for i in range(length))
def generate_city(length, charset=string.ascii_letters):
return ''.join(random.choice(charset) for i in range(length))
def generate_street_address(length, charset=string.ascii_letters + string.digits):
return ''.join(random.choice(charset) for i in range(length))
import pytest
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from urllib.parse import urljoin
from time import sleep
from tests.utils import *
class TestWorkoutsAthletes:
@pytest.fixture(scope='class', autouse=True)
def setUp(self, setUp, load_users, load_workouts, remote_browser, base_url):
# get athletes
users = {k: v for k, v in load_users.items() if k.startswith('athlete')}
# login athlete
user_details = users['athlete1'] ##list(self.users.keys())[0]
login(remote_browser, base_url, user_details['username'], user_details['password'])
# re-direct user to workouts.html
remote_browser.get(urljoin(base_url, 'workouts.html'))
@pytest.fixture(scope="class")
def result_cache(self):
return dict()
# my-workouts
def test_visibility_my_workouts(self, remote_browser, base_url, load_workouts, load_users, result_cache):
validate_displayed_workout_list(remote_browser, base_url, load_workouts, load_users, 'my-workouts',
result_cache)
def test_visibility_my_workouts_details(self, remote_browser, load_workouts, result_cache):
validate_displayed_workout_data(remote_browser, load_workouts, 'my-workouts', result_cache, type='details')
def test_visibility_my_workouts_files(self, remote_browser, load_workouts, result_cache):
validate_displayed_workout_data(remote_browser, load_workouts, 'my-workouts', result_cache, type='files')
def test_visibility_my_workouts_comments(self, remote_browser, load_workouts, result_cache):
validate_displayed_workout_data(remote_browser, load_workouts, 'my-workouts', result_cache, type='comments')
# public-workouts
def test_visibility_public_workouts(self, remote_browser, base_url, load_workouts, load_users, result_cache):
validate_displayed_workout_list(remote_browser, base_url, load_workouts, load_users, 'public-workouts',
result_cache)
def test_visibility_public_workouts_details(self, remote_browser, load_workouts, result_cache):
validate_displayed_workout_data(remote_browser, load_workouts, 'public-workouts', result_cache, type='details')
def test_visibility_public_workouts_files(self, remote_browser, load_workouts, result_cache):
validate_displayed_workout_data(remote_browser, load_workouts, 'public-workouts', result_cache, type='files')
def test_visibility_public_workouts_comments(self, remote_browser, load_workouts, result_cache):
validate_displayed_workout_data(remote_browser, load_workouts, 'public-workouts', result_cache, type='comments')
# athlete-workouts
def test_visibility_athlete_workouts(self, remote_browser, base_url, load_workouts, load_users, result_cache):
validate_displayed_workout_list(remote_browser, base_url, load_workouts, load_users, 'athlete-workouts',
result_cache)