Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
9051890
chore(tests): Add tests to workflow
Joe-Heffer-Shef Mar 13, 2025
d6570bd
feat(tests): Add a test suite
Joe-Heffer-Shef Mar 14, 2025
ccddc57
chore(tests): Add survey tests placeholder
Joe-Heffer-Shef Mar 14, 2025
97c9403
Merge remote-tracking branch 'refs/remotes/origin/dev' into test/backend
Joe-Heffer-Shef Mar 14, 2025
d449a7c
chore(tests): Start adding survey tests
Joe-Heffer-Shef Mar 14, 2025
4539cea
???
Joe-Heffer-Shef Mar 14, 2025
ca7ace6
Tidy up the test suite
Joe-Heffer-Shef Mar 19, 2025
df88ea7
Fix survey service syntax
Joe-Heffer-Shef Mar 19, 2025
67035d2
Fix survey service logic
Joe-Heffer-Shef Mar 19, 2025
5126cd4
Start writing survey tests
Joe-Heffer-Shef Mar 19, 2025
e7ed812
Test all survey service methods
Joe-Heffer-Shef Mar 19, 2025
1660977
Test all survey service methods
Joe-Heffer-Shef Mar 19, 2025
a6efc2b
Add unit tests for survey views
Joe-Heffer-Shef Mar 19, 2025
92eabaa
Disable invite test
Joe-Heffer-Shef Mar 20, 2025
78e4908
Merge remote-tracking branch 'refs/remotes/origin/dev' into test/backend
Joe-Heffer-Shef Mar 26, 2025
b338d11
Add login option
Joe-Heffer-Shef Mar 26, 2025
cf66062
Fix: Correct HTTP status code
Joe-Heffer-Shef Mar 26, 2025
9bdeb41
feat: Enable parallel unit tests
Joe-Heffer-Shef Mar 26, 2025
b6129f7
chore(tests): Remove pytest
Joe-Heffer-Shef Mar 26, 2025
2ad62b2
chore(tests): Fix syntax
Joe-Heffer-Shef Mar 26, 2025
b0a3310
chore(tests): Increase test verbosity
Joe-Heffer-Shef Mar 26, 2025
5d7a473
Refactor test suite
Joe-Heffer-Shef Mar 27, 2025
07f856f
Refactor test suite
Joe-Heffer-Shef Mar 27, 2025
fa5fc10
Implement invitation factory
Joe-Heffer-Shef Mar 27, 2025
2e569b1
Use survey factory
Joe-Heffer-Shef Mar 27, 2025
3ec277b
Format Python code
Joe-Heffer-Shef Mar 27, 2025
3d84cab
Refactor test suite further
Joe-Heffer-Shef Mar 27, 2025
38314db
-
Joe-Heffer-Shef Mar 27, 2025
55a6695
Add parent class setup
Joe-Heffer-Shef Mar 27, 2025
0c1721b
Use our post() method
Joe-Heffer-Shef Mar 27, 2025
904ad3d
Improve project views test code quality
Joe-Heffer-Shef Mar 27, 2025
b210d96
Add coverage reporting
Joe-Heffer-Shef Mar 27, 2025
a610643
Merge pull request #185 from RSE-Sheffield/test/backend-coverage
Joe-Heffer-Shef Mar 27, 2025
c7cf3e2
Coverage report in Markdown
Joe-Heffer-Shef Mar 27, 2025
3c28728
Add pretty header
Joe-Heffer-Shef Mar 27, 2025
1f9e860
Add pretty header
Joe-Heffer-Shef Mar 27, 2025
ba4c7d5
Add coverage configuration file
Joe-Heffer-Shef Mar 27, 2025
abc9eb6
Don't measure coverage on test files
Joe-Heffer-Shef Mar 27, 2025
9504e9b
Show least-covered files first
Joe-Heffer-Shef Mar 27, 2025
bbedd6f
Add test for mock responses
Joe-Heffer-Shef Mar 28, 2025
cacc96a
Merge branch 'dev' into test/backend
Joe-Heffer-Shef Apr 2, 2025
143f057
Add `npm run build` to testing workflow
Joe-Heffer-Shef Apr 2, 2025
d36ac21
Add `npm install` to testing workflow
Joe-Heffer-Shef Apr 2, 2025
8451bf8
Use setup-node action
Joe-Heffer-Shef Apr 2, 2025
095dbee
Use npm ci
Joe-Heffer-Shef Apr 2, 2025
573da49
Add link to other readme
Joe-Heffer-Shef Apr 2, 2025
b122beb
Merge remote-tracking branch 'refs/remotes/origin/dev' into test/backend
Joe-Heffer-Shef Apr 2, 2025
8c12e8e
Merge remote-tracking branch 'refs/remotes/origin/dev' into test/backend
Joe-Heffer-Shef Apr 15, 2025
a772e60
Attempt to fix unit tests
Joe-Heffer-Shef Apr 15, 2025
f955cec
Attempt to fix unit tests
Joe-Heffer-Shef Apr 15, 2025
2d832eb
Delete assets/sort-survey-configurator/package-lock.json
Joe-Heffer-Shef Apr 15, 2025
01fed98
Fix unit tests
Joe-Heffer-Shef Apr 15, 2025
e338438
Use Node version 20
Joe-Heffer-Shef Apr 15, 2025
0f59783
Fix JavaScript package working dir
Joe-Heffer-Shef Apr 15, 2025
26d225e
Fix changed ui path
Joe-Heffer-Shef Apr 15, 2025
4273301
Merge branch 'dev' into test/backend
Joe-Heffer-Shef Apr 22, 2025
4e091db
chore: Only use debug toolbar in dev
Joe-Heffer-Shef Apr 23, 2025
2d98aa5
Merge branch 'refs/heads/dev' into test/backend
Joe-Heffer-Shef Apr 23, 2025
1ec0623
chore: Only use debug toolbar in dev
Joe-Heffer-Shef Apr 23, 2025
000998d
Add invitation view tests
Joe-Heffer-Shef Apr 23, 2025
03d7599
Merge pull request #241 from RSE-Sheffield/test/invite
Joe-Heffer-Shef Apr 23, 2025
29f82a8
Merge remote-tracking branch 'refs/remotes/origin/dev' into test/backend
Joe-Heffer-Shef Apr 29, 2025
bdaee10
Fix linting errors
Joe-Heffer-Shef Apr 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Coverage report configuration
# https://coverage.readthedocs.io/en/7.7.1/config.html

