Skip to content
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

Initial playwright setup #1665

Merged
merged 79 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from 78 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
3601e04
initial playwright setup
kcpevey Feb 28, 2023
635d99b
[pre-commit.ci] Apply automatic pre-commit fixes
pre-commit-ci[bot] Feb 28, 2023
304c288
update run notebook test, working well now
kcpevey Mar 9, 2023
d4f30f7
[pre-commit.ci] Apply automatic pre-commit fixes
pre-commit-ci[bot] Mar 9, 2023
bd4e939
add notebook for testing, clean up CI
kcpevey Mar 20, 2023
78e7fc4
Merge branch 'develop' into add_playwright
kcpevey Mar 20, 2023
481e788
use the right dotenv
kcpevey Mar 20, 2023
a57f3c4
run playwright tests under separate workflow
kcpevey Mar 20, 2023
6d20c98
change run location
kcpevey Mar 20, 2023
1f1e141
run headless
kcpevey Mar 20, 2023
be6c794
ensure headless
kcpevey Mar 20, 2023
aae1d16
pass cli argument to pytest as fixture
kcpevey Mar 20, 2023
4f8dc3d
typo...
kcpevey Mar 20, 2023
ed4e830
restrict tests on PRs to exclude playwright tests
kcpevey Mar 21, 2023
6e5ee6d
add a few file types to gitignore
kcpevey Mar 21, 2023
f6de84d
move playwright to k8s gh action
kcpevey Mar 21, 2023
a2faeee
gh action syntax strikes again!
kcpevey Mar 21, 2023
bac4a59
fix string env var replacement
kcpevey Mar 21, 2023
5b305cd
add username/password login option
kcpevey Mar 22, 2023
6ec87f0
minor fixes
kcpevey Mar 22, 2023
2ad22fe
add https to url
kcpevey Mar 27, 2023
2874c8b
remove conflicing sync test
kcpevey Mar 27, 2023
346eaae
add config to artifacts
kcpevey Mar 28, 2023
dc7b227
add config to artifact... second try
kcpevey Mar 28, 2023
8200a4f
add instance type to env vars to smooth working between local and ci
kcpevey Mar 28, 2023
5024154
fix stupid mistake
kcpevey Mar 28, 2023
2390753
add videos to tests
kcpevey Mar 29, 2023
c755179
try/except on the nav fixture- temp
kcpevey Mar 29, 2023
70f0f66
fix bad username/password
kcpevey Mar 29, 2023
c4105a9
git clone, add a simpler test to check login
kcpevey Mar 30, 2023
50926ab
apparently can't run multiple tests with different nav instances...
kcpevey Mar 30, 2023
5f80e59
clone repo in test, add more doc strings
kcpevey Mar 30, 2023
5f94818
fix extra slashes in constructed url
kcpevey Mar 30, 2023
a7ef508
add wait for notebook to actually run
kcpevey Mar 30, 2023
cd39da3
jupyter loading doesn't count as network traffic
kcpevey Mar 31, 2023
ec249d5
dont wait for server message, its too variable
kcpevey Mar 31, 2023
beebd9f
add sleep to terminal interactions
kcpevey Apr 11, 2023
a4b7cba
Merge branch 'develop' into add_playwright
kcpevey Apr 11, 2023
ccf0f61
[pre-commit.ci] Apply automatic pre-commit fixes
pre-commit-ci[bot] Apr 11, 2023
a431301
remove extra setting
kcpevey Apr 11, 2023
9b101bb
check conda env list
kcpevey Apr 12, 2023
e719bd0
switch to default env
kcpevey Apr 12, 2023
7c6b62a
rename file
kcpevey Apr 12, 2023
bee6053
switch order of cypress and playwright tests, separate navigator and …
kcpevey Apr 12, 2023
76d78d5
increase cypress wait for jlab to load
kcpevey Apr 12, 2023
2433510
fix import
kcpevey Apr 12, 2023
62ce07b
clone repo takes longer than 5 seconds
kcpevey Apr 13, 2023
7da6cc2
extend notebook runtime
kcpevey Apr 13, 2023
726a08d
increase cypress wait for jlab to load
kcpevey Apr 14, 2023
ca1570b
extend time to clone repo
kcpevey Apr 14, 2023
4886e3f
more waits in more places to account for jupyter flakinesss
kcpevey Apr 14, 2023
4da9d4a
Merge branch 'develop' into add_playwright
kcpevey Apr 18, 2023
8a905a2
doc strings cleanup
kcpevey Apr 20, 2023
f2a1396
disable azure tests for now
kcpevey Apr 20, 2023
c5c9624
restart dialog doesn't always popup
kcpevey Apr 21, 2023
7db3696
address review comments
kcpevey May 9, 2023
9965b29
Merge branch 'develop' into add_playwright
kcpevey May 9, 2023
96ba3b1
address review comments
kcpevey May 9, 2023
06bdba7
Apply suggestions from code review
kcpevey May 9, 2023
15a5044
rework server startup
kcpevey May 9, 2023
729092a
fix import
kcpevey May 9, 2023
9e62d31
jupyter is flaky, add retry to restart run all
kcpevey May 10, 2023
df1a2ca
fix trailing slash, remove instance type from .env
kcpevey May 10, 2023
93c703b
more reviewer comments
kcpevey May 11, 2023
5245dc4
Update tests_e2e/playwright/conftest.py
kcpevey May 11, 2023
902b466
fixture cleanup
kcpevey May 11, 2023
74e99c0
Merge branch 'develop' into add_playwright
kcpevey May 16, 2023
9d5ce0b
fix typo
aktech Jul 18, 2023
11d6cf3
Merge branch 'develop' into add_playwright
aktech Jul 18, 2023
7af2be8
write notebook on the nebari cluster instead of cloning
aktech Jul 18, 2023
2558c4a
remove cloning docs
aktech Jul 18, 2023
efaff5b
add docs to write_file, open terminal etc
aktech Jul 18, 2023
76ff6db
change notebook path relative to playwright
aktech Jul 18, 2023
a0954b5
[pre-commit.ci] Apply automatic pre-commit fixes
pre-commit-ci[bot] Jul 18, 2023
eb9a768
address comments
pmeier Jul 20, 2023
e803e2a
Merge branch 'develop' into add_playwright
pmeier Jul 20, 2023
e4d2a5f
add docs about creating a user
aktech Jul 20, 2023
ece5d19
add support for --headed and --slowmo
pmeier Jul 20, 2023
79aaba0
fix login env variables
aktech Jul 20, 2023
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
14 changes: 14 additions & 0 deletions .github/workflows/kubernetes_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ jobs:
run: |
conda install --quiet --yes -c anaconda pip
pip install .[dev]
playwright install
- name: Download and Install Kubectl
run: |
mkdir -p bin
Expand Down Expand Up @@ -149,6 +150,18 @@ jobs:
with:
working-directory: tests_e2e

