Skip to content

Commit

Permalink
Refactoring websrc fixture names
Browse files Browse the repository at this point in the history
  • Loading branch information
DiogenesAnalytics committed Mar 7, 2024
1 parent 8c99497 commit 8492237
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 57 deletions.
154 changes: 113 additions & 41 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import pytest
from flask import Flask
from flask import request
from flask import send_from_directory


Expand All @@ -23,6 +24,29 @@ def pytest_configure(config):
config.addinivalue_line("markers", "flask: custom marker for flask server tests.")


def create_temp_websrc_dir(src: Path, dst: Path, src_files: Tuple[str, ...]) -> Path:
"""Create and populate a temporary directory with static web source files."""
# create new destination subdir
sub_dir = dst / "web_src"
sub_dir.mkdir()

# copy each file or directory from the project directory to the temporary directory
for item_name in src_files:
# get the path to the source file or directory in the project directory
source_item_path = src / item_name

# check if directory
if source_item_path.is_dir():
# if the item is a directory, recursively copy it
shutil.copytree(source_item_path, sub_dir / item_name)

else:
# if the item is a file, copy it
shutil.copy(source_item_path, sub_dir)

return sub_dir


def build_flask_app(serve_directory: Path, port: int) -> Flask:
"""Assembles Flask app to serve static site."""
# instantiate app
Expand Down Expand Up @@ -60,9 +84,47 @@ def serve_scripts(path):
else:
return "JavaScript file not found\n", 404

@app.route("/submit", methods=["POST"])
def submit_form():
# access form data submitted by the client
form_data = request.form

# print the form data
print("Form Data:", form_data)

return "Form submitted successfully!\n"

return app


def run_threaded_flask_app(app: Flask) -> None:
"""Run a Flask app using threading."""
# launch Flask app for projecf dir in thread
thread = threading.Thread(target=app.run)
thread.daemon = True
thread.start()


def load_config_file(directory: Path) -> Dict[str, Any]:
"""Load the JSON config file at directory."""
# open the config file in the project dir
with open(directory / "config.json", "r", encoding="utf-8") as config:
# load the JSON data into dict
return json.load(config)


def update_form_backend_config(
config: Dict[str, Any], src_path: Path, port: int
) -> None:
"""Set the form backend url to testing server url."""
# update form backend
config["form_backend_url"] = f"http://localhost:{port}/submit"

# Writing dictionary to JSON file with pretty printing (2 spaces indentation)
with open(src_path / "config.json", "w") as json_file:
json.dump(config, json_file, indent=2)


@pytest.fixture(scope="session")
def sb_test_url() -> str:
"""Simply defines the test URL for seleniumbase fixture testing."""
Expand All @@ -80,56 +142,41 @@ def project_directory() -> Path:


@pytest.fixture(scope="session")
def project_web_app(project_directory: Path):
"""Create a Flask app for testing with the website source."""
return build_flask_app(project_directory, 5000)
def website_files() -> Tuple[str, ...]:
"""Declare the files necessary for serving the website."""
# define the files and directories to copy from the project directory
return ("index.html", "config.json", "styles", "scripts")


@pytest.fixture(scope="session")
def live_project_web_app_url(request, project_web_app: Flask):
"""Runs Flask app for project directory in a thread."""
# launch Flask app for projecf dir in thread
thread = threading.Thread(target=project_web_app.run)
thread.daemon = True
thread.start()
def session_tmp_dir(tmp_path_factory) -> Path:
"""Uses temporary path factory to create a session-scoped temp path."""
# create a temporary directory using tmp_path_factory
return tmp_path_factory.mktemp("session_temp_dir")

# get port
assert "PORT" in project_web_app.config, "PORT key not set"
port = project_web_app.config.get("PORT")

# get url
return f"http://localhost:{port}"
@pytest.fixture(scope="session")
def session_websrc_tmp_dir(
project_directory: Path, session_tmp_dir: Path, website_files: Tuple[str, ...]
) -> Generator[Path, None, None]:
"""Create a per-session copy of the website source code for editing."""
# create a temporary directory
temp_dir = create_temp_websrc_dir(project_directory, session_tmp_dir, website_files)

