Skip to content

Commit

Permalink
Working locally as far as document uploading.
Browse files Browse the repository at this point in the history
  • Loading branch information
brunns committed Jun 3, 2024
1 parent 2180a81 commit 0205488
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 70 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
cp .env.integration .env
echo AZURE_OPENAI_ENDPOINT=${{ secrets.AZURE_OPENAI_ENDPOINT }} >> .env
echo AZURE_OPENAI_API_KEY=${{ secrets.AZURE_OPENAI_API_KEY }} >> .env
docker compose up -d --wait elasticsearch core-api worker db django-app
docker compose up -d --wait elasticsearch db worker minio core-api django-app
- name: Wait 60s for services to be ready
Expand All @@ -64,8 +64,8 @@ jobs:
- name: Test integration with pytest
run: |
poetry install --with dev --without ai,api,worker,docs
python -m pytest tests
poetry install --only dev
poetry run pytest tests/
- name: notify slack failure
id: slack-failure
Expand Down
14 changes: 7 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ test-django:
docker compose run django-app venv/bin/pytest tests/ --ds redbox_app.settings -v --cov=redbox_app.redbox_core --cov-fail-under 80 -o log_cli=true

test-integration: stop
cp .env $(BACKUP_ENV_FILENAME)
cp .env.integration .env
docker compose up -d core-api db minio elasticsearch worker core-api django-app
poetry install --no-root --no-ansi --with dev --without ai,api,worker
# cp .env $(BACKUP_ENV_FILENAME)
# cp .env.integration .env
docker compose up -d --wait elasticsearch db worker minio core-api django-app
poetry install --no-root --no-ansi --with dev --without ai,api,worker,docs
sleep 10
poetry run pytest tests
cp $(BACKUP_ENV_FILENAME) .env
rm $(BACKUP_ENV_FILENAME)
poetry run pytest tests/
# cp $(BACKUP_ENV_FILENAME) .env
# rm $(BACKUP_ENV_FILENAME)

collect-static:
docker compose run django-app venv/bin/django-admin collectstatic --noinput
Expand Down
43 changes: 19 additions & 24 deletions django_app/tests_playwright/_signin.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
import logging
import os
import subprocess
from pathlib import Path

from _settings import BASE_URL
from dotenv import load_dotenv
from playwright.sync_api import Page, expect

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
from playwright.sync_api import Page
from tests_playwright.pages import HomePage, LandingPage, SignInConfirmationPage
from yarl import URL

EMAIL_ADDRESS = os.environ["USER_EMAIL"]
DJANGO_ROOT = Path(__file__).parents[1]
load_dotenv(dotenv_path=DJANGO_ROOT / ".env", override=True)


def sign_in(page: Page):
email_address = os.environ["USER_EMAIL"]
def sign_in(page: Page) -> "HomePage":
# Landing page
landing_page = LandingPage(page)

# Sign in
sign_in_page = landing_page.navigate_to_sign_in()
sign_in_page.email = EMAIL_ADDRESS
sign_in_page.continue_()

if not email_address:
message = "USER_EMAIL not set in your .env - this must be set to the email address you use for signing in."
raise ValueError(message)
# Use magic link
magic_link = get_magic_link(EMAIL_ADDRESS, DJANGO_ROOT)
sign_in_confirmation_page = SignInConfirmationPage(page, magic_link)
return sign_in_confirmation_page.navigate_to_documents_page()

# Sign in page
page.goto(f"{BASE_URL / 'sign-in'}")
expect(page.get_by_text("Redbox Copilot")).to_be_visible()
page.get_by_label("Email Address").type(email_address)
page.get_by_text("Continue").click()

# Get magic link
def get_magic_link(email_address: str, django_root: Path) -> URL:
command = ["poetry", "run", "python", "manage.py", "show_magiclink_url", email_address]
result = subprocess.run(command, capture_output=True, text=True, cwd=DJANGO_ROOT) # noqa: S603
result = subprocess.run(command, capture_output=True, text=True, cwd=django_root) # noqa: S603
magic_link = result.stdout.strip().lstrip("/")

# Complete sign-in and verify
page.goto(f"{BASE_URL / magic_link}")
page.get_by_role("button").click()
expect(page.get_by_text("Sign out")).to_be_visible()
return BASE_URL / magic_link
20 changes: 15 additions & 5 deletions django_app/tests_playwright/pages.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from abc import ABCMeta, abstractmethod
from enum import Enum
from itertools import islice
from pathlib import Path
from typing import Any, ClassVar, NamedTuple
Expand Down Expand Up @@ -50,11 +51,7 @@ def check_title(self):

def check_a11y(self):
results = self.axe.run(self.page, context=None, options=self.AXE_OPTIONS)
if results.violations_count > 0:
expect(
self.page.get_by_text("Accessibility issues"),
f"Accessibility issues in {self.__class__.__name__} at {self.url}",
).to_be_visible()
assert results.violations_count == 0, f"{self.url} - {results.generate_report()}"

def navigate_to_privacy_page(self) -> "PrivacyPage":
self.page.get_by_role("link", name="Privacy", exact=True).click()
Expand Down Expand Up @@ -183,6 +180,11 @@ def get_file_chooser_by_label(self):
return fc_info.value


class FeedbackType(Enum):
HELPFUL = "Helpful"
NOT_HELPFUL = "Not helpful"