[run]
source = .
omit =
__init__.py
*/migrations/*
SORT/*
*/admin.py
*/tests/*

[report]
format = markdown
skip_empty = true
sort = Cover
1 change: 1 addition & 0 deletions .github/workflows/check-gunicorn-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
- name: Setup Python
uses: actions/[email protected]
with:
# This should match the version used in production
python-version: "3.12"
cache: "pip"
- name: Install dependencies
Expand Down
File renamed without changes.
29 changes: 23 additions & 6 deletions .github/workflows/django-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ on:
pull_request:
branches: [ "main", "dev" ]
jobs:
lint:
django-test:
name: Run Django automated checks and tests
runs-on: ubuntu-24.04
steps:
- name: Checkout
Expand All @@ -15,12 +16,19 @@ jobs:
with:
# This should match the version used in production
python-version: "3.12"
cache: "pip"
- name: Install dependencies
run: pip install --requirement requirements.txt --requirement requirements-dev.txt
- name: Setup Node.js
uses: actions/setup-node@v4
with:
# This should match the version used in production
node-version: 'v20.x'
cache: 'npm'
- name: Install JavaScript package
run: |
# Update pip
python -m pip install --upgrade pip
# https://pip.pypa.io/en/stable/cli/pip_install/
pip install --requirement requirements.txt
npm ci
npm run build
# https://docs.djangoproject.com/en/5.1/ref/django-admin/#check
- name: Run Django system checks
run: |
Expand All @@ -30,4 +38,13 @@ jobs:
- name: Run Django test suites
run: |
export DJANGO_SECRET_KEY="$(python -c "import secrets; print(secrets.token_urlsafe())")"
python manage.py test
# Iterate over Django apps with test directories
for test_dir in **/tests
do
echo "Testing $test_dir"
# https://docs.djangoproject.com/en/5.1/topics/testing/advanced/#integration-with-coverage-py
coverage run --source="home,survey" manage.py test "$test_dir" --verbosity=2 --parallel=auto
done
# Generate coverage report
echo "## Coverage report" >> $GITHUB_STEP_SUMMARY
coverage report --format="markdown" >> $GITHUB_STEP_SUMMARY
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
.coverage

