From 49e7ab3f9e703b71f4a4d16805ac3c3337d515a5 Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Sat, 20 Jun 2026 09:31:39 +0200 Subject: [PATCH 1/9] ci: run Playwright e2e against shopware-demo-environment Boots the FriendsOfShopware demo container (6.7), compiles the plugin admin assets with shopware-cli, installs+activates the plugin into the container, and runs the Playwright suite against it. Also make the a11y spec wait until the async range-select labeling settles before scanning (avoids a false select-name violation on slower CI machines), and track tests/package-lock.json so 'npm ci' works. --- .github/workflows/playwright.yml | 101 ++++ .gitignore | 1 + tests/.gitignore | 1 - tests/e2e/accessibility.spec.ts | 11 + tests/package-lock.json | 862 +++++++++++++++++++++++++++++++ 5 files changed, 975 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 tests/package-lock.json diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..65a228c --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,101 @@ +name: Playwright E2E + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +jobs: + e2e: + runs-on: ubuntu-latest + timeout-minutes: 30 + + env: + APP_URL: http://localhost:8000 + SHOPWARE_ADMIN_USERNAME: admin + SHOPWARE_ADMIN_PASSWORD: shopware + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # Compile the plugin's administration assets so the demo container can + # serve them (the container ships a prebuilt admin without our plugin). + - name: Setup shopware-cli + uses: shopware/shopware-cli-action@v3 + + - name: Build plugin administration assets + run: shopware-cli extension build . + + - name: Start Shopware demo environment + run: | + docker run -d --name shopware \ + -e APP_URL="${APP_URL}" \ + -e SHOPWARE_ADMIN_PASSWORD="${SHOPWARE_ADMIN_PASSWORD}" \ + -p 8000:8000 \ + ghcr.io/friendsofshopware/shopware-demo-environment:6.7 + + - name: Wait for Shopware to be ready + run: | + for i in $(seq 1 60); do + code=$(curl -s -o /dev/null -w "%{http_code}" "${APP_URL}/admin" || true) + echo "attempt ${i}: HTTP ${code}" + if [ "${code}" = "200" ]; then exit 0; fi + sleep 5 + done + echo "Shopware did not become ready in time" >&2 + docker logs shopware || true + exit 1 + + # Copy the plugin (incl. the freshly compiled Resources/public) into the + # container and install + activate it. + - name: Install the plugin into the container + run: | + # Stage the plugin without test/CI cruft so we don't ship node_modules + # or the git tree into the container (the compiled Resources/public, + # built above, is included). + mkdir -p /tmp/plugin/FroshAdminDashboard + rsync -a \ + --exclude='.git' \ + --exclude='tests/node_modules' \ + --exclude='tests/test-results' \ + --exclude='tests/playwright-report' \ + ./ /tmp/plugin/FroshAdminDashboard/ + docker exec shopware mkdir -p /var/www/html/custom/plugins + docker cp /tmp/plugin/FroshAdminDashboard shopware:/var/www/html/custom/plugins/FroshAdminDashboard + docker exec -w /var/www/html shopware php bin/console plugin:refresh + docker exec -w /var/www/html shopware php bin/console plugin:install --activate FroshAdminDashboard + docker exec -w /var/www/html shopware php bin/console assets:install + docker exec -w /var/www/html shopware php bin/console cache:clear + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: tests/package-lock.json + + - name: Install test dependencies + working-directory: tests + run: npm ci + + - name: Install Playwright browser + working-directory: tests + run: npx playwright install --with-deps chromium + + - name: Run Playwright tests + working-directory: tests + run: npx playwright test + + - name: Upload Playwright report + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: tests/playwright-report + retention-days: 14 + + - name: Dump Shopware logs on failure + if: failure() + run: docker logs shopware || true diff --git a/.gitignore b/.gitignore index 27b6643..a61ebc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /vendor/ /node_modules/ /src/Resources/public/ +/src/Storefront/Resources/public/ *.log .DS_Store .idea/ diff --git a/tests/.gitignore b/tests/.gitignore index 2e2c064..c86f571 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,5 +1,4 @@ node_modules/ -package-lock.json .env /playwright-report/ /test-results/ diff --git a/tests/e2e/accessibility.spec.ts b/tests/e2e/accessibility.spec.ts index 3839cb7..96968c0 100644 --- a/tests/e2e/accessibility.spec.ts +++ b/tests/e2e/accessibility.spec.ts @@ -12,6 +12,17 @@ test.describe('Frosh Admin Dashboard — accessibility', () => { const dashboard = new DashboardPage(AdminPage); await dashboard.goto(); + // The core sw-chart-card renders its range only once data has - // loaded, and the widget then labels it asynchronously (the runtime - // workaround for the unlabelled core select). Wait until every rendered - // select has its accessible name before scanning, otherwise axe can race - // the labeling on slower (CI) machines and report a false select-name. - await expect - .poll(async () => - AdminPage.locator('.frosh-dashboard-grid select:not([aria-label]):not([aria-labelledby])').count(), - ) - .toBe(0); + // The default dashboard renders two time-series widgets (Total sales, + // Number of orders). Their core sw-chart-card range