class ChatsPage(SignedInBasePage):
def get_expected_page_title(self) -> str:
return "Chats - Redbox Copilot"
Expand All @@ -202,6 +204,14 @@ def send(self) -> "ChatsPage":
def all_messages(self) -> list[str]:
return self.page.locator(".iai-chat-message").all_inner_texts()

def check_feedback_prompt_visible(self, feedback: FeedbackType) -> bool:
if feedback == FeedbackType.NOT_HELPFUL:
return self.page.get_by_text("Can you let me know what wasn’t accurate?").is_visible() # noqa: RUF001
return self.page.get_by_text("Thank you for your feedback").first.is_visible()

def give_feedback(self, feedback: FeedbackType):
self.page.get_by_role("button", name=feedback.value, exact=True).click()


class PrivacyPage(BasePage):
def get_expected_page_title(self) -> str:
Expand Down
26 changes: 26 additions & 0 deletions django_app/tests_playwright/test_chats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from _signin import sign_in
from playwright.sync_api import Page
from tests_playwright.pages import ChatsPage, FeedbackType


def test_response_feedback(page: Page):
home_page = sign_in(page)

chats_page: ChatsPage = home_page.navigate_to_chats()
chats_page.write_message = "This is a test chat"
chats_page = chats_page.send()

assert not chats_page.check_feedback_prompt_visible(FeedbackType.HELPFUL)
assert not chats_page.check_feedback_prompt_visible(FeedbackType.NOT_HELPFUL)

chats_page.give_feedback(FeedbackType.HELPFUL)
assert chats_page.check_feedback_prompt_visible(FeedbackType.HELPFUL)
assert not chats_page.check_feedback_prompt_visible(FeedbackType.NOT_HELPFUL)

chats_page.give_feedback(FeedbackType.NOT_HELPFUL)
assert chats_page.check_feedback_prompt_visible(FeedbackType.NOT_HELPFUL)
assert not chats_page.check_feedback_prompt_visible(FeedbackType.HELPFUL)

chats_page.give_feedback(FeedbackType.NOT_HELPFUL)
assert not chats_page.check_feedback_prompt_visible(FeedbackType.HELPFUL)
assert not chats_page.check_feedback_prompt_visible(FeedbackType.NOT_HELPFUL)
2 changes: 1 addition & 1 deletion tests/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def get_expected_page_title(self) -> str:
def upload_document(self, upload_file: Path) -> DocumentsPage:
self.get_file_chooser_by_label().set_files(upload_file)
self.page.get_by_role("button", name="Upload").click()
return self.navigate_to_documents()
return DocumentsPage(self.page)

def get_file_chooser_by_label(self):
with self.page.expect_file_chooser() as fc_info:
Expand Down
54 changes: 24 additions & 30 deletions tests/test_journey.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
import logging
import os
import string
import subprocess
from pathlib import Path
from random import choice

import pytest
from pages import LandingPage, SignInConfirmationPage
from playwright.sync_api import Page
from yarl import URL

from tests.pages import LandingPage, SignInConfirmationPage

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

BASE_URL = URL("http://localhost:8090/")
EMAIL_ADDRESS = "[email protected]"
TEST_ROOT = Path(__file__)
TEST_ROOT = Path(__file__).parent


def test_user_journey(page: Page):
create_user(EMAIL_ADDRESS)
def test_user_journey(page: Page, email_address: str):
create_user(email_address)

# Landing page
landing_page = LandingPage(page, BASE_URL)

# Sign in
sign_in_page = landing_page.navigate_to_sign_in()
sign_in_page.email = EMAIL_ADDRESS
sign_in_page.email = email_address
sign_in_page.continue_()

# Use magic link
magic_link = get_magic_link(EMAIL_ADDRESS)
magic_link = get_magic_link(email_address)
logger.debug("magic_link: %s", magic_link)
sign_in_confirmation_page = SignInConfirmationPage(page, magic_link)

# Documents page
Expand Down Expand Up @@ -72,33 +73,26 @@ def create_user(email_address: str):
"compose",
"run",
"django-app",
"poetry",
"run",
"python",
"manage.py",
"venv/bin/django-admin",
"createsuperuser",
"--noinput",
"--email",
email_address,
]
env = os.environ.copy()
env["DJANGO_SUPERUSER_EMAIL"] = email_address
env["DJANGO_SUPERUSER_USERNAME"] = email_address
env["DJANGO_SUPERUSER_PASSWORD"] = email_address
subprocess.run(command, capture_output=True, text=True, env=env) # noqa: S603
result = subprocess.run(command, capture_output=True, text=True) # noqa: S603
result.check_returncode()
logger.debug("create_user result: %s", result)


def get_magic_link(email_address: str) -> URL:
command = [
"docker",
"compose",
"run",
"django-app",
"poetry",
"run",
"python",
"manage.py",
"show_magiclink_url",
email_address,
]
command = ["docker", "compose", "run", "django-app", "venv/bin/django-admin", "show_magiclink_url", email_address]
result = subprocess.run(command, capture_output=True, text=True) # noqa: S603
result.check_returncode()
magic_link = result.stdout.strip().lstrip("/")
return BASE_URL / magic_link


@pytest.fixture()
def email_address() -> str:
username = "".join(choice(string.ascii_lowercase) for _ in range(10)) # noqa: S311
return f"{username}@cabinetoffice.gov.uk"

0 comments on commit 0205488

Please sign in to comment.