# Django stuff:
*.log
local_settings.py
db.sqlite3
*.sqlite3

# Sphinx documentation

Expand Down
25 changes: 22 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ Please use the [Kanban board](https://github.com/orgs/RSE-Sheffield/projects/19)

## Proposing changes

1. [Raise an issue](https://github.com/RSE-Sheffield/SORT/issues/new?template=Blank+issue) clearly describing the problem or user requirements;
2. [Create a branch](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/creating-a-branch-for-an-issue) that is associated with that issue. It can be helpful to prefix the branch name to match the type of changes e.g. `feat/123-my-feature` for features or `docs/my-guide` for documentation, etc. See [Semantic branch names](https://damiandabrowski.medium.com/semantic-branch-names-and-commit-messages-3ac38a6fcbb6).
1. [Raise an issue](https://github.com/RSE-Sheffield/SORT/issues/new?template=Blank+issue) clearly describing the
problem or user requirements;
2. [Create a branch](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/creating-a-branch-for-an-issue)
that is associated with that issue. It can be helpful to prefix the branch name to match the type of changes
e.g. `feat/123-my-feature` for features or `docs/my-guide` for documentation, etc.
See [Semantic branch names](https://damiandabrowski.medium.com/semantic-branch-names-and-commit-messages-3ac38a6fcbb6).
3. In that branch, make changes that aim to resolve that issue;
4. Create a [draft pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests) (PR) while the changes are being designed;
4. Create
a [draft pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests) (
PR) while the changes are being designed;
5. When ready, mark the PR "Ready for review" and request for reviewers to look at the proposed changes;

## Environments
Expand All @@ -25,6 +31,15 @@ There are two main environments:
- Development (the `dev` branch and the `sort-web-dev` virtual machine)
- Production (the `main` branch and the `sort-web-app` virtual machine)

### Local development environment

```bash
python -m venv .venv
source .venv/bin/activate
pip install --editable .
pip install -r requirements-dev.txt
```

## Change process

Any proposed changes should be proposed in pull requests that would be merged into the `dev` branch.
Expand Down Expand Up @@ -63,3 +78,7 @@ gitGraph
# Code of Conduct

We expect all contributors to follow the SORT [Code of Conduct](CODE_OF_CONDUCT.md).

# Testing

Please read the [testing documentation](docs/testing.md).
34 changes: 25 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,36 @@ Please read [`docs/deployment.md`](docs/deployment.md).


# Vite integration
The SORT app uses some javascript components such as the survey configurator and the survey response form. This is
implemented using the svelte framework and vite is used as the bundler. Vite also provides a live server for
development which includes HMR (hot module reloading).
The SORT app uses some JavaScript components such as the survey configurator and the survey response form. This is
implemented using the svelte framework and vite is used as the bundler. [Vite](https://vite.dev/) also provides a live server for development which includes HMR (hot module reloading).

In order to integrate this into the html template, a tag library is created at `/home/templatetags/vite_integration.py`.
- The `vite_client` template tag is used to include Vite's HMR javascript code.
- The `vite_asset` tag is used to include asset files (e.g. typescript files) in the template.
- In debug mode, this creates a link directly to the vite dev server normally located at `http://localhost:5173`
- In production mode, it links to assets within the `/static/` url path.
In order to integrate this into the HTML template, a [custom template tag](https://docs.djangoproject.com/en/5.1/howto/custom-template-tags/) library is created at `/home/templatetags/vite_integration.py`.
- The `vite_client` template tag is used to include Vite's HMR JavaScript code.
- The `vite_asset` tag is used to include asset files (e.g. TypeScript files) in the template.
- In debug mode, this creates a link directly to the Vite dev server normally located at `http://localhost:5173`
- In production mode, the link changes to the location of the file in the `/static/` folder

For more information, please [read the README](ui-components/README.md).

## Installation

Install the JavaScript package using [`npm install`](https://docs.npmjs.com/cli/v8/commands/npm-install/)

```bash
npm install
```

## Usage

The development mode will run the test page at http://localhost:5173

```bash
npm run dev
```

## Before deployment

The script files within `/ui_components` must be built into the static folder before deployment. This
The script files within `./ui_components/` must be built into the static folder before deployment. This
can be done by running:

```bash
Expand Down
18 changes: 9 additions & 9 deletions SORT/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def cast_to_boolean(obj: Any) -> bool:


# Load environment variables from .env file
load_dotenv(os.getenv("DJANGO_ENV_PATH"))
load_dotenv(dotenv_path=os.getenv("DJANGO_ENV_PATH", ".env"))

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Expand All @@ -53,25 +53,28 @@ def cast_to_boolean(obj: Any) -> bool:
ALLOWED_HOSTS = os.getenv("DJANGO_ALLOWED_HOSTS", "sort-web-app.shef.ac.uk").split()

# Application definition

INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# Plugins
"django_bootstrap5",
"django_extensions",
"debug_toolbar",
"qr_code",
"crispy_forms",
"crispy_bootstrap5",
# apps created by FA:
# SORT apps
"home",
"survey",
]

if DEBUG:
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html
INSTALLED_APPS.append("debug_toolbar")

MIDDLEWARE = [
# Implement security in the web server, not in Django.
# https://docs.djangoproject.com/en/5.1/ref/middleware/#module-django.middleware.security
Expand All @@ -82,8 +85,9 @@ def cast_to_boolean(obj: Any) -> bool:
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"debug_toolbar.middleware.DebugToolbarMiddleware",
]
if DEBUG:
MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware")

ROOT_URLCONF = "SORT.urls"

Expand Down Expand Up @@ -207,10 +211,6 @@ def cast_to_boolean(obj: Any) -> bool:
)
VITE_MANIFEST_FILE_PATH = os.path.join(VITE_STATIC_DIR, "manifest.json")

# FA: for production:

# EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"

X_FRAME_OPTIONS = "SAMEORIGIN"

# File uploading
Expand Down
Empty file added SORT/test/__init__.py
Empty file.
4 changes: 4 additions & 0 deletions SORT/test/model_factory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Model factories

Create dummy objects and fixtures for testing using [Factory Boy](https://factoryboy.readthedocs.io/en/stable/) which [supports the Django ORM](https://factoryboy.readthedocs.io/en/stable/orms.html#module-factory.django).

8 changes: 8 additions & 0 deletions SORT/test/model_factory/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from .organisation import OrganisationFactory
from .project import ProjectFactory
from .survey import SurveyFactory
from .user import UserFactory, SuperUserFactory
from .invitation import InvitationFactory

__all__ = ["InvitationFactory", "UserFactory", "SuperUserFactory", "SurveyFactory", "OrganisationFactory",
"ProjectFactory"]
11 changes: 11 additions & 0 deletions SORT/test/model_factory/invitation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import factory.django

from survey.models import Invitation
from .survey import SurveyFactory


class InvitationFactory(factory.django.DjangoModelFactory):
class Meta:
model = Invitation

survey = factory.SubFactory(SurveyFactory)
19 changes: 19 additions & 0 deletions SORT/test/model_factory/organisation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import factory.django

from home.models import Organisation
from .organisation_membership import OrganisationMembershipFactory

from home.constants import ROLE_ADMIN


class OrganisationFactory(factory.django.DjangoModelFactory):
class Meta:
model = Organisation
django_get_or_create = ("name",)

name = factory.Sequence(lambda n: f"Organisation {n}")
description = factory.Sequence(lambda n: f"Organisation description {n}")
# Create an administrator user
# https://factoryboy.readthedocs.io/en/stable/recipes.html#reverse-dependencies-reverse-foreignkey
members = factory.RelatedFactory(OrganisationMembershipFactory, factory_related_name="organisation",
role=ROLE_ADMIN)
11 changes: 11 additions & 0 deletions SORT/test/model_factory/organisation_membership.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import factory.django

from home.models import OrganisationMembership
from .user import UserFactory


class OrganisationMembershipFactory(factory.django.DjangoModelFactory):
class Meta:
model = OrganisationMembership

user = factory.SubFactory(UserFactory, first_name="Admin User")
13 changes: 13 additions & 0 deletions SORT/test/model_factory/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import factory.django

from .organisation import OrganisationFactory
from home.models import Project


class ProjectFactory(factory.django.DjangoModelFactory):
class Meta:
model = Project

name = factory.Sequence(lambda n: f"Project {n}")
description = factory.Sequence(lambda n: f"Project description {n}")
organisation = factory.SubFactory(OrganisationFactory)
14 changes: 14 additions & 0 deletions SORT/test/model_factory/survey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import factory.django

from SORT.test.model_factory import ProjectFactory
from survey.models import Survey


class SurveyFactory(factory.django.DjangoModelFactory):
class Meta:
model = Survey
django_get_or_create = ("name", "description")

name = factory.Sequence(lambda n: f"Survey {n}")
description = factory.Sequence(lambda n: f"Survey description {n}")
project = factory.SubFactory(ProjectFactory)
4 changes: 4 additions & 0 deletions SORT/test/model_factory/user/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .user import UserFactory
from .superuser import SuperUserFactory

__all__ = ['UserFactory', 'SuperUserFactory']
3 changes: 3 additions & 0 deletions SORT/test/model_factory/user/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import secrets

PASSWORD = secrets.token_urlsafe()
8 changes: 8 additions & 0 deletions SORT/test/model_factory/user/superuser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import factory
from .user import UserFactory


class SuperUserFactory(UserFactory):
is_superuser = True
first_name = factory.Sequence(lambda n: f"Superuser{n}")
email = factory.Sequence(lambda n: f"superuser{n}@sort.com")
14 changes: 14 additions & 0 deletions SORT/test/model_factory/user/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import django.contrib.auth
import factory
from .constants import PASSWORD


class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = django.contrib.auth.get_user_model()
django_get_or_create = ("email",)

email = factory.Sequence(lambda n: f"user{n}@sort.com")
first_name = factory.Sequence(lambda n: f"User{n}")
last_name = factory.Sequence(lambda n: f"Lastname{n}")
password = factory.PostGenerationMethodCall("set_password", PASSWORD)
4 changes: 4 additions & 0 deletions SORT/test/test_case/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .service import ServiceTestCase
from .view import ViewTestCase

__all__ = ["ViewTestCase", "ServiceTestCase"]
13 changes: 13 additions & 0 deletions SORT/test/test_case/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import django.test

from ..model_factory import UserFactory, SuperUserFactory


class SORTTestCase(django.test.TestCase):
"""
A test case for testing the SORT web application.
"""

def setUp(self):
self.user = UserFactory()
self.superuser = SuperUserFactory()
18 changes: 18 additions & 0 deletions SORT/test/test_case/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
A unit testing class for testing SORT application services.
"""

from typing import Optional
from home.services.base import BasePermissionService

from .base import SORTTestCase


class ServiceTestCase(SORTTestCase):
"""
A test case for testing an application service layer.
"""

def setUp(self):
super().setUp()
self.service: Optional[BasePermissionService] = None
Loading
Loading