From 8a8a328bdf71f513c69a5fd3eedcc63a5eb3fda1 Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Fri, 19 Sep 2025 16:56:18 -0500 Subject: [PATCH 01/11] feat: add login smoke test --- .github/workflows/smoke-tests.yml | 52 ++++++++++++++++++++++++++ scripts/smoke-tests/test_login_user.py | 37 ++++++++++++++++++ scripts/smoke-tests/utils.py | 17 +++++++++ 3 files changed, 106 insertions(+) create mode 100644 .github/workflows/smoke-tests.yml create mode 100644 scripts/smoke-tests/test_login_user.py create mode 100644 scripts/smoke-tests/utils.py diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml new file mode 100644 index 0000000..c53d4ba --- /dev/null +++ b/.github/workflows/smoke-tests.yml @@ -0,0 +1,52 @@ +name: Smoke Tests + +on: + workflow_call: + inputs: + target_url: + description: "Base URL for the application" + required: true + type: string + user_email: + description: "Test user email" + required: true + type: string + user_password: + description: "Test user password" + required: true + type: string + +jobs: + login-user: + name: Login User Test + runs-on: ubuntu-latest + + steps: + - name: Checkout actions-hub repo + uses: actions/checkout@v4 + with: + repository: nelc/actions-hub + ref: and/add-basic-smoke-tests + path: actions-hub + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + pip install pytest-playwright + playwright install + + - name: Hide the inputs values to keep them private in the logs when running this workflow + uses: levibostian/action-hide-sensitive-inputs@v1 + with: + exclude_inputs: target_url + + - name: Run login-user test + run: | + python actions-hub/scripts/smoke-tests/test_login_user.py \ + --base-url "${{ inputs.target_url }}" \ + --email "${{ inputs.user_email }}" \ + --password "${{ inputs.user_password }}" diff --git a/scripts/smoke-tests/test_login_user.py b/scripts/smoke-tests/test_login_user.py new file mode 100644 index 0000000..aac2854 --- /dev/null +++ b/scripts/smoke-tests/test_login_user.py @@ -0,0 +1,37 @@ +import argparse + +from playwright.sync_api import expect, sync_playwright +from utils import login_user + + +def test_login_user(base_url: str, email: str, password: str) -> None: + """ + Runs a login smoke test using Playwright against the specified base URL. + + Args: + base_url (str): Base URL of the target application. + email (str): Email of the test user. + password (str): Password of the test user. + + Raises: + AssertionError: If the login fails or the dashboard page is not reached. + """ + with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page() + + login_user(page, base_url, email, password) + + expect(page).to_have_url(f"{base_url}/dashboard") + + browser.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Run login smoke test using Playwright.") + parser.add_argument("--base-url", required=True, help="Base URL of the target application.") + parser.add_argument("--email", required=True, help="Login email.") + parser.add_argument("--password", required=True, help="Login password.") + args = parser.parse_args() + + test_login_user(base_url=args.base_url, email=args.email, password=args.password) diff --git a/scripts/smoke-tests/utils.py b/scripts/smoke-tests/utils.py new file mode 100644 index 0000000..bcfa625 --- /dev/null +++ b/scripts/smoke-tests/utils.py @@ -0,0 +1,17 @@ + + +def login_user(page, base_url: str, email: str, password: str): + """ + Logs in a user using the login form. + + + Args: + page: Playwright Page object. + base_url (str): Base URL of the application. + email (str): User email. + password (str): User password. + """ + page.goto(f"{base_url}/login") + page.fill("input[name='email']", email) + page.fill("input[name='password']", password) + page.click("button[type='submit']") From 0b7126756b8fcf5540b2441281289a488d065e43 Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Fri, 19 Sep 2025 18:17:44 -0500 Subject: [PATCH 02/11] feat: add view course test --- .github/workflows/smoke-tests.yml | 40 +++++++++++++++++++++ scripts/smoke-tests/test_view_course.py | 48 +++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 scripts/smoke-tests/test_view_course.py diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index c53d4ba..1a827d7 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -15,6 +15,11 @@ on: description: "Test user password" required: true type: string + course_id: + description: "Course ID to test viewing the course page" + required: true + type: string + jobs: login-user: @@ -50,3 +55,38 @@ jobs: --base-url "${{ inputs.target_url }}" \ --email "${{ inputs.user_email }}" \ --password "${{ inputs.user_password }}" + + view-course: + name: View Course Page Test + runs-on: ubuntu-latest + + steps: + - name: Checkout actions-hub repo + uses: actions/checkout@v4 + with: + repository: nelc/actions-hub + ref: and/add-basic-smoke-tests + path: actions-hub + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + pip install pytest-playwright + playwright install + + - name: Hide the inputs values to keep them private in the logs + uses: levibostian/action-hide-sensitive-inputs@v1 + with: + exclude_inputs: target_url,course_id + + - name: Run view-course test + run: | + python actions-hub/scripts/smoke-tests/test_view_course.py \ + --base-url "${{ inputs.target_url }}" \ + --email "${{ inputs.user_email }}" \ + --password "${{ inputs.user_password }}" \ + --course-id "${{ inputs.course_id }}" diff --git a/scripts/smoke-tests/test_view_course.py b/scripts/smoke-tests/test_view_course.py new file mode 100644 index 0000000..553eff3 --- /dev/null +++ b/scripts/smoke-tests/test_view_course.py @@ -0,0 +1,48 @@ +import argparse + +from playwright.sync_api import expect, sync_playwright +from utils import login_user + + +def test_view_course(base_url: str, course_id: str, email: str, password: str): + """ + Smoke test to verify that a course page is accessible and loads key content. + + Args: + base_url (str): The base URL of the platform. + course_id (str): The course identifier. + email (str): The user email to log in. + password (str): The user password to log in. + """ + with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page() + + login_user(page, base_url, email, password) + + course_url = f"{base_url}/learning/course/{course_id}/home" + page.wait_for_load_state("networkidle") + page.goto(course_url) + + expect(page).to_have_url(course_url) + expect(page.locator("ol#courseHome-outline li").first).to_be_visible() + assert page.locator("ol#courseHome-outline li").count() > 0 + assert page.locator("div#courseTabsNavigation .container-xl nav a").count() > 1 + + browser.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Smoke test: View Course Page") + parser.add_argument("--base-url", required=True, help="Base URL of the platform") + parser.add_argument("--course-id", required=True, help="Course ID to visit") + parser.add_argument("--email", required=True, help="User email for login") + parser.add_argument("--password", required=True, help="User password for login") + args = parser.parse_args() + + test_view_course( + base_url=args.base_url, + course_id=args.course_id, + email=args.email, + password=args.password, + ) From e3203ac9e7a5bd6bca910421f69f44172d090721 Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Mon, 22 Sep 2025 16:18:17 -0500 Subject: [PATCH 03/11] feat: add input to control execution tests --- .github/workflows/smoke-tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 1a827d7..a736462 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -3,6 +3,10 @@ name: Smoke Tests on: workflow_call: inputs: + run_test: + description: 'Select desired test' + required: true + type: string target_url: description: "Base URL for the application" required: true @@ -23,6 +27,7 @@ on: jobs: login-user: + if: ${{ inputs.run_test == 'All' || inputs.run_test == 'login-user'}} name: Login User Test runs-on: ubuntu-latest @@ -57,6 +62,7 @@ jobs: --password "${{ inputs.user_password }}" view-course: + if: ${{ inputs.run_test == 'All' || inputs.run_test == 'view-course'}} name: View Course Page Test runs-on: ubuntu-latest From b28eae3af82ab02919d02cfc184500ff798027b3 Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Mon, 22 Sep 2025 16:38:33 -0500 Subject: [PATCH 04/11] feat: add check_heartbeat test --- .github/workflows/smoke-tests.yml | 30 ++++++++++++++++++++++- scripts/smoke-tests/test_heartbeat.py | 34 +++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 scripts/smoke-tests/test_heartbeat.py diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index a736462..be2100b 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -3,7 +3,7 @@ name: Smoke Tests on: workflow_call: inputs: - run_test: + run_test: description: 'Select desired test' required: true type: string @@ -26,6 +26,34 @@ on: jobs: + check-heartbeat: + if: ${{ inputs.run_test == 'All' || inputs.run_test == 'check-heartbeat'}} + name: Check Heartbeat + runs-on: ubuntu-latest + + steps: + - name: Checkout actions-hub repo + uses: actions/checkout@v4 + with: + repository: nelc/actions-hub + ref: and/add-basic-smoke-tests + path: actions-hub + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + pip install pytest-playwright + playwright install + + - name: Run heartbeat test + run: | + python actions-hub/scripts/smoke-tests/test_heartbeat.py \ + --base-url "${{ inputs.target_url }}" + login-user: if: ${{ inputs.run_test == 'All' || inputs.run_test == 'login-user'}} name: Login User Test diff --git a/scripts/smoke-tests/test_heartbeat.py b/scripts/smoke-tests/test_heartbeat.py new file mode 100644 index 0000000..1117362 --- /dev/null +++ b/scripts/smoke-tests/test_heartbeat.py @@ -0,0 +1,34 @@ +import argparse + +from playwright.sync_api import sync_playwright + + +def test_heartbeat(base_url: str): + """ + Smoke test to verify that the /heartbeat endpoint is reachable and returns HTTP 200. + + Args: + base_url (str): The base URL of the platform. + """ + with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page() + + response = page.goto(f"{base_url.rstrip('/')}/heartbeat") + + if not response or response.status != 200: + raise AssertionError( + f"❌ Heartbeat check failed. Status: {response.status if response else 'No response'}" + ) + + print(f"✅ Heartbeat responded with HTTP {response.status}") + + browser.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Smoke test: Heartbeat Endpoint") + parser.add_argument("--base-url", required=True, help="Base URL of the platform") + args = parser.parse_args() + + test_heartbeat(base_url=args.base_url) From e6daeefb0d20e88968fd507f1c58a11423570599 Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Mon, 22 Sep 2025 17:30:10 -0500 Subject: [PATCH 05/11] feat: add validate plugins job --- .github/workflows/smoke-tests.yml | 33 +++++++++++++++++++ scripts/smoke-tests/test_eox_plugins.py | 44 +++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 scripts/smoke-tests/test_eox_plugins.py diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index be2100b..3cca135 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -23,6 +23,10 @@ on: description: "Course ID to test viewing the course page" required: true type: string + target_plugins: + description: "Desired plugins to validate" + required: true + type: string jobs: @@ -54,6 +58,35 @@ jobs: python actions-hub/scripts/smoke-tests/test_heartbeat.py \ --base-url "${{ inputs.target_url }}" + validate-plugins: + if: ${{ inputs.run_test == 'All' || inputs.run_test == 'validate-plugins'}} + name: Validate eox plugins + runs-on: ubuntu-latest + + steps: + - name: Checkout actions-hub repo + uses: actions/checkout@v4 + with: + repository: nelc/actions-hub + ref: and/add-basic-smoke-tests + path: actions-hub + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + pip install pytest-playwright + playwright install + + - name: Validate eox plugins + run: | + python actions-hub/scripts/smoke-tests/test_eox_plugins.py \ + --base-url "${{ inputs.target_url }}" \ + --plugins ${{ inputs.target_plugins }} + login-user: if: ${{ inputs.run_test == 'All' || inputs.run_test == 'login-user'}} name: Login User Test diff --git a/scripts/smoke-tests/test_eox_plugins.py b/scripts/smoke-tests/test_eox_plugins.py new file mode 100644 index 0000000..cf1164c --- /dev/null +++ b/scripts/smoke-tests/test_eox_plugins.py @@ -0,0 +1,44 @@ +import argparse + +from playwright.sync_api import sync_playwright + + +def test_plugin_versions(base_url: str, plugins: list[str]) -> None: + """ + Smoke test to verify that plugin info endpoints return a valid JSON with version info. + + Args: + base_url (str): The base URL of the LMS. + plugins (list[str]): List of plugins to check. + """ + with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page() + + for plugin in plugins: + full_url = f"{base_url.rstrip('/')}/{plugin}/eox-info" + print(f"→ Checking {full_url}...") + + response = page.goto(full_url) + assert response is not None and response.ok, f"❌ Failed to load {full_url}" + + try: + json_data = response.json() + except Exception as e: + raise AssertionError(f"❌ Invalid JSON response from {full_url}: {e}") + + version = json_data.get("version") + assert version, f"❌ 'version' key not found in JSON from {full_url}" + + print(f"✅ {plugin} version: {version}") + + browser.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Smoke test: Validate eox plugins") + parser.add_argument("--base-url", required=True, help="Base LMS URL") + parser.add_argument("--plugins", nargs="+", required=True, help="List of plugin to check") + args = parser.parse_args() + + test_plugin_versions(base_url=args.base_url, plugins=args.plugins) From c779f7e37730b41af1a20b6e198eb15010169e99 Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Tue, 23 Sep 2025 12:59:10 -0500 Subject: [PATCH 06/11] feat: add complete multiplechoice unit test --- .github/workflows/smoke-tests.yml | 41 ++++++++++ .../test_complete_multiplechoice_unit.py | 75 +++++++++++++++++++ scripts/smoke-tests/test_view_course.py | 1 - scripts/smoke-tests/utils.py | 1 + 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 scripts/smoke-tests/test_complete_multiplechoice_unit.py diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 3cca135..d6af5bc 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -27,6 +27,10 @@ on: description: "Desired plugins to validate" required: true type: string + unit_id: + description: "Unit ID to test complete-multiplechoice-unit" + required: true + type: string jobs: @@ -157,3 +161,40 @@ jobs: --email "${{ inputs.user_email }}" \ --password "${{ inputs.user_password }}" \ --course-id "${{ inputs.course_id }}" + + complete-multiplechoice-unit: + if: ${{ inputs.run_test == 'All' || inputs.run_test == 'complete-multiplechoice-unit'}} + name: Complete multiplechoice unit test + runs-on: ubuntu-latest + + steps: + - name: Checkout actions-hub repo + uses: actions/checkout@v4 + with: + repository: nelc/actions-hub + ref: and/add-basic-smoke-tests + path: actions-hub + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + pip install pytest-playwright + playwright install + + - name: Hide the inputs values to keep them private in the logs + uses: levibostian/action-hide-sensitive-inputs@v1 + with: + exclude_inputs: target_url,course_id + + - name: Run complete-multiplechoice-unit test + run: | + python actions-hub/scripts/smoke-tests/test_complete_multiplechoice_unit.py \ + --base-url "${{ inputs.target_url }}" \ + --email "${{ inputs.user_email }}" \ + --password "${{ inputs.user_password }}" \ + --course-id "${{ inputs.course_id }}" \ + --unit-id "${{ inputs.unit_id }}" diff --git a/scripts/smoke-tests/test_complete_multiplechoice_unit.py b/scripts/smoke-tests/test_complete_multiplechoice_unit.py new file mode 100644 index 0000000..b41f345 --- /dev/null +++ b/scripts/smoke-tests/test_complete_multiplechoice_unit.py @@ -0,0 +1,75 @@ +import argparse + +from playwright.sync_api import TimeoutError, sync_playwright +from utils import login_user + + +def test_complete_multiplechoice_unit(base_url: str, course_id: str, unit_id: str, email: str, password: str): + """ + Smoke test to verify that a multiple choice unit can be completed. + + Args: + base_url (str): The base URL of the platform. + course_id (str): The course ID to navigate into. + unit_id (str): The specific unit ID (usage key). + email (str): Login email. + password (str): Login password. + """ + unit_url = f"{base_url}/courses/{course_id}/jump_to_id/{unit_id}" + + with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page() + login_user(page, base_url, email, password) + page.goto(unit_url) + page.wait_for_load_state("networkidle") + + # Try to find the iframe that contains the unit content + iframe = page.frame_locator("#unit-iframe") + assert iframe is not None, "No iframe found containing unit content" + + # Get the first problem + problems = iframe.locator("div.problem") + assert problems.count() > 0, "No problems found" + problem = problems.first + + # Get problem choices + radios = problem.locator("input[type='radio']") + assert radios.count() > 0, "No multiple choice options found" + + # Get Submit button + submit_button = problem.locator("button.submit") + assert submit_button.is_visible(), "Submit button not found" + + for radio in radios.all(): + radio.click() + submit_button.click() + notification = problem.locator(".notification.success.notification-submit") + + try: + notification.wait_for(state="visible", timeout=5000) + break + except TimeoutError: + continue + + assert notification.is_visible(), "Success notification isn't visible" + + browser.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Smoke test: Complete a multiple choice unit") + parser.add_argument("--base-url", required=True, help="Base URL of the LMS") + parser.add_argument("--course-id", required=True, help="Course ID") + parser.add_argument("--unit-id", required=True, help="Unit ID (usage key)") + parser.add_argument("--email", required=True, help="User email for login") + parser.add_argument("--password", required=True, help="User password for login") + args = parser.parse_args() + + test_complete_multiplechoice_unit( + base_url=args.base_url, + course_id=args.course_id, + unit_id=args.unit_id, + email=args.email, + password=args.password, + ) diff --git a/scripts/smoke-tests/test_view_course.py b/scripts/smoke-tests/test_view_course.py index 553eff3..683f7ee 100644 --- a/scripts/smoke-tests/test_view_course.py +++ b/scripts/smoke-tests/test_view_course.py @@ -21,7 +21,6 @@ def test_view_course(base_url: str, course_id: str, email: str, password: str): login_user(page, base_url, email, password) course_url = f"{base_url}/learning/course/{course_id}/home" - page.wait_for_load_state("networkidle") page.goto(course_url) expect(page).to_have_url(course_url) diff --git a/scripts/smoke-tests/utils.py b/scripts/smoke-tests/utils.py index bcfa625..9976e53 100644 --- a/scripts/smoke-tests/utils.py +++ b/scripts/smoke-tests/utils.py @@ -15,3 +15,4 @@ def login_user(page, base_url: str, email: str, password: str): page.fill("input[name='email']", email) page.fill("input[name='password']", password) page.click("button[type='submit']") + page.wait_for_load_state("networkidle") From b34fe6845b9d0648e6648ca3ff61b7c487a46e6a Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Tue, 23 Sep 2025 16:26:50 -0500 Subject: [PATCH 07/11] feat: add register user test --- .github/workflows/smoke-tests.yml | 33 +++++++ scripts/smoke-tests/test_register_user.py | 108 ++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 scripts/smoke-tests/test_register_user.py diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index d6af5bc..bb7a978 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -198,3 +198,36 @@ jobs: --password "${{ inputs.user_password }}" \ --course-id "${{ inputs.course_id }}" \ --unit-id "${{ inputs.unit_id }}" + + register-user: + if: ${{ inputs.run_test == 'All' || inputs.run_test == 'register-user'}} + name: Register user test + runs-on: ubuntu-latest + + steps: + - name: Checkout actions-hub repo + uses: actions/checkout@v4 + with: + repository: nelc/actions-hub + ref: and/add-basic-smoke-tests + path: actions-hub + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + pip install pytest-playwright + playwright install + + - name: Hide the inputs values to keep them private in the logs + uses: levibostian/action-hide-sensitive-inputs@v1 + with: + exclude_inputs: target_url,course_id + + - name: Run register-user test + run: | + python actions-hub/scripts/smoke-tests/test_register_user.py \ + --base-url "${{ inputs.target_url }}" diff --git a/scripts/smoke-tests/test_register_user.py b/scripts/smoke-tests/test_register_user.py new file mode 100644 index 0000000..69f120f --- /dev/null +++ b/scripts/smoke-tests/test_register_user.py @@ -0,0 +1,108 @@ +import argparse +import random +import string + +from playwright.sync_api import expect, sync_playwright + + +def random_string(length=6): + """ + Generate a random alphanumeric string of the given length. + + Args: + length (int): Length of the string to generate. Default is 6. + + Returns: + str: Random string composed of lowercase letters and digits. + """ + return "".join(random.choices(string.ascii_lowercase + string.digits, k=length)) + + +def fill_required_fields(page): + """ + Fills all required input and select fields within the registration form. + + It handles different input types (text, email, password) and selects the + first valid option for dropdown fields. + + Args: + page: A Playwright Page instance already loaded with the registration form. + """ + required_fields = page.locator(".required-fields > div") + + for field_index in range(required_fields.count()): + field = required_fields.nth(field_index) + + # Check if it's an input + if field.locator("input").count() > 0: + input_field = field.locator("input").first + input_name = input_field.get_attribute("name") + input_type = input_field.get_attribute("type") or "" + + if input_type == "email": + input_field.fill(f"testuser_{random_string()}@example.com") + elif input_type == "text" and input_name == "arabic_name": + input_field.fill("مستخدم اختبار") + elif input_type == "text" and input_name == "national_id": + input_field.fill(str(random.randint(4000000000, 9999999999))) + elif input_type == "text": + input_field.fill(f"Test{random_string()}") + elif input_type == "password": + input_field.fill("StrongPass123!") + elif input_type == "hidden": + continue + else: + input_field.fill("placeholder") + + # Check if it's a select dropdown + elif field.locator("select").count() > 0: + select_field = field.locator("select").first + options = select_field.locator("option") + + for option_index in range(options.count()): + value = options.nth(option_index).get_attribute("value") + if value and value != "": + select_field.select_option(value=value) + break + + +def test_register_user(base_url: str): + """ + Smoke test to register a new user on the Open edX platform. + + Navigates to the registration page, fills all required fields, + submits the form, and checks whether the registration was successful. + + Args: + base_url (str): The base URL of the LMS. + """ + with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + page = browser.new_page() + page.goto(f"{base_url}/register") + + # Fill required fields + fill_required_fields(page) + + # Submit form + page.locator("#register-form button[type='submit']").click() + + # Wait for redirection or error + page.wait_for_load_state("networkidle") + error_box = page.locator("div.js-form-errors.status.submission-error") + + if error_box.is_visible(): + error_text = error_box.locator("ul.message-copy").inner_text() + raise AssertionError(f"❌ Registration failed: {error_text}") + + expect(page).to_have_url(f"{base_url}/dashboard") + + browser.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Smoke test: User registration") + parser.add_argument("--base-url", required=True, help="Base LMS URL") + args = parser.parse_args() + + test_register_user(base_url=args.base_url) From e32ec59f28dcd4be8c6ff358dd558991079f6d0a Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Tue, 23 Sep 2025 16:45:30 -0500 Subject: [PATCH 08/11] fix: remove redundant quotes --- .github/workflows/smoke-tests.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index bb7a978..769dae8 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -4,31 +4,31 @@ on: workflow_call: inputs: run_test: - description: 'Select desired test' + description: Select desired test required: true type: string target_url: - description: "Base URL for the application" + description: Base URL for the application required: true type: string user_email: - description: "Test user email" + description: Test user email required: true type: string user_password: - description: "Test user password" + description: Test user password required: true type: string course_id: - description: "Course ID to test viewing the course page" + description: Course ID to test viewing the course page required: true type: string target_plugins: - description: "Desired plugins to validate" + description: Desired plugins to validate required: true type: string unit_id: - description: "Unit ID to test complete-multiplechoice-unit" + description: Unit ID to test complete-multiplechoice-unit required: true type: string From cad54438c6c2c8cff097025404bf3f8725d9c193 Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Wed, 24 Sep 2025 14:25:54 -0500 Subject: [PATCH 09/11] feat: set composite action --- .github/actions/setup-playwright/action.yml | 30 +++++++ .github/workflows/smoke-tests.yml | 86 +++------------------ 2 files changed, 42 insertions(+), 74 deletions(-) create mode 100644 .github/actions/setup-playwright/action.yml diff --git a/.github/actions/setup-playwright/action.yml b/.github/actions/setup-playwright/action.yml new file mode 100644 index 0000000..b1fb5d2 --- /dev/null +++ b/.github/actions/setup-playwright/action.yml @@ -0,0 +1,30 @@ +name: Setup Python + Playwright +description: Common setup for all Playwright-based smoke tests +inputs: + python-version: + description: Version of Python to use + required: false + default: "3.11" + exclude_inputs: + description: Comma-separated list of inputs to exclude from masking + required: false + default: target_url,course_id + +runs: + using: composite + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + + - name: Install dependencies + run: | + pip install pytest-playwright + playwright install + shell: bash + + - name: Hide the inputs values to keep them private in the logs + uses: levibostian/action-hide-sensitive-inputs@v1 + with: + exclude_inputs: ${{ inputs.exclude_inputs }} diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 769dae8..7c406dc 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -47,15 +47,8 @@ jobs: ref: and/add-basic-smoke-tests path: actions-hub - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install dependencies - run: | - pip install pytest-playwright - playwright install + - name: Setup Python and Playwright + uses: ./actions-hub/.github/actions/setup-playwright - name: Run heartbeat test run: | @@ -75,15 +68,8 @@ jobs: ref: and/add-basic-smoke-tests path: actions-hub - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install dependencies - run: | - pip install pytest-playwright - playwright install + - name: Setup Python and Playwright + uses: ./actions-hub/.github/actions/setup-playwright - name: Validate eox plugins run: | @@ -104,20 +90,8 @@ jobs: ref: and/add-basic-smoke-tests path: actions-hub - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install dependencies - run: | - pip install pytest-playwright - playwright install - - - name: Hide the inputs values to keep them private in the logs when running this workflow - uses: levibostian/action-hide-sensitive-inputs@v1 - with: - exclude_inputs: target_url + - name: Setup Python and Playwright + uses: ./actions-hub/.github/actions/setup-playwright - name: Run login-user test run: | @@ -139,20 +113,8 @@ jobs: ref: and/add-basic-smoke-tests path: actions-hub - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install dependencies - run: | - pip install pytest-playwright - playwright install - - - name: Hide the inputs values to keep them private in the logs - uses: levibostian/action-hide-sensitive-inputs@v1 - with: - exclude_inputs: target_url,course_id + - name: Setup Python and Playwright + uses: ./actions-hub/.github/actions/setup-playwright - name: Run view-course test run: | @@ -175,20 +137,8 @@ jobs: ref: and/add-basic-smoke-tests path: actions-hub - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install dependencies - run: | - pip install pytest-playwright - playwright install - - - name: Hide the inputs values to keep them private in the logs - uses: levibostian/action-hide-sensitive-inputs@v1 - with: - exclude_inputs: target_url,course_id + - name: Setup Python and Playwright + uses: ./actions-hub/.github/actions/setup-playwright - name: Run complete-multiplechoice-unit test run: | @@ -212,20 +162,8 @@ jobs: ref: and/add-basic-smoke-tests path: actions-hub - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install dependencies - run: | - pip install pytest-playwright - playwright install - - - name: Hide the inputs values to keep them private in the logs - uses: levibostian/action-hide-sensitive-inputs@v1 - with: - exclude_inputs: target_url,course_id + - name: Setup Python and Playwright + uses: ./actions-hub/.github/actions/setup-playwright - name: Run register-user test run: | From 057d4849f66eff7902cea3c708d27b5839da3a02 Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Wed, 24 Sep 2025 17:32:00 -0500 Subject: [PATCH 10/11] feat: add cache for dependencies --- .github/actions/setup-playwright/action.yml | 15 ++++++++++++++- .github/actions/setup-playwright/requirements.txt | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .github/actions/setup-playwright/requirements.txt diff --git a/.github/actions/setup-playwright/action.yml b/.github/actions/setup-playwright/action.yml index b1fb5d2..c48baf1 100644 --- a/.github/actions/setup-playwright/action.yml +++ b/.github/actions/setup-playwright/action.yml @@ -13,6 +13,19 @@ inputs: runs: using: composite steps: + - name: Cache Python & Playwright dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cache/pip + ~/.cache/ms-playwright + key: > + ${{ runner.os }}-py-${{ inputs.python-version }}-playwright-${{ + hashFiles('actions-hub/.github/actions/setup-playwright/requirements.txt') + }} + restore-keys: | + ${{ runner.os }}-py-${{ inputs.python-version }}-playwright + - name: Set up Python uses: actions/setup-python@v5 with: @@ -20,7 +33,7 @@ runs: - name: Install dependencies run: | - pip install pytest-playwright + pip install -r ./actions-hub/.github/actions/setup-playwright/requirements.txt playwright install shell: bash diff --git a/.github/actions/setup-playwright/requirements.txt b/.github/actions/setup-playwright/requirements.txt new file mode 100644 index 0000000..801cd51 --- /dev/null +++ b/.github/actions/setup-playwright/requirements.txt @@ -0,0 +1 @@ +pytest-playwright From a8fcad7a0eff085d33570592beea4b74082b2b1c Mon Sep 17 00:00:00 2001 From: andrey-canon Date: Thu, 25 Sep 2025 17:01:26 -0500 Subject: [PATCH 11/11] feat: set main branch --- .github/workflows/smoke-tests.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 7c406dc..6ce80d4 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -44,7 +44,7 @@ jobs: uses: actions/checkout@v4 with: repository: nelc/actions-hub - ref: and/add-basic-smoke-tests + ref: main path: actions-hub - name: Setup Python and Playwright @@ -65,7 +65,7 @@ jobs: uses: actions/checkout@v4 with: repository: nelc/actions-hub - ref: and/add-basic-smoke-tests + ref: main path: actions-hub - name: Setup Python and Playwright @@ -87,7 +87,7 @@ jobs: uses: actions/checkout@v4 with: repository: nelc/actions-hub - ref: and/add-basic-smoke-tests + ref: main path: actions-hub - name: Setup Python and Playwright @@ -110,7 +110,7 @@ jobs: uses: actions/checkout@v4 with: repository: nelc/actions-hub - ref: and/add-basic-smoke-tests + ref: main path: actions-hub - name: Setup Python and Playwright @@ -134,7 +134,7 @@ jobs: uses: actions/checkout@v4 with: repository: nelc/actions-hub - ref: and/add-basic-smoke-tests + ref: main path: actions-hub - name: Setup Python and Playwright @@ -159,7 +159,7 @@ jobs: uses: actions/checkout@v4 with: repository: nelc/actions-hub - ref: and/add-basic-smoke-tests + ref: main path: actions-hub - name: Setup Python and Playwright