# provide the temporary directory path to the test function
yield temp_dir

@pytest.fixture(scope="session")
def website_files() -> Tuple[str, ...]:
"""Declare the files necessary for serving the website."""
# define the files and directories to copy from the project directory
return ("index.html", "config.json", "styles", "scripts")
# remove the temporary directory and its contents
shutil.rmtree(temp_dir)


@pytest.fixture(scope="function")
def temp_web_src(
def function_websrc_tmp_dir(
project_directory: Path, tmp_path: Path, website_files: Tuple[str, ...]
) -> Generator[Path, None, None]:
"""Create a copy of the website source code for editing."""
"""Create a per-function copy of the website source code for editing."""
# create a temporary directory
temp_dir = tmp_path / "web_src"
temp_dir.mkdir()

# copy each file or directory from the project directory to the temporary directory
for item_name in website_files:
# get the path to the source file or directory in the project directory
source_item_path = project_directory / item_name

# check if directory
if source_item_path.is_dir():
# if the item is a directory, recursively copy it
shutil.copytree(source_item_path, temp_dir / item_name)

else:
# if the item is a file, copy it
shutil.copy(source_item_path, temp_dir)
temp_dir = create_temp_websrc_dir(project_directory, tmp_path, website_files)

# provide the temporary directory path to the test function
yield temp_dir
Expand All @@ -141,7 +188,32 @@ def temp_web_src(
@pytest.fixture(scope="session")
def default_site_config(project_directory: Path) -> Dict[str, Any]:
"""Load the default config.json file."""
# open the config file in the project dir
with open(project_directory / "config.json", "r", encoding="utf-8") as config:
# load the JSON data into dict
return json.load(config)
return load_config_file(project_directory)


@pytest.fixture(scope="session")
def session_web_app(
default_site_config: Dict[str, Any], session_websrc_tmp_dir: Path
) -> Flask:
"""Create a session-scoped Flask app for testing with the website source."""
# set port
port = 5000

# now update config.json with new backend url
update_form_backend_config(default_site_config, session_websrc_tmp_dir, port)

# create app
return build_flask_app(session_websrc_tmp_dir, port)


@pytest.fixture(scope="session")
def live_session_web_app_url(session_web_app: Flask) -> str:
"""Runs session-scoped Flask app in a thread."""
# start threaded app
run_threaded_flask_app(session_web_app)

# get port
port = session_web_app.config.get("PORT")

# get url
return f"http://localhost:{port}"
61 changes: 51 additions & 10 deletions tests/test_fixtures.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
"""Test the fixtures used in the tests."""

import json
from pathlib import Path
from typing import Tuple

import pytest
from flask import Flask
from seleniumbase import BaseCase

from tests.conftest import load_config_file


def check_files_subset(source_dir: Path, webfiles: Tuple[str, ...]) -> bool:
"""Check if subset of files is found in another directory."""
Expand All @@ -26,9 +30,11 @@ def test_websrc_in_project_dir(


@pytest.mark.fixture
def test_websrc_in_temp_dir(temp_web_src: Path, website_files: Tuple[str, ...]) -> None:
def test_websrc_in_temp_dir(
session_websrc_tmp_dir: Path, website_files: Tuple[str, ...]
) -> None:
"""Simply confirm that the website files are in the temp web source dir."""
assert check_files_subset(temp_web_src, website_files)
assert check_files_subset(session_websrc_tmp_dir, website_files)


@pytest.mark.fixture
Expand All @@ -53,35 +59,70 @@ def test_hello_world_sb(sb: BaseCase, sb_test_url: str) -> None:

@pytest.mark.flask
@pytest.mark.fixture
def test_index_route(project_web_app):
def test_index_route(session_web_app: Flask) -> None:
"""Test the index route."""
client = project_web_app.test_client()
client = session_web_app.test_client()
response = client.get("/")
assert response.status_code == 200


@pytest.mark.flask
@pytest.mark.fixture
def test_other_root_files_route(project_web_app):
def test_other_root_files_route(session_web_app: Flask) -> None:
"""Test the route for serving other root files."""
client = project_web_app.test_client()
client = session_web_app.test_client()
response = client.get("/config.json")
assert response.status_code == 200


@pytest.mark.flask
@pytest.mark.fixture
def test_serve_styles_route(project_web_app):
def test_serve_styles_route(session_web_app: Flask) -> None:
"""Test the route for serving CSS files."""
client = project_web_app.test_client()
client = session_web_app.test_client()
response = client.get("/styles/form.css")
assert response.status_code == 200


@pytest.mark.flask
@pytest.mark.fixture
def test_serve_scripts_route(project_web_app):
def test_serve_scripts_route(session_web_app: Flask) -> None:
"""Test the route for serving JavaScript files."""
client = project_web_app.test_client()
client = session_web_app.test_client()
response = client.get("/scripts/form.js")
assert response.status_code == 200


@pytest.mark.flask
@pytest.mark.fixture
def test_port_in_app_config(session_web_app: Flask) -> None:
"""Confirm port has been set in Flask app config."""
assert "PORT" in session_web_app.config, "PORT key not set"


@pytest.mark.flask
@pytest.mark.fixture
def test_session_config_form_backend_updated(
session_websrc_tmp_dir: Path, session_web_app: Flask
) -> None:
"""Make sure config file has been updated with url."""
# load config file
config = load_config_file(session_websrc_tmp_dir)

# get config
client = session_web_app.test_client()
response = client.get("/config.json")

# verify the response status code
assert response.status_code == 200

# convert the response content to JSON
json_data = json.loads(response.data)

# check that key is in config
key = "form_backend_url"
assert key in json_data
assert key in config

# check configs match
assert config[key] == json_data[key]
34 changes: 28 additions & 6 deletions tests/test_website.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ def test_config_schema(default_site_config: Dict[str, Any]) -> None:


@pytest.mark.website
def test_normal_display(sb: BaseCase, live_project_web_app_url: str) -> None:
def test_normal_display(sb: BaseCase, live_session_web_app_url: str) -> None:
"""Simply tests that the website is displaying normally."""
# open with browser
sb.open(live_project_web_app_url)
sb.open(live_session_web_app_url)

# verify that the container element is visible
sb.assert_element_visible(".container")
Expand All @@ -36,11 +36,11 @@ def test_normal_display(sb: BaseCase, live_project_web_app_url: str) -> None:

@pytest.mark.website
def test_email_in_instructions(
sb: BaseCase, live_project_web_app_url: str, default_site_config: Dict[str, Any]
sb: BaseCase, live_session_web_app_url: str, default_site_config: Dict[str, Any]
) -> None:
"""Test that email is dynamically added to instructions."""
# temp website src
sb.open(live_project_web_app_url)
sb.open(live_session_web_app_url)

# get instructions text
instruct_text = sb.get_text("#instructions")
Expand All @@ -51,14 +51,36 @@ def test_email_in_instructions(

@pytest.mark.website
def test_custom_title_works(
sb: BaseCase, live_project_web_app_url: str, default_site_config: Dict[str, Any]
sb: BaseCase, live_session_web_app_url: str, default_site_config: Dict[str, Any]
) -> None:
"""Test that title is dynamically updated from config.json."""
# temp website src
sb.open(live_project_web_app_url)
sb.open(live_session_web_app_url)

# get the title of the webpage
title = sb.get_title()

# check email in text
assert default_site_config["title"] == title


@pytest.mark.website
def test_form_backend_updated(sb: BaseCase, live_session_web_app_url: str) -> None:
"""Check that the form backend url has been updated correctly."""
# open the webpage
sb.open(live_session_web_app_url)

# find the form element
form_element = sb.get_element("form")

# make sure it exists
assert form_element is not None

# get the value of the "action" attribute of the form element
form_target = form_element.get_attribute("action")

# make sure it exists
assert form_target is not None

# now check that it is the right url
assert form_target == live_session_web_app_url + "/submit"

0 comments on commit 8492237

Please sign in to comment.