- name: Playwright Tests
env:
USERNAME: ${{ env.CYPRESS_EXAMPLE_USER_NAME }}
PASSWORD: ${{ env.CYPRESS_EXAMPLE_USER_PASSWORD }}
NEBARI_FULL_URL: https://github-actions.nebari.dev/
working-directory: tests_e2e/playwright
run: |
# create environment file
envsubst < .env.tpl > .env
# run playwright pytest tests in headed mode with the chromium browser
xvfb-run pytest --browser chromium

- name: Save Cypress screenshots and videos
if: always()
uses: actions/upload-artifact@v3
Expand All @@ -157,6 +170,7 @@ jobs:
path: |
./tests_e2e/cypress/screenshots/
./tests_e2e/cypress/videos/
./tests_e2e/playwright/videos/
pmeier marked this conversation as resolved.
Show resolved Hide resolved

- name: Deployment Pytests
run: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- ".github/workflows/test.yaml"
- "tests/**"
- "tests_deployment/**"
- "tests_e2e/**"
- "tests_e2e/cypress/**"
- "scripts/**"
- "src/**"
- "pyproject.toml"
Expand All @@ -20,7 +20,7 @@ on:
- ".github/workflows/test.yaml"
- "tests/**"
- "tests_deployment/**"
- "tests_e2e/**"
- "tests_e2e/cypress/**"
- "scripts/**"
- "src/**"
- "pyproject.toml"
Expand Down Expand Up @@ -56,4 +56,4 @@ jobs:
- name: Test Nebari
run: |
pytest --version
pytest --ignore=tests_deployment
pytest --ignore=tests_deployment --ignore=tests_e2e/playwright
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# docs
.nox
_build
.env

# setuptools scm
src/_nebari/_version.py

Expand All @@ -7,6 +12,9 @@ terraform.tfstate
terraform.tfstate.backup
.terraform.tfstate.lock.info

# tests
videos/

# python
__pycache__
build/
Expand Down Expand Up @@ -41,5 +49,6 @@ nebari-config.yaml

.vscode/
.pytest_cache

.ipynb_checkpoints
.DS_Store
/.ruff_cache
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ dev = [
"black==22.3.0",
"dask-gateway",
"diagrams",
"python-dotenv",
"escapism",
"flake8==3.8.4",
"importlib-metadata<5.0",
Expand All @@ -84,6 +85,7 @@ dev = [
"pre-commit",
"pytest",
"pytest-timeout",
"pytest-playwright",
"grayskull",
"build",
"jinja2",
Expand Down
3 changes: 3 additions & 0 deletions tests_e2e/playwright/.env.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
KEYCLOAK_USERNAME="USERNAME_OR_GOOGLE_EMAIL"
KEYCLOAK_PASSWORD="PASSWORD"
NEBARI_FULL_URL="https://nebari.quansight.dev/"
199 changes: 199 additions & 0 deletions tests_e2e/playwright/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# Nebari integration testing with Playwright


## How does it work?

Playwright manages interactions with any website. We are using it to interact
with a deployed Nebari instance and test the various integrations that are
included.

For our test suite, we utilize Playwright's synchronous API. The first task
is to launch the web browser you'd like to test in. Options in our test suite
are `chromium`, `webkit`, and `firefox`. Playwright uses browser contexts to
achieve test isolation. The context can either be created by default or
manually (for the purposes of generating multiple contexts per test in the case
of admin vs user testing). Next the page on the browser is created. For all
tests this starts as a blank page, then during the test, we navigate to a given
url. This is all achieved in the `setup` method of the `Navigator` class.

## Setup

Install Nebari with the development requirements (which include Playwright)

`pip install -e ".[dev]"`

Then install playwright itself (required).

`playwright install`
kcpevey marked this conversation as resolved.
Show resolved Hide resolved

> If you see the warning `BEWARE: your OS is not officially supported by Playwright; downloading fallback build., it is not critical.` Playwright will likely still work microsoft/playwright#15124

### Create environment file

Create a copy of the `.env` template file

```bash
cd tests_e2e/playwright
cp .env.tpl .env
pmeier marked this conversation as resolved.
Show resolved Hide resolved
```

kcpevey marked this conversation as resolved.
Show resolved Hide resolved
Fill in the newly created `.env` file with the following values:

* KEYCLOAK_USERNAME: Nebari username for username/password login OR Google email address or Google sign in
* KEYCLOAK_PASSWORD: Password associated with USERNAME
* NEBARI_FULL_URL: full url path including scheme to Nebari instance, e.g. "https://nebari.quansight.dev/"

This user can be created with the following command (or you can use an existing non-root user):

```
nebari keycloak adduser --user <username> <password> --config <NEBARI_CONFIG_PATH>
```

## Running the Playwright tests

The playwright tests are run inside of pytest using

```python
pytest tests_e2e/playwright/test_playwright.py
```
pmeier marked this conversation as resolved.
Show resolved Hide resolved

Videos of the test playback will be available in `$PWD/videos/`.
To see what is happening while the test is run, pass the `--headed` option to `pytest`.
You can also add the `--slowmo=$MILLI_SECONDS` option to add a delay before each action
by Playwright and thus slowing down the process.

Another option is to run playwright methods outside of pytest. Both
`navigator.py` and `run_notebook.py` can be run as scripts. For example,

```python
import os

import dotenv
# load environment variables from .env file
dotenv.load_dotenv()
# instantiate the navigator class
nav = Navigator(
nebari_url="https://nebari.quansight.dev/",
username=os.environ["KEYCLOAK_USERNAME"],
password=os.environ["KEYCLOAK_PASSWORD"],
auth="password",
instance_name="small-instance",
headless=False,
slow_mo=100,
)
# go through login sequence (defined by `auth` method in Navigator class)
nav.login()
# start the nebari server (defined by `instance_type` in Navigator class)
nav.start_server()
# reset the jupyterlab workspace to ensure we're starting with only the
# Launcher screen open, and we're in the root directory.
nav.reset_workspace()
# instantiate our test application
test_app = RunNotebook(navigator=nav)
# Write the sample notebook on the nebari instance
notebook_filepath_in_repo = (
"tests_e2e/playwright/test_data/test_notebook_output.ipynb"
)
notebook_filepath_on_nebari = "test_notebook_output.ipynb"
with open(notebook_filepath_in_repo, "r") as notebook:
test_app.nav.write_file(
filepath=notebook_filepath_on_nebari, content=notebook.read()
)
# run a sample notebook
test_app.run_notebook(
path="nebari/tests_e2e/playwright/test_data/test_notebook_output.ipynb",
expected_output_text="success: 6",
conda_env="conda-env-default-py",
)
# close out playwright and its associated browser handles
nav.teardown()
```

## Writing Playwright tests
kcpevey marked this conversation as resolved.
Show resolved Hide resolved

In general most of the testing happens through `locators` which is Playwright's
way of connecting a python object to the HTML element on the page.
The Playwright API has several mechanisms for getting a locator for an item on
the page (`get_by_role`, `get_by_text`, `get_by_label`, `get_by_placeholder`,
etc).

```python
button = self.page.get_by_role("button", name="Sign in with Keycloak")
```

Once you have a handle on a locator, you can interact with it in different ways,
depending on the type of object. For example, clicking
a button:

```python
button.click()
```

Occasionally you'll need to wait for things to load on the screen. We can
either wait for the page to finish loading:

```python
self.page.wait_for_load_state("networkidle")
```

or we can wait for something specific to happen with the locator itself:

```python
button.wait_for(timeout=3000, state="attached")
```

Note that waiting for the page to finish loading may be deceptive inside of
Jupyterlab since things may need to load _inside_ the page, not necessarily
causing network traffic - or causing several bursts network traffic, which
would incorrectly pass the `wait_for_load_state` after the first burst.

Playwright has a built-in auto-wait feature which waits for a timeout period
for some actionable items. See https://playwright.dev/docs/actionability .

### Workflow for creating new tests

An example of running a new run notebook test might look like this:

```python
import os

import dotenv
# load environment variables from .env file
dotenv.load_dotenv()
# instantiate the navigator class
nav = Navigator(
nebari_url="https://nebari.quansight.dev/",
username=os.environ["KEYCLOAK_USERNAME"],
password=os.environ["KEYCLOAK_PASSWORD"],
auth="password",
instance_name="small-instance",
headless=False,
slow_mo=100,
)
# go through login sequence (defined by `auth` method in Navigator class)
nav.login()
# start the nebari server (defined by `instance_type` in Navigator class)
nav.start_server()
# reset the jupyterlab workspace to ensure we're starting with only the
# Launcher screen open, and we're in the root directory.
nav.reset_workspace()
# instantiate our test application
test_app = RunNotebook(navigator=nav)
# Write the sample notebook on the nebari instance
notebook_filepath_in_repo = (
"tests_e2e/playwright/test_data/test_notebook_output.ipynb"
)
notebook_filepath_on_nebari = "test_notebook_output.ipynb"
with open(notebook_filepath_in_repo, "r") as notebook:
test_app.nav.write_file(
filepath=notebook_filepath_on_nebari, content=notebook.read()
)
# run a sample notebook
test_app.run_notebook(
path="nebari/tests_e2e/playwright/test_data/test_notebook_output.ipynb",
expected_output_text="success: 6",
conda_env="conda-env-default-py",
)
# close out playwright and its associated browser handles
nav.teardown()
```
59 changes: 59 additions & 0 deletions tests_e2e/playwright/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import logging
import os
from pathlib import Path

import dotenv
import pytest
from navigator import Navigator

logger = logging.getLogger()


@pytest.fixture(scope="session")
def _navigator_session(browser_name, pytestconfig):
"""Set up a navigator instance, login with username/password, start
a server. Teardown when session is complete.
Do not use this for individual tests, use `navigator` fixture
for tests."""
dotenv.load_dotenv()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why we need the whole dotenv package? Is this just for debugging locally? If so, can't we manage without an extra dependency? It seems just loading a Python file should suffice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting I do from dotenv import load_dotenv ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the question is rather "Do we need dotenv at all"? Unless I'm missing something, we only use it to set some environment variables that we later use inside the test. Can't we just set them manually? We do we need an extra dependency for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it for easy-of-use so that I don't have to set env vars to run a test. I can keep a .env in my local repo, have it gitignored, and everything just works. Otherwise, I'd have to manually set them in the py file before I ran locally. Which would then cause a git diff. I added it as dev requirement, which I didn't think would be that big a deal. If you take serious issue with it, I can remove.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you take serious issue with it, I can remove.

I personally like to keep the needed tools / requirements to a minimum to keep the mental load small. That being said, without anyone else chiming in, this is a "you vs. me" situation. Unless we want to have a more general discussion about this with the team now, I'm ok to withdraw my concern in favor of velocity.

# try/except added here in attempt to reach teardown after error in
# order to close the browser context which will save the video so I debug
# the error.
try:
nav = Navigator(
nebari_url=os.environ["NEBARI_FULL_URL"],
username=os.environ["KEYCLOAK_USERNAME"],
password=os.environ["KEYCLOAK_PASSWORD"],
headless=not pytestconfig.getoption("--headed"),
slow_mo=pytestconfig.getoption("--slowmo"),
browser=browser_name,
auth="password",
instance_name="small-instance", # small-instance included by default
video_dir="videos/",
)
except Exception as e:
logger.debug(e)
raise

try:
nav.login_password()
pmeier marked this conversation as resolved.
Show resolved Hide resolved
nav.start_server()
yield nav
except Exception as e:
pmeier marked this conversation as resolved.
Show resolved Hide resolved
logger.debug(e)
raise
finally:
nav.teardown()


@pytest.fixture(scope="function")
def navigator(_navigator_session):
"""High level navigator instance with a reset workspace."""
_navigator_session.reset_workspace()
yield _navigator_session


@pytest.fixture(scope="session")
def test_data_root():
here = Path(__file__).parent
return here / "test_data"
Loading
Loading