Skip to content

Commit

Permalink
CI fixes and improvements (#331)
Browse files Browse the repository at this point in the history
Put back uploading screenshots as artifacts. Screenshots were previously generated in the `screenshots/` directory inside the Git repo.  However, we now change ownership of the Git repo that is mounted into the testing container to user jovyan. 

Therefore, the Git repo directory is no longer writable from outside the container, which means pytest cannot write to it and screenshot files are not saved. It is workaround by giving the package folder the write access of all user inside the container.

Other fixes:

1. Do not test with multiple Python versions, tests themselves run inside the container so this was meaningles.
2. Fix the `setup.cfg`. See comment below. I needed to remove the `setupcfg-fmt` pre-commit hook, but since we plan to move to `pyproject.toml` anyway I think this is fine. 
3. Version bumps of various testing dependencies.
4. Updated `pre-commit` plugins by running `pre-commit autoupdate` manually. This PR supersecedes #323
  • Loading branch information
danielhollas authored Dec 12, 2022
1 parent 50406cc commit 3310e5c
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 63 deletions.
17 changes: 11 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
# Run basic tests for this app on the latest aiidalab-docker image.
# Run basic tests for this app

name: continuous-integration

Expand All @@ -13,16 +13,21 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: '3.10'
cache: pip
cache-dependency-path: |
.pre-commit-config.yaml
**/setup.cfg
**/pyproject.toml
**/requirements*.txt
- name: Install dependencies
run: |
python -m pip install pre-commit==2.11.1
run: python -m pip install pre-commit~=2.20

- name: Run pre-commit
run: pre-commit run --all-files || ( git status --short ; git diff ; exit 1 )
37 changes: 21 additions & 16 deletions .github/workflows/di.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,29 @@ jobs:
strategy:
matrix:
tag: [latest]
image: [aiidalab/full-stack]
browser: [Chrome, Firefox]
python-version: ['3.8', '3.10']
firefox: ['96.0']
fail-fast: false

runs-on: ubuntu-latest
timeout-minutes: 60

steps:
- name: Check out app
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Cache Python dependencies
uses: actions/cache@v1
- name: Set up Python
uses: actions/setup-python@v4
with:
path: ~/.cache/pip
key: pip-${{ matrix.python-version }}-tests-${{ hashFiles('**/setup.json') }}
restore-keys: pip-${{ matrix.python-version }}-tests

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
python-version: '3.10'
cache: pip
cache-dependency-path: |
**/setup.cfg
**/pyproject.toml
**/requirements*.txt
- name: Install dependencies for test
run: |
pip install -U -r requirements_test.txt
run: pip install -U -r requirements_test.txt

- name: Set jupyter token env
run: echo "JUPYTER_TOKEN=$(openssl rand -hex 32)" >> $GITHUB_ENV
Expand All @@ -52,7 +48,7 @@ jobs:
- name: Install Firefox
uses: browser-actions/setup-firefox@latest
with:
firefox-version: ${{ matrix.firefox }}
firefox-version: '96.0'
if: matrix.browser == 'Firefox'

- name: Install geckodriver
Expand All @@ -65,3 +61,12 @@ jobs:
run: pytest --driver ${{ matrix.browser }}
env:
TAG: ${{ matrix.tag }}
AIIDALAB_IMAGE: ${{ matrix.image }}

- name: Upload screenshots as artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: Screenshots-${{ matrix.tag }}-${{ matrix.browser }}
path: screenshots/
if-no-files-found: error
11 changes: 3 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
repos:

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: check-json
- id: check-yaml
Expand All @@ -22,7 +22,7 @@ repos:
language_version: python3 # Should be a command that runs python3.6+

- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 6.0.0
hooks:
- id: flake8
args: [--count, --show-source, --statistics]
Expand All @@ -35,17 +35,12 @@ repos:
- id: isort
args: [--profile, black, --filter-files]

- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.2.0
hooks:
- id: setup-cfg-fmt

- repo: https://github.com/sirosen/check-jsonschema
rev: 0.19.2
hooks:
- id: check-github-workflows

- repo: https://github.com/kynan/nbstripout
rev: 0.5.0
rev: 0.6.1
hooks:
- id: nbstripout
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[build-system]
requires = [
"setuptools>=42",
"setuptools>=62.6",
"wheel"
]
build-backend = "setuptools.build_meta"
4 changes: 2 additions & 2 deletions requirements_test.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pytest~=6.0
pytest~=6.2
pytest-docker~=1.0
pytest-selenium~=4.0
webdriver-manager~=3.8
selenium==4.1.0
selenium~=4.7.0
11 changes: 5 additions & 6 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ python_requires = >=3.8
[options.extras_require]
dev =
bumpver==2022.1119
pre-commit==2.11.1
test =
requirements-test.txt
pre-commit~=2.20
test = file: requirements_test.txt

[options.package_data]
aiidalab_qe.parameters = qeapp.yaml
Expand All @@ -51,9 +50,9 @@ categories =

[flake8]
ignore =
E501 # Line length handled by black.
W503 # Line break before binary operator, preferred formatting for black.
E203 # Whitespace before ':', preferred formatting for black.
E501
W503
E203

[bumpver]
current_version = "v22.12.0"
Expand Down
60 changes: 46 additions & 14 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from pathlib import Path
from urllib.parse import urljoin

import pytest
Expand All @@ -17,24 +18,45 @@ def is_responsive(url):


@pytest.fixture(scope="session")
def notebook_service(docker_ip, docker_services):
"""Ensure that HTTP service is up and responsive."""
def docker_compose(docker_services):
return docker_services._docker_compose

docker_compose = docker_services._docker_compose

# assurance for host user UID other that 1000
chown_command = "exec -T -u root aiidalab bash -c 'chown -R jovyan:users /home/jovyan/apps/aiidalab-qe'"
docker_compose.execute(chown_command)
@pytest.fixture(scope="session")
def aiidalab_exec(docker_compose):
def execute(command, user=None, workdir=None, **kwargs):
opts = "-T"
if user:
opts = f"{opts} --user={user}"
if workdir:
opts = f"{opts} --workdir={workdir}"
command = f"exec {opts} aiidalab {command}"

install_command = "bash -c 'pip install -U .'"
command = f"exec --workdir /home/jovyan/apps/aiidalab-qe/src -T aiidalab {install_command}"
docker_compose.execute(command)
return docker_compose.execute(command, **kwargs)

install_command = "bash -c 'python tests/helper_dep_requirements.py && pip install -U -r /tmp/requirements.txt'"
command = (
f"exec --workdir /home/jovyan/apps/aiidalab-qe -T aiidalab {install_command}"
)
docker_compose.execute(command)
return execute


@pytest.fixture(scope="session")
def nb_user(aiidalab_exec):
return aiidalab_exec("bash -c 'echo \"${NB_USER}\"'").decode().strip()


@pytest.fixture(scope="session")
def notebook_service(docker_ip, docker_services, aiidalab_exec, nb_user):
"""Ensure that HTTP service is up and responsive."""

# Directory ~/apps/aiidalab-qe/ is mounted by docker,
# make it writeable for jovyan user, needed for `pip install`
appdir = f"/home/{nb_user}/apps/aiidalab-qe"
aiidalab_exec(f"chmod -R a+rw {appdir}", user="root")

# Install workchains
aiidalab_exec("pip install .", workdir=f"{appdir}/src", user=nb_user)

# Install App
install_command = "bash -c 'python tests/helper_dep_requirements.py && pip install -r /tmp/requirements.txt'"
aiidalab_exec(install_command, workdir=appdir, user=nb_user)

# `port_for` takes a container port and returns the corresponding host port
port = docker_services.port_for("aiidalab", 8888)
Expand Down Expand Up @@ -62,6 +84,16 @@ def _selenium_driver(nb_path, wait_time=5.0):
return _selenium_driver


@pytest.fixture(scope="session")
def screenshot_dir():
sdir = Path.joinpath(Path.cwd(), "screenshots")
try:
os.mkdir(sdir)
except FileExistsError:
pass
return sdir


@pytest.fixture
def firefox_options(firefox_options):
firefox_options.add_argument("--headless")
Expand Down
2 changes: 1 addition & 1 deletion tests/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version: '3.4'
services:

aiidalab:
image: aiidalab/full-stack:${TAG:-latest}
image: ${AIIDALAB_IMAGE:-aiidalab/full-stack}:${TAG:-latest}
environment:
RMQHOST: messaging
TZ: Europe/Zurich
Expand Down
4 changes: 2 additions & 2 deletions tests/helper_dep_requirements.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3

"""This helper script is for temporarily remove the
`aiidalab-qe-workchain` package from dependencies list so that
"""This helper script removes the `aiidalab-qe-workchain` package
from dependencies list so that
in the test it will not use the remote released source.
"""

Expand Down
25 changes: 18 additions & 7 deletions tests/test_qe_app.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import time
from pathlib import Path

import requests
from selenium.webdriver.common.by import By

Expand All @@ -8,21 +11,29 @@ def test_notebook_service_available(notebook_service):
assert response.status_code == 200


def test_qe_app_take_screenshot(selenium_driver):
def test_qe_app_take_screenshot(selenium_driver, screenshot_dir):
driver = selenium_driver("qe.ipynb", wait_time=30.0)
driver.set_window_size(1920, 985)
driver.get_screenshot_as_file("screenshots/qe-app.png")
driver.set_window_size(1920, 1485)
time.sleep(15)
driver.get_screenshot_as_file(str(Path.joinpath(screenshot_dir, "qe-app.png")))


def test_qe_app_select_silicon(selenium_driver):
def test_qe_app_select_silicon(selenium_driver, screenshot_dir):
driver = selenium_driver("qe.ipynb", wait_time=30.0)
driver.set_window_size(1920, 985)
driver.set_window_size(1920, 1485)
driver.find_element(
By.XPATH, "//*[text()='From Examples']"
).click() # click `From Examples` tab for input structure
driver.find_element(By.XPATH, "//option[@value='Diamond']").click()
driver.get_screenshot_as_file("screenshots/qe-app-select-diamond-selected.png")
time.sleep(2)
driver.get_screenshot_as_file(
str(Path.joinpath(screenshot_dir, "qe-app-select-diamond-selected.png"))
)
confirm_button = driver.find_element(By.XPATH, "//button[text()='Confirm']")
confirm_button.location_once_scrolled_into_view # scroll into view
confirm_button.click()
driver.get_screenshot_as_file("screenshots/qe-app-select-diamond-confirmed.png")
# Test that we have indeed proceeded to the next step
driver.find_element(By.XPATH, "//span[contains(.,'✓ Step 1')]")
driver.get_screenshot_as_file(
str(Path.joinpath(screenshot_dir, "qe-app-select-diamond-confirmed.png"))
)

0 comments on commit 3310e5c

Please sign in to comment.