Skip to content

UI tests with Playwright #413

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 41 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
7c58c7f
wip
blakerosenthal May 8, 2024
e36f4b4
working health page
blakerosenthal May 12, 2024
45a7f33
working api server
blakerosenthal May 13, 2024
b83aafe
fix headless mode
blakerosenthal May 13, 2024
463f564
fix timeout logic
blakerosenthal May 13, 2024
c6f2153
add more tests, auth header not working
blakerosenthal May 14, 2024
c5f3eee
assign text ports and skip failing auth test
blakerosenthal May 14, 2024
f80cadb
Merge branch 'main' into ui-tests
blakerosenthal May 14, 2024
c9ccca8
placeholder test for ui chat
blakerosenthal May 14, 2024
1a6f840
install playwright to gh env
blakerosenthal May 14, 2024
1dc7548
make an api wrapper to try to get around the pickling issue on mac/wi…
blakerosenthal May 14, 2024
1c81128
always install playwright
blakerosenthal May 14, 2024
cd68b85
some PR review fixes
blakerosenthal May 16, 2024
fc61dbe
move shared stuff to utils
blakerosenthal May 16, 2024
6dc91eb
wip
blakerosenthal May 22, 2024
d9f19cb
wip: replace server with cli function
blakerosenthal May 22, 2024
b7e59a5
remove breakpoint
blakerosenthal May 22, 2024
d62bf5e
increase timeout
pmeier May 23, 2024
e6ab71b
server mods
blakerosenthal May 24, 2024
382d71b
use playwright Page directly
blakerosenthal May 24, 2024
b378a51
check to make sure document is in database
blakerosenthal May 24, 2024
53b4c4f
increase timeout
blakerosenthal May 24, 2024
0d80801
increase timeout again
blakerosenthal May 28, 2024
aae2f22
use consistent ports
blakerosenthal May 28, 2024
36e6f43
try with slowmo
blakerosenthal May 30, 2024
050dc72
reorder element expectations; remove slowmo
blakerosenthal May 30, 2024
a983307
hack
blakerosenthal May 30, 2024
f49bdd8
upload playwright video on failing tests
blakerosenthal May 31, 2024
648823d
syntax fix
blakerosenthal May 31, 2024
7038f30
run upload on failure
blakerosenthal May 31, 2024
855c0d3
get tests to pass again
blakerosenthal May 31, 2024
7672c0c
Merge branch 'main' into ui-tests
blakerosenthal Jun 7, 2024
50c0ae1
separate ui and non-ui tests
blakerosenthal Jun 7, 2024
4dade55
avoid relative imports; better func name
blakerosenthal Jun 7, 2024
162e982
use function scope for test ports
blakerosenthal Jun 7, 2024
8c82a4d
review nits
blakerosenthal Jun 7, 2024
dcd4339
make Server a context manager
blakerosenthal Jun 7, 2024
c8ba9dc
try to fix CI matrix
blakerosenthal Jun 7, 2024
d2dfcce
attempt #2
blakerosenthal Jun 7, 2024
8a60430
nits
pmeier Jun 10, 2024
37e9812
remove unused func
blakerosenthal Jun 10, 2024
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
4 changes: 4 additions & 0 deletions .github/actions/setup-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ runs:
shell: bash -el {0}
run: mamba install --yes --channel conda-forge redis-server

- name: Install playwright
shell: bash -el {0}
run: playwright install

- name: Install ragna
shell: bash -el {0}
run: |
Expand Down
2 changes: 2 additions & 0 deletions environment-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dependencies:
- pytest >=6
- pytest-mock
- pytest-asyncio
- pytest-playwright
- playwright
- mypy ==1.10.0
- pre-commit
- types-aiofiles
Expand Down
2 changes: 1 addition & 1 deletion ragna/deploy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
]

from ._authentication import Authentication, RagnaDemoAuthentication
from ._config import Config
from ._config import ApiConfig, Config, UiConfig

# isort: split

Expand Down
183 changes: 183 additions & 0 deletions tests/deploy/ui/test_ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import os
import time
from multiprocessing import Process

import httpx
import panel as pn
import pytest
import uvicorn
from playwright.sync_api import expect, sync_playwright

from ragna.assistants import RagnaDemoAssistant
from ragna.core._utils import default_user
from ragna.deploy import ApiConfig, Config, UiConfig
from ragna.deploy._api import app as api_app
from ragna.deploy._ui import app as ui_app

TEST_API_PORT = "38769"
TEST_UI_PORT = "38770"


class TestAssistant(RagnaDemoAssistant):
def answer(self, prompt, sources, *, multiple_answer_chunks: bool):
content = next(super().answer(prompt, sources))

if multiple_answer_chunks:
for chunk in content.split(" "):
yield f"{chunk} "
else:
yield content


@pytest.fixture(scope="session")
def headed_mode(pytestconfig):
return pytestconfig.getoption("headed") or False


@pytest.fixture
def config(tmp_local_root):
return Config(
local_root=tmp_local_root,
assistants=[TestAssistant],
ui=UiConfig(port=TEST_UI_PORT),
api=ApiConfig(port=TEST_API_PORT),
)


class ApiServer:
def __init__(self, config):
self.config = config

def start_server(self):
uvicorn.run(
api_app(
config=self.config,
ignore_unavailable_components=True,
),
host=self.config.api.hostname,
port=self.config.api.port,
)

def server_up(self):
try:
return httpx.get(self.config.api.url).is_success
except httpx.ConnectError:
return False

def start(self):
self.proc = Process(target=self.start_server, args=(), daemon=True)
self.proc.start()

timeout = 5
while timeout > 0 and not self.server_up():
print(f"Waiting for API server to come up on {self.config.api.url}")
time.sleep(1)
timeout -= 1

def stop(self):
self.proc.kill()


@pytest.fixture
def api_server(config):
server = ApiServer(config)
try:
server.start()
yield server
finally:
server.stop()


def auth_header(base_url):
username = default_user()
token = (
httpx.post(
base_url + "/token",
data={
"username": username,
"password": os.environ.get(
"RAGNA_DEMO_AUTHENTICATION_PASSWORD", username
),
},
)
.raise_for_status()
.json()
)

return f"Bearer {token}"


@pytest.fixture(scope="function")
def page(config, api_server, headed_mode):
server = ui_app(config=config, open_browser=False)

with sync_playwright() as playwright:
server.serve()
browser = playwright.chromium.launch(headless=not headed_mode)
context = browser.new_context()
page = context.new_page()

yield page

context.close()
browser.close()
pn.state.kill_all_servers()


def test_health(config, page) -> None:
health_url = config.ui.origins[0] + "/health"
page.goto(health_url)
expect(page.get_by_role("heading", name="Ok")).to_be_visible()


def test_index_with_blank_credentials(config, page) -> None:
# Index page, no auth
index_url = config.ui.origins[0]
page.goto(index_url)
expect(page.get_by_role("button", name="Sign In")).to_be_visible()

# Authorize with no credentials
page.get_by_role("button", name="Sign In").click()

expect(page.get_by_role("button", name=" New Chat")).to_be_visible()


@pytest.mark.skip(reason="Need to figure out how to set the auth token")
def test_index_with_auth_token(config, page) -> None:
auth = auth_header(config.api.url)
assert len(auth) > len("Bearer ")

page.set_extra_http_headers({"Authorization": auth}) # this doesn't work
pn.state.cookies["auth_token"] = "" # or this
pn.state.headers["Authorization"] = auth # or this

index_url = config.ui.origins[0]
page.goto(index_url)
expect(page.get_by_role("button", name=" New Chat")).to_be_visible()


@pytest.mark.skip(reason="TODO: figure out best locators")
def test_new_chat(config, page) -> None:
index_url = config.ui.origins[0]
page.goto(index_url)
expect(page.get_by_role("button", name="Sign In")).to_be_visible()
page.get_by_role("button", name="Sign In").click()

expect(page.get_by_role("button", name=" New Chat")).to_be_visible()
page.get_by_role("button", name=" New Chat").click()

expect(page.locator("#fileUpload-p4447")).to_be_visible()
page.locator("#fileUpload-p4447").click()

page.locator("#fileUpload-p4447").set_input_files()
page.get_by_role("button", name="Start Conversation").click()
page.get_by_text("How can I help you with the").click()
page.get_by_placeholder("Ask a question about the").click()
page.get_by_placeholder("Ask a question about the").fill(
"Tell me about the documents"
)
page.get_by_role("button", name="").click()
page.get_by_role("button", name=" Source Info").click()
page.locator("#main div").filter(has_text="Source Info ¶ This response").nth(
3
).click()
Loading