diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml new file mode 100644 index 0000000000..3225c63107 --- /dev/null +++ b/.github/workflows/integration-test.yml @@ -0,0 +1,43 @@ +name: Integration tests +run-name: Integration tests +on: [push] +jobs: + integration-test: + runs-on: ubuntu-latest + steps: + - run: echo "Executing Integration tests" + - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" + - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." + - name: Check out repository code + uses: actions/checkout@v4 + - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." + - uses: browser-actions/setup-chrome@v1 + - run: chrome --version + - run: echo "🖥️ The workflow is now ready to test your code on the runner." + - name: "Create python environment" + run: | + python3 -m venv calibre-web-env + source calibre-web-env/bin/activate + - name: Install Python dependencies for testing + uses: py-actions/py-dependency-install@v4 + with: + path: "requirements.txt" + - name: "Download dummy database" + run: | + wget -O app.db "https://github.com/iiab/iiab/raw/refs/heads/master/roles/calibre-web/files/app.db" + - name: "Debugging line" + run: | + ls -la + - name: "Execute calibre web in background" + run: | + nohup python3 cps.py & + - name: "Verify local website" + run: | + curl -L localhost:8083 + - name: Install Python dependencies for testing + uses: py-actions/py-dependency-install@v4 + with: + path: "integration-tests-requirements.txt" + - name: Execute PyTest for Integration Tests + run: | + HEADLESS=true pytest -s diff --git a/.gitignore b/.gitignore index ff08f874bc..cba74ff204 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ gdrive_credentials client_secrets.json gmail.json /.key +environments/ diff --git a/README.md b/README.md index 5bf5c7055c..a662aa825f 100755 --- a/README.md +++ b/README.md @@ -136,6 +136,52 @@ Calibre-Web is a web app that offers a clean and intuitive interface for browsin We would like to thank all the [contributors](https://github.com/janeczku/calibre-web/graphs/contributors) and maintainers of Calibre-Web for their valuable input and dedication to the project. Your contributions are greatly appreciated. +## Running integration tests + +Integration tests were added to this project, follow the steps below to set up and execute the integration tests: + +### Prerequisites + +1. **Install dependencies**: + Ensure you have all required dependencies installed. Use the following commands: + + ```bash + python3 -m venv calibre-web-env + source calibre-web-env/bin/activate + pip install -r requirements.txt + ``` + +1. Add the dummy database from IIAB project: + ```bash + wget -O app.db "https://github.com/iiab/iiab/raw/refs/heads/master/roles/calibre-web/files/app.db" + ``` + +1. Execute calibre web in background: + ```bash + nohup python3 cps.py & + ``` + +1. Install tests requirements: + + ```bash + pip install -r integration-tests-requirements.txt + ``` + +1. Execute the tests: + + If you want to watch the execution process, and the interaction with the browser: + + ``` + HEADLESS=false pytest -s + ``` + + And you can just run headless: + + ``` + HEADLESS=true pytest -s + ``` + + ## Contact Join us on [Discord](https://discord.gg/h2VsJ2NEfB) diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/features/basic_behavior.feature b/features/basic_behavior.feature new file mode 100644 index 0000000000..edaba27442 --- /dev/null +++ b/features/basic_behavior.feature @@ -0,0 +1,18 @@ +Feature: Basic behavior + Testing basic behavior like showing home page and login + + Scenario: Home Page + Given Calibre web is running + When I go to the home page + + Then I should not see the error message + And see homepage information + + Scenario: Login + Given I visit the calibre web homepage + When I login with valid credentials + + Then I should see the success message + And see the information for logged users + + diff --git a/integration-tests-requirements.txt b/integration-tests-requirements.txt new file mode 100644 index 0000000000..bbcc47fb87 --- /dev/null +++ b/integration-tests-requirements.txt @@ -0,0 +1,34 @@ +attrs==24.2.0 +certifi==2024.8.30 +charset-normalizer==3.4.0 +h11==0.14.0 +idna==3.10 +iniconfig==2.0.0 +Mako==1.3.6 +MarkupSafe==3.0.2 +outcome==1.3.0.post0 +packaging==24.1 +parse==1.20.2 +parse_type==0.6.4 +pluggy==1.5.0 +PySocks==1.7.1 +pytest==8.3.3 +pytest-bdd==7.3.0 +pytest-splinter==3.3.2 +python-dotenv==1.0.1 +requests==2.32.3 +selenium==4.25.0 +setuptools==75.2.0 +six==1.16.0 +sniffio==1.3.1 +sortedcontainers==2.4.0 +splinter==0.21.0 +trio==0.27.0 +trio-websocket==0.11.1 +typing_extensions==4.12.2 +urllib3==2.2.3 +webdriver-auto-update==1.2.1 +webdriver-manager==4.0.2 +websocket-client==1.8.0 +wget==3.2 +wsproto==1.2.0 diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000000..5f1f93d10c --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +bdd_features_base_dir = features/ diff --git a/tests/functional/test_basic_behavior.py b/tests/functional/test_basic_behavior.py new file mode 100644 index 0000000000..b92d643767 --- /dev/null +++ b/tests/functional/test_basic_behavior.py @@ -0,0 +1,96 @@ +"""Basic behavior feature tests.""" +import pytest +from pytest_bdd import ( + given, + scenario, + then, + when, +) + +import os +import subprocess +from urllib.parse import urljoin +import time + +@pytest.fixture(scope='session') +def splinter_headless(): + """Override splinter headless option.""" + if os.environ['HEADLESS'] == "true": + return True + else: + return False + +@pytest.fixture(scope='session') +def splinter_webdriver(): + """Override splinter webdriver name.""" + return 'chrome' + +@pytest.fixture +def step_context(): + """Fixture to save information to use through steps.""" + return {} + +@scenario('basic_behavior.feature', 'Home Page') +def test_home_page(): + """Home Page.""" + + +@given('Calibre web is running') +def _(step_context): + """Calibre web is running.""" + step_context['ip_address'] = 'localhost:8083' + + +@when('I go to the home page') +def _(browser, step_context): + """I go to the home page.""" + url = urljoin("".join(['http://', str(step_context['ip_address'])]), '/') + browser.visit(url) + + +@then('I should not see the error message') +def _(browser, step_context): + """I should not see the error message.""" + +@then('see homepage information') +def _(browser): + """see homepage information.""" + print("!!!!!!!") + print(browser.title) + print(browser.url) + print("!!!!!!!") + assert browser.is_text_present('Books'), 'Book test' + +@scenario('basic_behavior.feature', 'Login') +def test_login(): + """Login.""" + + +@given('I visit the calibre web homepage') +def _(browser, step_context): + """I visit the calibre web homepage.""" + step_context['ip_address'] = 'localhost:8083' + url = urljoin("".join(['http://', str(step_context['ip_address'])]), '/') + browser.visit(url) + + +@when('I login with valid credentials') +def _(browser, step_context): + """I login with valid credentials.""" + browser.fill('username', 'Admin') + browser.fill('password', 'changeme') + button = browser.find_by_name('submit') + # Interact with elements + button.click() + + +@then('I should see the success message') +def _(browser, step_context): + """I should see the success message.""" + assert browser.is_text_present('You are now logged in as:'), 'Login successful' + +@then('see the information for logged users') +def _(browser): + """see the information for logged users""" + assert browser.is_text_present('Books'), 'Expected "Books" text to be visible on the home page' + assert browser.is_text_present('Download to IIAB'), 'Expected "Download to IIAB" button for logged users'