From 82385e8228de09660569d911db73cc0b2753caa1 Mon Sep 17 00:00:00 2001 From: Rom1-B <8530352+Rom1-B@users.noreply.github.com> Date: Wed, 19 Aug 2026 10:26:03 +0200 Subject: [PATCH 1/4] Feat: add e2e tests workflow with Playwright support --- .github/workflows/e2e-tests.yml | 133 ++++++++++++++++++++++++++++++++ README.md | 55 +++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 .github/workflows/e2e-tests.yml diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 0000000..074c714 --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,133 @@ +# Reusable Playwright e2e workflow, kept separate from `continuous-integration.yml` +# because it needs a dedicated e2e_testing database/environment and browser +# install, which the CI workflow does not provide. +name: "End-to-end tests" + +on: + workflow_call: + inputs: + plugin-key: + required: true + type: string + glpi-version: + required: true + type: string + php-version: + required: true + type: string + db-image: + required: true + type: string + +jobs: + e2e: + name: "Playwright" + runs-on: "ubuntu-latest" + container: + image: "ghcr.io/glpi-project/githubactions-glpi-apache:php-${{ inputs.php-version }}-glpi-${{ inputs.glpi-version }}" + # `--user github-actions-runner` is mandatory to prevent rights issues on mounted volume during checkout operation + options: >- + --volume ${{ github.workspace }}:/var/www/glpi/plugins:rw + --user github-actions-runner + services: + db: + image: "ghcr.io/glpi-project/githubactions-${{ inputs.db-image }}" + env: + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + MYSQL_DATABASE: "glpi_e2e" + options: >- + --shm-size=1g + defaults: + run: + # By default, execute commands using the `www-data` user to prevent rights issues on GLPI generated files. + shell: "sudo --set-home --user=www-data --preserve-env bash --noprofile --norc -eo pipefail {0}" + working-directory: "/var/www/glpi" + steps: + - name: "Checkout" + uses: "actions/checkout@v7" + with: + path: "${{ inputs.plugin-key }}" + + - name: "Fix plugin directory ownership" + # Use default `bash` shell with `github-actions-runner` user, the only + # one allowed passwordless sudo. + shell: "bash" + run: | + sudo setfacl --recursive --modify u:www-data:rwx "/var/www/glpi/plugins/${{ inputs.plugin-key }}" + + - name: "Mark plugin directory as safe for git" + run: | + git config --global --add safe.directory "/var/www/glpi/plugins/${{ inputs.plugin-key }}" + + - name: "Restore composer cache" + uses: "actions/cache@v6" + with: + path: "/var/www/glpi/plugins/${{ inputs.plugin-key }}/vendor" + key: "${{ inputs.plugin-key }}-composer-${{ hashFiles(format('{0}/composer.lock', inputs.plugin-key)) }}" + + - name: "Install composer dependencies" + working-directory: "/var/www/glpi/plugins/${{ inputs.plugin-key }}" + run: | + composer install --ansi --no-interaction --no-progress --prefer-dist + + - name: "Install e2e database" + # Port 8090 is the vhost bound to GLPI_ENVIRONMENT_TYPE=e2e_testing in + # this image; port 80 serves a different (unconfigured) environment. + run: | + bin/console database:install --ansi --no-interaction --force --reconfigure --no-telemetry \ + --db-host=db --db-name=glpi_e2e --db-user=root --db-password="" \ + --env=e2e_testing + bin/console config:set url_base "http://localhost:8090" --env=e2e_testing + bin/console config:set url_base_api "http://localhost:8090/apirest.php" --env=e2e_testing + + - name: "Install and activate plugin" + run: | + bin/console plugin:install --ansi --no-interaction --username=glpi "${{ inputs.plugin-key }}" --env=e2e_testing + bin/console plugin:activate --ansi --no-interaction "${{ inputs.plugin-key }}" --env=e2e_testing + + - name: "Run apache" + # Use default `bash` shell with `github-actions-runner` user + shell: "bash" + run: | + sudo service apache2 start + + - name: "Read installed Playwright version" + id: "playwright-version" + # Use default `bash` shell with `github-actions-runner` user: only this + # user can write to the runner-owned $GITHUB_OUTPUT file. + shell: "bash" + run: | + echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT" + + - name: "Restore Playwright browser cache" + id: "playwright-cache" + uses: "actions/cache@v6" + with: + path: "/home/www-data/.cache/ms-playwright" + key: "playwright-chromium-${{ steps.playwright-version.outputs.version }}" + + - name: "Install Playwright browser" + if: "steps.playwright-cache.outputs.cache-hit != 'true'" + run: | + npx playwright install chromium + + - name: "Install Playwright system dependencies" + # Use default `bash` shell with `github-actions-runner` user: this + # command needs passwordless sudo to install system packages as root. + shell: "bash" + run: | + npx playwright install-deps chromium + + - name: "Playwright" + env: + E2E_BASE_URL: "http://localhost:8090" + run: | + npx playwright test --project="plugin:${{ inputs.plugin-key }}" --reporter=html + + - name: "Upload Playwright report" + if: ${{ !cancelled() }} + uses: "actions/upload-artifact@v7" + with: + name: "playwright-report" + path: "/var/www/glpi/tests/e2e/results" + retention-days: 7 diff --git a/README.md b/README.md index d9951ba..bce6e8a 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,48 @@ On pull requests, the workflow checks that the `CHANGELOG` file has been updated On pull requests that modify `plugin.xml` or `.xml`, the workflow also validates that all URLs declared in the file are reachable. URLs inside `` tags that are newly introduced by the PR only produce a warning (the release archive may not be published yet), while all other invalid URLs fail the check. +## End-to-end tests workflow + +This workflow runs the plugin's Playwright specs (`tests/e2e/specs`) against a dedicated `e2e_testing` GLPI environment. +It is kept separate from the `continuous-integration.yml` workflow because it needs its own database and a browser install, which the CI workflow does not provide. + +```yaml +name: "End-to-end tests" + +on: + push: + branches: + - "main" + tags: + - "*" + pull_request: + workflow_dispatch: + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + e2e: + uses: "glpi-project/plugin-ci-workflows/.github/workflows/e2e-tests.yml@v1" + with: + # The plugin key (system name). + plugin-key: "myplugin" + + # The version of GLPI on which to run the tests. + glpi-version: "12.0.x" + + # The version of PHP on which to run the tests. + php-version: "8.3" + + # The database docker image on which to run the tests. + db-image: "mariadb:10.6" +``` + +See the [Continuous integration workflow](#continuous-integration-workflow) section above for the available `glpi-version`/`php-version`/`db-image` combinations. + +The plugin specs are auto-discovered by GLPI's own `playwright.config.ts` as the `plugin:` project, so no plugin-side Playwright configuration is required beyond the `tests/e2e/specs` directory. + ## Code coverage Code coverage is automatically enabled when a `.glpi-coverage.json` configuration file is present at the root of the plugin directory. @@ -178,6 +220,19 @@ jobs: php-version: "${{ matrix.php-version }}" db-image: "${{ matrix.db-image }}" + e2e: + name: "Playwright - GLPI ${{ matrix.glpi-version }} - php:${{ matrix.php-version }} - ${{ matrix.db-image }}" + needs: "generate-ci-matrix" + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.generate-ci-matrix.outputs.matrix) }} + uses: "glpi-project/plugin-ci-workflows/.github/workflows/e2e-tests.yml@v1" + with: + plugin-key: "myplugin" + glpi-version: "${{ matrix.glpi-version }}" + php-version: "${{ matrix.php-version }}" + db-image: "${{ matrix.db-image }}" + coverage-report: if: github.event_name == 'pull_request' needs: "ci" From e355e2ab6260be3ba6da0c59ed26dac07ba77121 Mon Sep 17 00:00:00 2001 From: Rom1-B <8530352+Rom1-B@users.noreply.github.com> Date: Thu, 20 Aug 2026 10:50:12 +0200 Subject: [PATCH 2/4] review --- .github/workflows/e2e-tests.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 074c714..50016d2 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -43,6 +43,11 @@ jobs: shell: "sudo --set-home --user=www-data --preserve-env bash --noprofile --norc -eo pipefail {0}" working-directory: "/var/www/glpi" steps: + - name: "Validate plugin-key" + shell: "bash" + run: | + [[ "${{ inputs.plugin-key }}" =~ ^[a-z][a-z0-9_-]*$ ]] || { echo "Invalid plugin-key"; exit 1; } + - name: "Checkout" uses: "actions/checkout@v7" with: @@ -108,8 +113,10 @@ jobs: - name: "Install Playwright browser" if: "steps.playwright-cache.outputs.cache-hit != 'true'" + # Cache save runs as a different user than www-data; must be world-readable. run: | npx playwright install chromium + chmod -R a+rX "$HOME/.cache/ms-playwright" - name: "Install Playwright system dependencies" # Use default `bash` shell with `github-actions-runner` user: this @@ -129,5 +136,5 @@ jobs: uses: "actions/upload-artifact@v7" with: name: "playwright-report" - path: "/var/www/glpi/tests/e2e/results" + path: "/var/www/glpi/playwright-report" retention-days: 7 From a15821259ac58f4c7ddbef5b0eb3e7f733b334ac Mon Sep 17 00:00:00 2001 From: Rom1-B <8530352+Rom1-B@users.noreply.github.com> Date: Fri, 21 Aug 2026 12:10:45 +0200 Subject: [PATCH 3/4] adrien --- .github/workflows/e2e-tests.yml | 60 ++++++++++++++++----------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 50016d2..10ecb1c 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -42,6 +42,8 @@ jobs: # By default, execute commands using the `www-data` user to prevent rights issues on GLPI generated files. shell: "sudo --set-home --user=www-data --preserve-env bash --noprofile --norc -eo pipefail {0}" working-directory: "/var/www/glpi" + env: + CACHE_DIR: "/home/www-data/.cache/e2e-tests-workflow" steps: - name: "Validate plugin-key" shell: "bash" @@ -53,24 +55,38 @@ jobs: with: path: "${{ inputs.plugin-key }}" - - name: "Fix plugin directory ownership" - # Use default `bash` shell with `github-actions-runner` user, the only - # one allowed passwordless sudo. + - name: "Configure cache directories" + # sudo needs the `github-actions-runner` user (only one with passwordless sudo). shell: "bash" run: | + sudo mkdir --parents "${{ env.CACHE_DIR }}/composer" + sudo chown --recursive github-actions-runner "${{ env.CACHE_DIR }}" + composer config cache-dir "${{ env.CACHE_DIR }}/composer" + + - name: "Restore cache" + uses: "actions/cache@v6" + with: + path: | + ${{ env.CACHE_DIR }} + key: "${{ inputs.plugin-key }}-dependencies-${{ inputs.php-version }}-${{ github.job }}-${{ hashFiles(format('{0}/composer.lock', inputs.plugin-key)) }}" + restore-keys: | + ${{ inputs.plugin-key }}-dependencies-${{ inputs.php-version }}-${{ github.job }}- + ${{ inputs.plugin-key }}-dependencies-${{ inputs.php-version }}- + ${{ inputs.plugin-key }}-dependencies- + + - name: "Fix directories ACL" + # Runs after cache restore so cache-extracted files (owned by github-actions-runner) get the ACL too. + shell: "bash" + run: | + sudo setfacl --recursive --modify u:www-data:rwx "${{ env.CACHE_DIR }}" sudo setfacl --recursive --modify u:www-data:rwx "/var/www/glpi/plugins/${{ inputs.plugin-key }}" - name: "Mark plugin directory as safe for git" run: | git config --global --add safe.directory "/var/www/glpi/plugins/${{ inputs.plugin-key }}" - - name: "Restore composer cache" - uses: "actions/cache@v6" - with: - path: "/var/www/glpi/plugins/${{ inputs.plugin-key }}/vendor" - key: "${{ inputs.plugin-key }}-composer-${{ hashFiles(format('{0}/composer.lock', inputs.plugin-key)) }}" - - name: "Install composer dependencies" + if: ${{ hashFiles(format('{0}/composer.json', inputs.plugin-key)) != '' }} working-directory: "/var/www/glpi/plugins/${{ inputs.plugin-key }}" run: | composer install --ansi --no-interaction --no-progress --prefer-dist @@ -91,36 +107,18 @@ jobs: bin/console plugin:activate --ansi --no-interaction "${{ inputs.plugin-key }}" --env=e2e_testing - name: "Run apache" - # Use default `bash` shell with `github-actions-runner` user shell: "bash" run: | sudo service apache2 start - - name: "Read installed Playwright version" - id: "playwright-version" - # Use default `bash` shell with `github-actions-runner` user: only this - # user can write to the runner-owned $GITHUB_OUTPUT file. - shell: "bash" - run: | - echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT" - - - name: "Restore Playwright browser cache" - id: "playwright-cache" - uses: "actions/cache@v6" - with: - path: "/home/www-data/.cache/ms-playwright" - key: "playwright-chromium-${{ steps.playwright-version.outputs.version }}" - - name: "Install Playwright browser" - if: "steps.playwright-cache.outputs.cache-hit != 'true'" - # Cache save runs as a different user than www-data; must be world-readable. + # Not cached: Playwright recommends against it (restore time ≈ download time), + # and OS deps below are reinstalled on every run regardless. run: | npx playwright install chromium - chmod -R a+rX "$HOME/.cache/ms-playwright" - name: "Install Playwright system dependencies" - # Use default `bash` shell with `github-actions-runner` user: this - # command needs passwordless sudo to install system packages as root. + # Needs passwordless sudo to install system packages as root. shell: "bash" run: | npx playwright install-deps chromium @@ -135,6 +133,6 @@ jobs: if: ${{ !cancelled() }} uses: "actions/upload-artifact@v7" with: - name: "playwright-report" + name: "playwright-report-${{ inputs.glpi-version }}-${{ inputs.php-version }}" path: "/var/www/glpi/playwright-report" retention-days: 7 From 85aca2d04edd79268eac206737139dea2df7f240 Mon Sep 17 00:00:00 2001 From: Rom1-B <8530352+Rom1-B@users.noreply.github.com> Date: Fri, 21 Aug 2026 12:25:48 +0200 Subject: [PATCH 4/4] fix --- .github/workflows/e2e-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 10ecb1c..4dd8351 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -57,7 +57,10 @@ jobs: - name: "Configure cache directories" # sudo needs the `github-actions-runner` user (only one with passwordless sudo). + # working-directory is the plugin dir so `composer config` writes to its local + # composer.json, readable regardless of which user runs later composer commands. shell: "bash" + working-directory: "/var/www/glpi/plugins/${{ inputs.plugin-key }}" run: | sudo mkdir --parents "${{ env.CACHE_DIR }}/composer" sudo chown --recursive github-actions-runner "${{ env.CACHE_DIR }}"