-
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.
feature/TSS-2293-trs-dbt-migration-create-end-to-end-test-scripts (#430)
* wip * adding end to end test scripts * adding more tests * adding read me for end to end testing * adding better heading and styling * added completed end to end public registeration * adding login test
- Loading branch information
Showing
10 changed files
with
410 additions
and
1 deletion.
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
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
Empty file.
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,63 @@ | ||
import os | ||
import pytest | ||
from playwright.sync_api import sync_playwright | ||
|
||
BASE_URL = os.getenv("BASE_FRONTEND_TESTING_URL", "http://localhost:8002/") | ||
HEADLESS = os.getenv("TEST_HEADLESS", "false").lower() == "true" | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def session_data(): | ||
"""Return a dictionary to store session data.""" | ||
return { | ||
"cookies": None, | ||
"email": None, | ||
"password": None, | ||
} | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def playwright_instance(): | ||
"""Return a Playwright instance.""" | ||
with sync_playwright() as p: | ||
yield p | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def browser(playwright_instance): | ||
"""Return a browser instance.""" | ||
if HEADLESS: | ||
browser = playwright_instance.chromium.launch(headless=True) | ||
else: | ||
browser = playwright_instance.chromium.launch(slow_mo=100, headless=HEADLESS) | ||
yield browser | ||
browser.close() | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def context(browser, session_data): | ||
# Create a new browser context | ||
context = browser.new_context() | ||
context.set_default_timeout(0) | ||
|
||
# Initially, session_data["cookies"] will be None. | ||
# Check if "cookies" key exists and has a value; if not, it means it's the first test run. | ||
if session_data.get("cookies") is None: | ||
# Since it's the first run, let the browser context initiate and capture the cookies. | ||
session_data["cookies"] = context.cookies() | ||
else: | ||
# If it's not the first run, load the initially captured cookies into the context. | ||
context.add_cookies(session_data["cookies"]) | ||
|
||
yield context | ||
context.close() | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def page(context): | ||
# Create a new page in the provided context | ||
_page = context.new_page() | ||
_page.goto(BASE_URL, wait_until="domcontentloaded") | ||
# Wait for the page to load | ||
_page.wait_for_timeout(10000) | ||
yield _page |
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,4 @@ | ||
[pytest] | ||
testpaths = e2e | ||
python_files = test_*.py | ||
python_functions = test_* |
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,10 @@ | ||
faker==19.3.1 ; python_version >= "3.9" and python_version < "4.0" | ||
pytest-cov==2.10.1 ; python_version >= "3.8" and python_version < "4.0" | ||
pytest-django==4.5.2 ; python_version >= "3.8" and python_version < "4.0" | ||
pytest-forked==1.6.0 ; python_version >= "3.8" and python_version < "4.0" | ||
pytest-order==1.1.0 ; python_version >= "3.8" and python_version < "4.0" | ||
pytest-watch==4.2.0 ; python_version >= "3.8" and python_version < "4.0" | ||
pytest-xdist==2.1.0 ; python_version >= "3.8" and python_version < "4.0" | ||
pytest==8.0.1 ; python_version >= "3.8" and python_version < "4.0" | ||
pytest-dependency==0.6.0 ; python_version >= "3.8" and python_version < "4.0" | ||
playwright==1.41.2 ; python_version >= "3.8" and python_version < "4.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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import pytest | ||
|
||
from playwright.sync_api import expect | ||
|
||
from e2e.utils import get_base_url, retry, generate_test_name, generate_test_email, generate_test_password, generate_test_address, genetrate_test_postcode | ||
|
||
BASE_URL = get_base_url() | ||
|
||
@retry() | ||
def test_public_login(page): | ||
page.goto(BASE_URL) | ||
expect(page.get_by_role("heading", name="Trade Remedies Service: sign")).to_be_visible() | ||
page.get_by_role("button", name="Sign in").click() | ||
expect(page.get_by_role("button", name="Sign in")).to_be_visible() | ||
|
||
|
||
@retry() | ||
def test_public_register(page): | ||
page.goto(BASE_URL) | ||
expect(page.get_by_role("heading", name="Trade Remedies Service: sign")).to_be_visible() | ||
expect(page.get_by_role("link", name="Create an account")).to_be_visible() | ||
page.get_by_role("link", name="Create an account").click() | ||
page.get_by_role("heading", name="Has anyone else from your").click() | ||
page.get_by_label("No").check() | ||
page.get_by_role("button", name="Continue").click() | ||
expect(page.get_by_role("heading", name="Create an account")).to_be_visible() | ||
|
||
|
||
@retry() | ||
@pytest.mark.dependency(name="test_register_user_with_new_org", scope="session") | ||
def test_register_user_with_new_org(page, session_data): | ||
|
||
email = generate_test_email() | ||
name = generate_test_name() | ||
password = generate_test_password() | ||
address = generate_test_address() | ||
postcode = genetrate_test_postcode() | ||
|
||
page.goto(BASE_URL) | ||
expect(page.get_by_role("heading", name="Trade Remedies Service: sign")).to_be_visible() | ||
expect(page.get_by_role("link", name="Create an account")).to_be_visible() | ||
page.get_by_role("link", name="Create an account").click() | ||
page.get_by_role("heading", name="Has anyone else from your").click() | ||
page.get_by_label("No").check() | ||
page.get_by_role("button", name="Continue").click() | ||
page.get_by_label("Enter your name").click() | ||
page.get_by_label("Enter your name").fill(name) | ||
page.get_by_label("Your email address").click() | ||
page.get_by_label("Your email address").fill(email) | ||
page.get_by_label("I have read and understood").check() | ||
page.get_by_role("button", name="Continue").click() | ||
page.get_by_label("Enter Password").click() | ||
page.get_by_label("Enter Password").fill(password) | ||
page.get_by_role("button", name="Continue").click() | ||
expect(page.get_by_role("heading", name="Two-factor authentication")).to_be_visible() | ||
expect(page.get_by_text("How would you like to receive")).to_be_visible() | ||
page.locator('input.govuk-radios__input[id="email"]').click() | ||
page.get_by_role("button", name="Continue").click() | ||
expect(page.get_by_text("Is your organisation a UK")).to_be_visible() | ||
page.get_by_label("No").check() | ||
page.get_by_role("button", name="Continue").click() | ||
page.get_by_label("Name of your organisation").click() | ||
page.get_by_label("Name of your organisation").fill(f"{name} LTD") | ||
page.get_by_label("Street address").click() | ||
page.get_by_label("Street address").fill(address) | ||
page.get_by_label("Postcode or zip code").click() | ||
page.get_by_label("Postcode or zip code").fill(postcode) | ||
page.get_by_label("Country").select_option("GB") | ||
page.get_by_label("Organisation registration").click() | ||
page.get_by_label("Organisation registration").fill("12345678") | ||
page.get_by_role("button", name="Continue").click() | ||
expect(page.get_by_role("heading", name="Further details about your")).to_be_visible() | ||
expect(page.get_by_role("button", name="Create my account")).to_be_visible() | ||
page.get_by_role("button", name="Create my account").click() | ||
page.locator("#main-content").get_by_role("link", name="Sign in").click() | ||
expect(page.get_by_role("heading", name="Sign in to Trade Remedies")).to_be_visible() | ||
|
||
# store user name and password for later use in another test | ||
session_data["email"] = email | ||
session_data["password"] = password | ||
|
||
|
||
@retry() | ||
def test_login_with_invalid_credentials(page): | ||
page.goto(BASE_URL) | ||
page.get_by_role("button", name="Sign in").click() | ||
expect(page.get_by_role("heading", name="Sign in to Trade Remedies")).to_be_visible() | ||
page.get_by_label("Email address").click() | ||
page.get_by_label("Email address").fill("[email protected]") | ||
page.get_by_label("Password").click() | ||
page.get_by_label("Password").fill("123456789") | ||
page.get_by_role("button", name="Sign in").click() | ||
expect(page.get_by_label("There is a problem")).to_be_visible() | ||
|
||
|
||
@retry() | ||
@pytest.mark.dependency(depends=["test_register_user_with_new_org"], scope="session") | ||
def test_login_with_valid_credentials(page, session_data): | ||
email = session_data["email"] | ||
password = session_data["password"] | ||
page.goto(BASE_URL) | ||
page.get_by_role("button", name="Sign in").click() | ||
expect(page.get_by_role("heading", name="Sign in to Trade Remedies")).to_be_visible() | ||
|
||
page.get_by_label("Email address").click() | ||
page.get_by_label("Email address").fill(email) | ||
page.get_by_label("Password").click() | ||
page.get_by_label("Password").fill(password) | ||
page.get_by_role("button", name="Sign in").click() | ||
expect(page.get_by_text("Verify your email address", exact=True)).to_be_visible() |
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,116 @@ | ||
import os | ||
import random | ||
import string | ||
import inspect | ||
import re | ||
import time | ||
import pytest | ||
|
||
from functools import wraps | ||
|
||
def get_base_url(): | ||
return os.getenv("BASE_FRONTEND_TESTING_URL", "http://localhost:8002/") | ||
|
||
def clean_full_url(url): | ||
"""Clean a URL by removing multiple slashes and trailing slash.""" | ||
|
||
# Split the URL into protocol and the rest. | ||
if "://" in url: | ||
protocol, rest = url.split("://", 1) | ||
# Clean the 'rest' part of the URL by replacing multiple slashes with a single one. | ||
rest = re.sub(r"/+", "/", rest) | ||
# Remove trailing slash if present | ||
if rest.endswith('/'): | ||
rest = rest[:-1] | ||
# Concatenate the protocol and the cleaned part back together. | ||
cleaned_url = f"{protocol}://{rest}" | ||
else: | ||
# If there's no protocol, just clean the URL directly. | ||
cleaned_url = re.sub(r"/+", "/", url) | ||
# Remove trailing slash if present | ||
if cleaned_url.endswith('/'): | ||
cleaned_url = cleaned_url[:-1] | ||
return cleaned_url | ||
|
||
|
||
def retry(tries=3, delay=3, backoff=2, logger=None): | ||
"""Retry calling the decorated function using an exponential backoff.""" | ||
|
||
def deco_retry(f): | ||
@wraps(f) | ||
def f_retry(*args, **kwargs): | ||
mtries, mdelay = tries, delay | ||
# Get argument names of the function | ||
arg_names = inspect.getfullargspec(f).args | ||
|
||
# Run the function with retries | ||
while mtries > 1: | ||
try: | ||
return f(*args, **kwargs) | ||
except TimeoutError as e: | ||
msg = f"{e}, Retrying in {mdelay} seconds..." | ||
if logger: | ||
logger.warning(msg) | ||
else: | ||
print(msg) | ||
time.sleep(mdelay) | ||
mtries -= 1 | ||
mdelay *= backoff | ||
|
||
# Final attempt | ||
return f(*args, **kwargs) | ||
|
||
# If the function expects a fixture named 'request', assume it's a pytest test | ||
if "request" in inspect.getfullargspec(f).args: | ||
# Use pytest's request fixture to modify the function with retry logic | ||
@pytest.fixture | ||
def wrapper(request, *args, **kwargs): | ||
# Modify the test function by injecting the retry decorator | ||
return f_retry(*args, **kwargs) | ||
|
||
return wrapper | ||
else: | ||
return f_retry | ||
|
||
return deco_retry | ||
|
||
|
||
def generate_test_name(): | ||
"""Generate a random sample user name.""" | ||
prefix = "user_" | ||
suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) | ||
return prefix + suffix | ||
|
||
|
||
def generate_test_email(): | ||
"""Generate a random sample email address.""" | ||
prefix = "test_" | ||
suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) | ||
name = ".".join([prefix, suffix]) | ||
domain = "@gov.uk" | ||
return name + domain | ||
|
||
|
||
def generate_test_password(): | ||
"""Generate a random sample password with at least 8 characters and at most 12 characters.""" | ||
# password needs to be in the format | ||
# capital letter, lowercase letter, number, special character | ||
password = ''.join(random.choices(string.ascii_uppercase, k=1)) | ||
password += ''.join(random.choices(string.ascii_lowercase, k=1)) | ||
password += ''.join(random.choices(string.digits, k=1)) | ||
password += ''.join(random.choices(string.punctuation, k=1)) | ||
|
||
# We now add 8 more characters to reach a total of 12 | ||
password += ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=8)) | ||
|
||
return password | ||
|
||
|
||
def generate_test_address(): | ||
"""Generate a random address.""" | ||
return ''.join(random.choices(string.ascii_letters + string.digits, k=8)) | ||
|
||
|
||
def genetrate_test_postcode(): | ||
"""Generate a random postcode.""" | ||
return ''.join(random.choices(string.ascii_uppercase + string.digits, k=7)) |
Oops, something went wrong.