generated from opensafely-core/repo-template
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into 147-radio-buttons
- Loading branch information
Showing
11 changed files
with
288 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# 1. Cypress for end-to-end testing | ||
|
||
Date: 2023-07-14 | ||
|
||
## Status | ||
|
||
Accepted | ||
|
||
## Context | ||
|
||
We need to test the functionality of the application and manual testing was becoming tedious because of the need to accept/reject every output. | ||
|
||
## Decision | ||
|
||
Use Cypress for end-to-end testing, both as an aid to development (it can walk through the functionality in a browser window faster than a person and provide continuous feedback) and as part of our CI test suite to automatically detect problems. | ||
|
||
## Consequences | ||
|
||
In other areas we've used Playwright, which also has support for Python. However, we needed something that would help us in the short-term, and Cypress was faster to get started with. We've not evaluated Cypress for longer term use. | ||
|
||
With any kind of end-to-end testing there's a risk that the tests will be slow and that the time to run the test suite will significantly increase. We've put the Cypress tests into a separate GitHub Action step, called with a different just command, so that we can monitor the time it takes to run. At the time of writing, the Cypress tests take around 1 minute to run, compared to two minutes for the unit tests. | ||
|
||
There's also a risk that having tests coupled to the UI will make development slower and make it harder to change things. To avoid this, we will follow the [Cypress Best Practices](https://docs.cypress.io/guides/references/best-practices) when designing our tests. This includes using data attributes, e.g `data-sacro-el`, to avoid coupling specific UI characteristics to test behaviour. Following the best practices should also help us keep the tests fast and reliable. | ||
|
||
We believe that the advantages of having the Cypress tests there to support development by providing fast feedback, outweigh the cost of having to maintain them and the slowdown in the CI pipeline. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import functools | ||
|
||
from django.conf import settings | ||
|
||
|
||
class IncorrectVersionError(Exception): | ||
def __init__(self, *args, used, supported, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.used = used | ||
self.supported = supported | ||
|
||
def __str__(self): | ||
return f"Unsupported ACRO output. This viewer supports ACRO version {self.supported}, but your results were generated with version {self.used}." | ||
|
||
|
||
class UnsupportedVersionFormatError(Exception): | ||
def __init__(self, *args, version, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.version = version | ||
|
||
|
||
@functools.total_ordering | ||
class Version: | ||
"""Utility class to parse and compare version strings""" | ||
|
||
def __init__(self, version: str) -> None: | ||
try: | ||
major, minor, *_ = version.split(".") | ||
|
||
# check major and minor are valid numbers | ||
int(major) | ||
int(minor) | ||
|
||
self.major = major | ||
self.minor = minor | ||
except ValueError: | ||
msg = f"Expected version to be in format 1.2.3, got {version}" | ||
raise UnsupportedVersionFormatError(msg, version=version) | ||
|
||
self.original = version | ||
|
||
def __eq__(self, other: "Version") -> bool: | ||
return self.major == other.major and self.minor == other.minor | ||
|
||
def __gt__(self, other: "Version") -> bool: | ||
if self.major > other.major: | ||
return True | ||
|
||
if self.major == other.major and self.minor > other.minor: | ||
return True | ||
|
||
return False | ||
|
||
def __repr__(self): | ||
return f"Version: {self.original}" | ||
|
||
def __str__(self): | ||
return self.original | ||
|
||
|
||
def check_version(version: str) -> None: | ||
""" | ||
Check the given version against the supported version in settings | ||
We don't care about bugfix versions so the Version class ignores them. | ||
""" | ||
supported = Version(settings.ACRO_SUPPORTED_VERSION) | ||
used = Version(version) | ||
|
||
if used < supported: | ||
raise IncorrectVersionError(used=used, supported=supported) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import shutil | ||
from pathlib import Path | ||
|
||
import pytest | ||
|
||
from sacro import views | ||
|
||
|
||
@pytest.fixture | ||
def TEST_PATH(): | ||
return Path("outputs/results.json") | ||
|
||
|
||
@pytest.fixture | ||
def test_outputs(tmp_path, TEST_PATH): | ||
shutil.copytree(TEST_PATH.parent, tmp_path, dirs_exist_ok=True) | ||
return views.Outputs(tmp_path / TEST_PATH.name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import json | ||
import shutil | ||
|
||
from django.test import Client | ||
|
||
|
||
def test_error_handling_middleware(client, tmp_path, TEST_PATH): | ||
shutil.copytree(TEST_PATH.parent, tmp_path, dirs_exist_ok=True) | ||
path = tmp_path / TEST_PATH.name | ||
|
||
# change the version number | ||
data = json.load(path.open()) | ||
data["version"] = "0.3.0" | ||
json.dump(data, path.open("w")) | ||
|
||
response = Client().get(f"/?path={path}") | ||
assert response.status_code == 500 | ||
assert ( | ||
"Unsupported ACRO output. This viewer supports ACRO version 0.4.x, but your results were generated with version 0.3.0." | ||
in response.rendered_content | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import pytest | ||
from django.test import override_settings | ||
|
||
from sacro.versioning import ( | ||
IncorrectVersionError, | ||
UnsupportedVersionFormatError, | ||
Version, | ||
check_version, | ||
) | ||
|
||
|
||
@override_settings(ACRO_SUPPORTED_VERSION="0.4.0") | ||
def test_check_version(monkeypatch): | ||
assert check_version("0.4.0") is None | ||
assert check_version("0.4.2") is None | ||
|
||
with pytest.raises(IncorrectVersionError): | ||
check_version("0.3.0") | ||
|
||
|
||
@pytest.mark.parametrize("version", ["test", "v0.4.0"]) | ||
def test_version_init_with_unexpected_format(version): | ||
with pytest.raises(UnsupportedVersionFormatError): | ||
Version(version) | ||
|
||
|
||
@pytest.mark.parametrize("version", ["0.4.0", "0.4"]) | ||
def test_version_init_success(version): | ||
Version(version) | ||
|
||
|
||
def test_version_rich_comparison_eq(): | ||
assert Version("0.4.0") == Version("0.4.0") | ||
|
||
# check bugfix numbers are ignored | ||
assert Version("0.4.0") == Version("0.4.2") | ||
|
||
|
||
def test_version_rich_comparison_ge(): | ||
assert Version("0.4.0") >= Version("0.3.0") | ||
assert Version("0.4.0") >= Version("0.4.0") | ||
assert Version("1.0.0") >= Version("0.3.0") | ||
|
||
|
||
def test_version_rich_comparison_gt(): | ||
assert Version("0.4.0") > Version("0.3.0") | ||
assert Version("1.0.0") > Version("0.3.0") | ||
|
||
|
||
def test_version_rich_comparison_le(): | ||
assert Version("0.3.0") <= Version("0.4.0") | ||
assert Version("0.4.0") <= Version("0.4.0") | ||
assert Version("0.3.0") <= Version("1.0.0") | ||
|
||
|
||
def test_version_rich_comparison_lt(): | ||
assert Version("0.3.0") < Version("0.4.0") | ||
assert Version("0.3.0") < Version("1.0.0") | ||
|
||
|
||
def test_version_rich_comparison_ne(): | ||
assert Version("1.4.0") != Version("0.4.0") | ||
assert Version("0.3.0") != Version("0.4.0") | ||
|
||
# check bugfix numbers are ignored | ||
assert Version("0.3.2") != Version("0.4.2") | ||
|
||
|
||
def test_version_repr(): | ||
assert repr(Version("0.7.0")) == "Version: 0.7.0" | ||
|
||
|
||
def test_version_str(): | ||
assert str(Version("0.7.0")) == "0.7.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters