Skip to content

Commit

Permalink
Merge pull request #351 from openzim/automated_daily_tests
Browse files Browse the repository at this point in the history
Automate daily tests of ZIM behavior - Youtube only for now
  • Loading branch information
benoit74 authored Aug 7, 2024
2 parents 751e104 + 6d078c4 commit 1ea533c
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/DailyTests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: DailyTests

on:
schedule:
- cron: "0 4 * * *"
workflow_dispatch:


jobs:
run-daily-tests:
runs-on: ubuntu-22.04

steps:
- name: checkout
uses: actions/checkout@v4

- name: build zimit image
run: docker build -t local-zimit .

- name: run crawl of test website
run: docker run -v $PWD/output3:/output local-zimit zimit --url https://website.test.openzim.org/ --name tests_eng_test-website --zim-file tests_eng_test-website.zim

- name: build selenium test image
run: docker build -t local-selenium tests-daily

- name: run integration test suite
run: docker run -v $PWD/tests-daily/daily.py:/app/daily.py -v $PWD/output3:/output local-selenium bash -c "cd /app && pytest -h && pytest -v --log-level=INFO --log-format='%(levelname)s - %(message)s' daily.py"
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Add `--custom-behaviors` argument to support path/HTTP(S) URL custom behaviors to pass to the crawler (#313)
- Add daily automated end-to-end tests of a page with Youtube player (#330)

### Changed

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ test = [
dev = [
"pre-commit==3.7.1",
"debugpy==1.8.1",
"selenium==4.23.0", # used in daily tests, convenient for dev purpose (autocompletion)
"zimit[scripts]",
"zimit[lint]",
"zimit[test]",
Expand Down
75 changes: 75 additions & 0 deletions tests-daily/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Let's extract kiwix-tools as usual on alpine temporary build container
FROM alpine:3.18 as kiwix-serve
LABEL org.opencontainers.image.source https://github.com/openzim/kiwix-tools

# TARGETPLATFORM is injected by docker build
ARG TARGETPLATFORM
ARG KIWIX_TOOLS_VERSION

RUN set -e && \
# default (no KIWIX_TOOLS_VERSION set) to today's nightly
if [ -z "$KIWIX_TOOLS_VERSION" ] ; then KIWIX_TOOLS_VERSION=$(date +"%Y-%m-%d") ; fi && \
apk --no-cache add dumb-init curl && \
echo "TARGETPLATFORM: $TARGETPLATFORM" && \
if [ "$TARGETPLATFORM" = "linux/386" ]; then ARCH="i586"; \
# linux/arm64/v8 points to linux/arm64
elif [ "$TARGETPLATFORM" = "linux/arm64/v8" \
-o "$TARGETPLATFORM" = "linux/arm64" ]; then ARCH="aarch64"; \
# linux/arm translates to linux/arm/v7
elif [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then ARCH="armv8"; \
elif [ "$TARGETPLATFORM" = "linux/arm/v6" ]; then ARCH="armv6"; \
elif [ "$TARGETPLATFORM" = "linux/amd64/v3" \
-o "$TARGETPLATFORM" = "linux/amd64/v2" \
-o "$TARGETPLATFORM" = "linux/amd64" ]; then ARCH="x86_64"; \
# we dont suppot any other arch so let it fail
else ARCH="unknown"; fi && \
# download requested kiwix-tools version
url="http://mirror.download.kiwix.org/nightly/$KIWIX_TOOLS_VERSION/kiwix-tools_linux-$ARCH-$KIWIX_TOOLS_VERSION.tar.gz" && \
echo "URL: $url" && \
mkdir /kiwix-serve && \
curl -k -L $url | tar -xz -C /kiwix-serve --strip-components 1

# Build real "workload" container
FROM python:3.12-slim-bookworm

# Add kiwix-serve
COPY --from=kiwix-serve /kiwix-serve /usr/local/bin

# Update apt + install dependencies + install Google Chrome dependencies + clean-up apt lists
RUN apt-get update -y && \
apt-get install -qqy wget xvfb unzip jq && \
apt-get install -qqy libxss1 libappindicator1 libgconf-2-4 \
fonts-liberation libasound2 libnspr4 libnss3 libx11-xcb1 libxtst6 lsb-release xdg-utils \
libgbm1 libnss3 libatk-bridge2.0-0 libgtk-3-0 libx11-xcb1 libxcb-dri3-0 && \
rm -rf /var/lib/apt/lists/*

# Fetch the latest version numbers and URLs for Chrome and ChromeDriver
RUN wget -q -O /tmp/versions.json https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json

# Install chrome
RUN CHROME_URL=$(jq -r '.channels.Stable.downloads.chrome[] | select(.platform=="linux64") | .url' /tmp/versions.json) && \
wget -q --continue -O /tmp/chrome-linux64.zip $CHROME_URL && \
unzip /tmp/chrome-linux64.zip -d /opt/chrome

RUN chmod +x /opt/chrome/chrome-linux64/chrome

# Install chromedriver
RUN CHROMEDRIVER_URL=$(jq -r '.channels.Stable.downloads.chromedriver[] | select(.platform=="linux64") | .url' /tmp/versions.json) && \
wget -q --continue -O /tmp/chromedriver-linux64.zip $CHROMEDRIVER_URL && \
unzip /tmp/chromedriver-linux64.zip -d /opt/chromedriver && \
chmod +x /opt/chromedriver/chromedriver-linux64/chromedriver

# Set up Chromedriver Environment variables
ENV CHROMEDRIVER_DIR /opt/chromedriver
ENV PATH $CHROMEDRIVER_DIR:$PATH

# Clean up
RUN rm /tmp/chrome-linux64.zip /tmp/chromedriver-linux64.zip /tmp/versions.json

# Update pip, install selenium, create work directory
RUN \
python -m pip install --no-cache-dir -U \
pip \
selenium==4.23.0 \
pytest==8.2.2 \
&& mkdir -p /work
124 changes: 124 additions & 0 deletions tests-daily/daily.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import logging
import subprocess
from time import sleep

import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait

KIWIX_SERVE_START_SLEEP = 1

ZIM_NAME = "tests_eng_test-website"
YOUTUBE_VIDEO_PATH = "youtube.fuzzy.replayweb.page/embed/g5skcrNXdDM"

CHECK_VIDEO_IS_PLAYING_AFTER_SECS = 30

logger = logging.getLogger(__name__)


@pytest.fixture(scope="module")
def chrome_driver():
"""Start chrome and setup chrome driver / selenium"""

logger.info("Starting Chrome")
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
# Other options of interest:
# --disable-dev-shm-usage (not needed anymore with recent chrome versions)
# --disable-gpu (important for some versions of Chrome)
# --remote-debugging-port=9222 (should you need to remote debug)

# Set path to Chrome binary
chrome_options.binary_location = "/opt/chrome/chrome-linux64/chrome"

# Set path to ChromeDriver
chrome_service = ChromeService(
executable_path="/opt/chromedriver/chromedriver-linux64/chromedriver"
)

# Set up driver
driver = webdriver.Chrome(service=chrome_service, options=chrome_options)

yield driver

# Cleanup
logger.info("Quitting Chrome")
driver.quit()


@pytest.fixture(scope="module")
def kiwix_serve():
"""Start kiwix-serve with given ZIM"""

logger.info("Starting kiwix-serve")
process = subprocess.Popen(
[
"/usr/bin/env",
"/usr/local/bin/kiwix-serve",
f"/output/{ZIM_NAME}.zim",
]
)

logger.info(
f"Waiting {KIWIX_SERVE_START_SLEEP} secs to be 'sure' that kiwix-serve is ready"
)
sleep(KIWIX_SERVE_START_SLEEP)

if process.poll() is not None:
raise Exception("kiwix-serve has terminated too early")

yield process

# Cleanup
logger.info("Quitting kiwix-serve")
process.terminate()


def test_youtube_video(chrome_driver, kiwix_serve): # noqa: ARG001
"""Test that youtube video loads, and still plays after a while"""

chrome_driver.get(f"http://localhost:80/content/{ZIM_NAME}/{YOUTUBE_VIDEO_PATH}")

if chrome_driver.title == "Content not found":
raise Exception("Wrong URL, kiwix-serve said that content is not found")

button = WebDriverWait(chrome_driver, 1).until(
expected_conditions.presence_of_element_located(
(By.XPATH, "//button[@title='Play']")
)
)

logger.info("Play button found in page")

button.click()

video = WebDriverWait(chrome_driver, 1).until(
expected_conditions.presence_of_element_located((By.TAG_NAME, "video"))
)

logger.info("Video found in page")

# arguments[0] is the video tag passed to execute_script
if not chrome_driver.execute_script("return arguments[0].paused === false", video):
raise Exception("Video is not playing, failed to start probably")

logger.info("Video is playing")

logger.info(
f"Waiting {CHECK_VIDEO_IS_PLAYING_AFTER_SECS} secs to check video is still "
"playing"
)
sleep(CHECK_VIDEO_IS_PLAYING_AFTER_SECS)

# arguments[0] is the video tag passed to execute_script
if not chrome_driver.execute_script("return arguments[0].paused === false", video):
raise Exception(
"Video is not playing anymore after "
f"{CHECK_VIDEO_IS_PLAYING_AFTER_SECS} secs"
)
logger.info("Video is still playing")

0 comments on commit 1ea533c

Please sign in to comment.