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
backend/secfit/.vscode/
gitbackend/secfit/.vscode/
backend/secfit/*/migrations/__pycache__/
backend/secfit/*/__pycache__/
backend/secfit/db.sqlite3
......@@ -10,4 +10,9 @@ package-lock.json
/backend/secfit/coverage.xml
/backend/secfit/report.xml
/backend/secfit/htmlcov/
chromedriver.exe
/tests/.pytest_cache/
/tests/report.xml
/tests/report.html
node_modules
/tests/.pytest_cache/
......@@ -7,10 +7,9 @@ variables:
stages:
- test-init
- dpl-dev # deploy a review app
# - test-dev # run black box testing against the review app
- test-dev # run black box testing against the review app
- dpl-prod # deploy the productive app
integration-test-backend:
# generate junit xml report + coverage report -> see backend/secfit/pytest.ini
image: python:3.8
......@@ -30,40 +29,86 @@ integration-test-backend:
cobertura: backend/secfit/coverage.xml
coverage: '/TOTAL.*\s+(\d+\%)/'
deploy-dev:
image: ruby
stage: dpl-dev
variables:
HEROKU_API_KEY: $HEROKU_STAGING_API_KEY
script:
# update the list of available packages
- apt-get update -qy
# install the Ruby runtime on system
- apt-get install -y ruby-dev
- apt-get update
- apt-get install apt-transport-https
- echo "deb https://cli-assets.heroku.com/branches/stable/apt ./" > /etc/apt/sources.list.d/heroku.list
- wget -O- https://cli-assets.heroku.com/apt/release.key | apt-key add -
- apt-get update
- apt-get install -y heroku ruby-dev
- gem install dpl
- dpl --provider=heroku --app=$APP_NAME_FRONTEND_DEV --api-key=$HEROKU_STAGING_API_KEY
- dpl --provider=heroku --app=$APP_NAME_BACKEND_DEV --api-key=$HEROKU_STAGING_API_KEY
# - heroku pg:reset --confirm $APP_NAME_BACKEND_DEV --exit-code --app $APP_NAME_BACKEND_DEV
# - heroku run python backend/secfit/manage.py migrate -a $APP_NAME_BACKEND_DEV
- echo "Deployed to DEV server"
- heroku pg:reset --confirm $APP_NAME_BACKEND_DEV -a $APP_NAME_BACKEND_DEV
- heroku run python backend/secfit/manage.py migrate -a $APP_NAME_BACKEND_DEV
- heroku run python backend/secfit/manage.py loaddata backend/secfit/seed_test.json -a $APP_NAME_BACKEND_DEV
- echo "Reset DB, ran migrations and uploaded seed data for testing."
only:
- add-team
#
#bb-test-frontend:chrome:
# image: python:3.8
# stage: test-dev
# services:
# - selenium/standalone-chrome
# script:
# - cd tests
# - apt-get update -qy
# - pip install -r requirements.txt
# - pytest --browser=$BROWSER --local='false'
# artifacts:
# when: always # also upload reports if job fails
# expire_in: 1 week
# reports:
# junit: tests/report.xml
# only:
# - bbtesting-ex2-t3
- development
two-way-domain:chrome:
image: python:3.8
stage: test-dev
when: manual
services:
- selenium/standalone-chrome
script:
- cd tests
- apt-get update -qy
- pip install -r requirements.txt
- pytest test_register_domain.py -rP --perform_setup false --verbose --junitxml=test_register_domain.xml --browser=$BROWSER --local='false'
artifacts:
when: always # also upload reports if job fails
expire_in: 1 week
reports:
junit: tests/test_register_domain.xml
only:
- development
bva-register:chrome:
image: python:3.8
stage: test-dev
when: manual
services:
- selenium/standalone-chrome
script:
- cd tests
- apt-get update -qy
- pip install -r requirements.txt
- pytest test_register_bva.py -rP --perform_setup false --verbose --junitxml=test_register_bva.xml --browser=$BROWSER --local='false'
artifacts:
when: always # also upload reports if job fails
expire_in: 1 week
reports:
junit: tests/test_register_bva.xml
only:
- development
bva-exercises:chrome:
image: python:3.8
stage: test-dev
when: manual
services:
- selenium/standalone-chrome
script:
- cd tests
- apt-get update -qy
- pip install -r requirements.txt
- pytest test_exercises_bva.py -rP --perform_setup false --verbose --junitxml=test_exercises_bva.xml --browser=$BROWSER --local='false'
artifacts:
when: always # also upload reports if job fails
expire_in: 1 week
reports:
junit: tests/test_exercises_bva.xml
only:
- development
deploy-prod:
image: ruby
......@@ -71,12 +116,17 @@ deploy-prod:
when: manual
allow_failure: false
script:
- apt-get update -qy
- apt-get install -y ruby-dev
- apt-get update
- apt-get install apt-transport-https
- echo "deb https://cli-assets.heroku.com/branches/stable/apt ./" > /etc/apt/sources.list.d/heroku.list
- wget -O- https://cli-assets.heroku.com/apt/release.key | apt-key add -
- apt-get update
- apt-get install -y heroku ruby-dev
- gem install dpl
- dpl --provider=heroku --app=$APP_NAME_FRONTEND_PROD --api-key=$HEROKU_STAGING_API_KEY
- dpl --provider=heroku --app=$APP_NAME_BACKEND_PROD --api-key=$HEROKU_STAGING_API_KEY
# - heroku run python backend/secfit/manage.py migrate -a $APP_NAME_BACKEND_PROD
- echo "Deployed to PROD server"
- heroku run python backend/secfit/manage.py migrate -a $APP_NAME_BACKEND_PROD
- echo "Ran migrations on PROD backend"
only:
- master
......@@ -270,6 +270,11 @@ scripts except __init__.py file and re-create migrations
### Test-driven development (TDD) / CI/CD
##
**Q:** What should a typical CI/CD pipeline look like?
- [Gitlab Docu Pipeline Architectures](https://docs.gitlab.com/ee/ci/pipelines/pipeline_architectures.html)
- [Medium intro to Gitlab Jobs, Stages, Pipelines](https://medium.com/@ryzmen/gitlab-fast-pipelines-stages-jobs-c51c829b9aa1)
##
**Q:** What is the general concept of CI/CD? How does this look like on Gitlab?
- References:
......@@ -288,8 +293,8 @@ Human intervention is not required.
- **Selenium and WebdriverIO**
- References:
- [Gitlab Docu](https://docs.gitlab.com/ee/ci/examples/end_to_end_testing_webdriverio/)
- for JavaScript-based applications
- **Selenium**: Selenium is a piece of software that can control web browsers, e.g., to make them visit a specific URL
- **Selenium**: Selenium is a piece of software that can control web browsers (=JavaScript-based applications), e.g.,
to make them visit a specific URL
or interact with elements on the page. It can be programmatically controlled from a variety of programming languages.
- **WebdriverIO**:
- functions:
......@@ -303,8 +308,48 @@ can simply pass CSS selectors to browser.element to get access to elements on th
example, to click on the link back to the home page.
##
**Q:** How to run tests locally with Selenium and WebdriverIO?
- [Gitlab Docu - Selenium/WebdriverIO](https://docs.gitlab.com/ee/ci/examples/end_to_end_testing_webdriverio/#running-locally)
**Q:** How to run tests on the frontend with Selenium and WebdriverIO?
- [General Gitlab Docu - Selenium/WebdriverIO](https://docs.gitlab.com/ee/ci/examples/end_to_end_testing_webdriverio/#running-locally)
- [Selenium with Docker and Gitlab](https://github.com/esthervogt/python-gitlabci-selenium)
- very helpful!
- [E2E Testing with separate backend/frontend](https://bierus.medium.com/end-2-end-testing-with-separated-fronted-f2a5dc5be12)
- [Strategies for removal of test data](https://bierus.medium.com/end-2-end-testing-with-separated-fronted-f2a5dc5be12)
##
**Q:** How to launch a selenium chrome standalone browser?
- [Stackoverflow Article](https://stackoverflow.com/questions/45323271/how-to-run-selenium-with-chrome-in-docker)
- start container: ```docker run -d -p 4444:4444 selenium/standalone-chrome```
- stop container: ```docker stop <CONTAINER ID>```
- run: ```pytest```
##
**Q:** What is the difference between pytest.ini and conftest.py?
- pytest.ini: This is the primary pytest configuration file that allows you to change default behavior.
- conftest.py: This is a local plugin to allow hook functions and fixtures for the directory where the conftest.py file
exists and all subdirectories.
##
**Q:** How to make two tests depend on each other?
- [Stackoverflow article](https://stackoverflow.com/questions/10464502/how-can-i-skip-a-test-if-another-test-fails-with-py-test)
- use [```pip install pytest-dependency```](https://pypi.org/project/pytest-dependency/)
##
**Q:** What are helpful arguments for pytest?
- ```pytest -rP```: shows the captured output of passed tests
- ```pytest -rx```: shows the captured output of failed tests (default behaviour)
##
**Q:** What criteria is tested by the django EmailValidator?
- [Source Code EmailValidator](max length for domain name labels is 63 characters per RFC 1034)
##
**Q:** How to parameterize the generation of test functions?
- [Stackoverflow Article](https://stackoverflow.com/questions/32999470/parametrize-pytest-fixture/33208377#33208377)
##
**Q:** How to connect to localhost urls when running selenium in standalone container?
- [Stackoverflow Article](https://stackoverflow.com/questions/31324981/how-to-access-host-port-from-docker-container):
use host.docker.internal instead of localhost in the url
##
**Q:** What is the difference between unit, integration, functional, E2E and acceptance tests?
......@@ -411,6 +456,12 @@ need to make sure that the given user account has sufficient privileges to creat
image and a URL that the image points to. Examples for badges can be the pipeline status, test coverage, latest release,
or ways to contact the project maintainers.
##
**Q:** How to get the XPath of an element?
- Right-click "inspect" on the item you are trying to find the XPath.
- Right-click on the highlighted area on the HTML DOM.
- Go to Copy > select 'Copy XPath'.
## SecFit specifics
##
**Q:** What are athlete_files?
......
......@@ -46,7 +46,7 @@ RUN DJANGO_SUPERUSER_USERNAME=${DJANGO_SUPERUSER_USERNAME} \
&& echo "If you wish to alter the user credentials, then delete the user first."
# Create some exercises from seed data
RUN python manage.py loaddata seed.json
RUN python manage.py loaddata seed_test.json
#RUN python manage.py loaddata seed_users.json
# Run wsgi server with gunicorn
......
[pytest]
DJANGO_SETTINGS_MODULE=secfit.settings
python_files = tests.py test_*.py *_tests.py
addopts = --junitxml=report.xml --cov-report term-missing --cov=. --cov-report xml:coverage.xml
junit_logging = system-out
addopts = --verbose --junitxml=report.xml --cov-report term-missing --cov=. --cov-report xml:coverage.xml
\ No newline at end of file
[
{
"model": "workouts.workout",
"pk": 1,
"fields": {
"name": "workout1c1",
"date": "2022-03-23T22:10:00Z",
"notes": "workout1c1",
"owner": 6,
"visibility": "PU"
}
},
{
"model": "workouts.workout",
"pk": 2,
"fields": {
"name": "workout2c1",
"date": "2022-03-23T22:10:00Z",
"notes": "workout2c1",
"owner": 6,
"visibility": "CO"
}
},
{
"model": "workouts.workout",
"pk": 3,
"fields": {
"name": "workout3c1",
"date": "2022-03-23T22:12:00Z",
"notes": "workout3c1",
"owner": 6,
"visibility": "PR"
}
},
{
"model": "workouts.workout",
"pk": 4,
"fields": {
"name": "workout1a1",
"date": "2022-03-23T22:11:00Z",
"notes": "workout1a1",
"owner": 7,
"visibility": "PU"
}
},
{
"model": "workouts.workout",
"pk": 5,
"fields": {
"name": "workout2a1",
"date": "2022-03-23T22:12:00Z",
"notes": "workout2a1",
"owner": 7,
"visibility": "CO"
}
},
{
"model": "workouts.workout",
"pk": 6,
"fields": {
"name": "workout3a1",
"date": "2022-03-24T22:12:00Z",
"notes": "workout3a1",
"owner": 7,
"visibility": "PR"
}
},
{
"model": "workouts.exercise",
"pk": 1,
"fields": {
"name": "Push-up",
"description": "A push-up (or press-up in British English) is a common calisthenics exercise beginning from the prone position.",
"duration": 0,
"calories": 0,
"muscleGroup": "Legs",
"unit": "reps"
}
},
{
"model": "workouts.exercise",
"pk": 2,
"fields": {
"name": "Crunch",
"description": "The crunch is one of the most popular abdominal exercises.",
"duration": 0,
"calories": 0,
"muscleGroup": "Legs",
"unit": "reps"
}
},
{
"model": "workouts.exercise",
"pk": 3,
"fields": {
"name": "Plank",
"description": "The plank is an isometric core strength exercise that involves maintaining a position similar to a push-up for the maximum possible time.",
"duration": 0,
"calories": 0,
"muscleGroup": "Legs",
"unit": "seconds"
}
},
{
"model": "workouts.workoutfile",
"pk": 1,
"fields": {
"workout": 4,
"owner": 7,
"file": "workouts/4/workout-file.jpg"
}
},
{
"model": "users.user",
"pk": 1,
"fields": {
"password": "pbkdf2_sha256$216000$3BL8IxTEIkzV$Fh79DOZmk6CFrFE7/Glubxk50SzCkzF1P9cLKIqSpWk=",
"last_login": "2022-03-02T12:47:32.750Z",
"password": "pbkdf2_sha256$216000$a6UXDemjTOVX$bJ/Y/PCpQj2fFTU/SsdM9Hhhx6KXYixFjMA2bL8XYNw=",
"last_login": "2022-03-23T22:03:40.282Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "esther-vogt@gmx.net",
"is_staff": true,
"is_active": true,
"date_joined": "2022-02-23T19:40:47.604Z",
"date_joined": "2022-03-21T10:38:29.564Z",
"email": "esther-vogt@gmx.net",
"coach": null,
"phone_number": "",
"country": "",
"city": "",
"street_address": "",
"role": "Athletes",
"role": "Athlete",
"groups": [],
"user_permissions": []
}
},
{
"model": "users.user",
"pk": 2,
"pk": 6,
"fields": {
"password": "pbkdf2_sha256$216000$MNqM0ESAfJh2$pE/EqC6q6EmCg0II1aBKGlHGEKhw3lPL6+h2tBswvWA=",
"password": "pbkdf2_sha256$216000$leWVb2x2Y3wL$xgiz9vwhNoT1eu0+L20VKf+xhbnv7l/0h/jynJpld78=",
"last_login": null,
"is_superuser": false,
"username": "Tia-Clair_Toomey",
"username": "coach1",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2022-03-02T09:24:49.973Z",
"date_joined": "2022-03-23T22:09:55.285Z",
"email": "coach1@domain.com",
"coach": null,
"phone_number": "",
"country": "",
"city": "",
"street_address": "",
"role": "Athletes",
"role": "Coach",
"groups": [
1
2
],
"user_permissions": []
}
},
{
"model": "users.user",
"pk": 3,
"pk": 7,
"fields": {
"password": "pbkdf2_sha256$216000$CSxiFJQhjEUf$UMcCuCV/9jXQRALtPUtLpeTmJKbhYGvXrfMWSfs5nGw=",
"password": "pbkdf2_sha256$216000$1lJ8zIC8Gvzp$zJquv2euj3BmM3zEIwT8mgY+1Qs8jLmeEz0Ho3ACrho=",
"last_login": null,
"is_superuser": false,
"username": "Shane_Orr",
"username": "athlete1",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2022-03-02T09:25:06.583Z",
"coach": null,
"date_joined": "2022-03-23T22:11:36Z",
"email": "athlete1@domain.com",
"coach": 6,
"phone_number": "",
"country": "",
"city": "",
"street_address": "",
"role": "Coaches",
"role": "Athlete",
"groups": [
2
1
],
"user_permissions": []
}
},
{
"model": "users.user",
"pk": 4,
"pk": 8,
"fields": {
"password": "pbkdf2_sha256$216000$RBAIDnDJxplK$sfPpGuEVrNyXehX1r7sf7FeG690Lxl8kQQDG+U5XADw=",
"password": "pbkdf2_sha256$216000$SOPjFEULUbnw$DbWevm8Na1TtNnSxil68TuTMY/NZLeQFlJ77g4m6xW0=",
"last_login": null,
"is_superuser": false,
"username": "Mat_Fraser",
"username": "athlete2",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2022-03-02T12:49:44.588Z",
"coach": null,
"date_joined": "2022-03-23T22:13:10Z",
"email": "athlete2@domain.com",
"coach": 6,
"phone_number": "",
"country": "",
"city": "",
"street_address": "",
"role": "Athletes",
"role": "Athlete",
"groups": [
1
],
"user_permissions": []
}
},
{
"model": "comments.comment",
"pk": 1,
"fields": {
"owner": 7,
"workout": 4,
"content": "some comment",
"timestamp": "2022-03-23T22:16:35.217Z"
}
}
]
......@@ -8,10 +8,13 @@ pytestmark = pytest.mark.django_db
class TeamTestCase(TestCase):
@classmethod
def setUpTestData(cls):
Teams.objects.create(name = "test_team_1", coach = "test_coach_1")
Teams.objects.create(name = "test_team_1", coach = "test_coach_1", members="test_member_1" )
def test_name(self):
self.assertEqual(Teams.objects.get(id=1).name, "test_team_1")
def test_name(self):
def test_coach(self):
self.assertEqual(Teams.objects.get(id=1).coach, "test_coach_1")
def test_members(self):
self.assertEqual(Teams.objects.get(id=1).members, "test_member_1")
......@@ -13,10 +13,12 @@ class TeamListTestCase(TestCase):
def test_call_view_post(self):
form_data = {"name": "TeamListTestCase1",
"coach": "Coach1"}
"coach": "Coach1",
"members": "Member1"}
response = self.client.post(reverse("teams-list2"), form_data)
self.assertEqual(response.status_code, 201)
# print("hello")
# print(Teams.objects.get(name = "TeamListTestCase1"))
# print("hello")
self.assertTrue(Teams.objects.all().filter(coach="Coach1").exists())
\ No newline at end of file
self.assertTrue(Teams.objects.all().filter(coach="Coach1").exists())
self.assertTrue(Teams.objects.all().filter(members="Member1").exists())
\ No newline at end of file
......@@ -31,8 +31,6 @@ class User(AbstractUser):
street_address = models.TextField(max_length=50, blank=True)
role = models.CharField(max_length=50, blank=False, choices=Roles.choices, default='Athlete')
# TODO: add constraints to not allow other user roles (https://adamj.eu/tech/2020/01/22/djangos-field-choices-dont-constrain-your-data/)
def athlete_directory_path(instance, filename):
"""
......
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