Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Playwright E2E

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 30

env:
# Playwright (the acceptance-test-suite reads SHOPWARE_ADMIN_*).
APP_URL: http://localhost:8000
SHOPWARE_ADMIN_USERNAME: admin
SHOPWARE_ADMIN_PASSWORD: shopware
# Consumed directly by shopware-cli for the Admin API upload — no config
# file needed (these take precedence over any .shopware-project.yml).
SHOPWARE_CLI_API_URL: http://localhost:8000
SHOPWARE_CLI_API_USERNAME: admin
SHOPWARE_CLI_API_PASSWORD: shopware

# The demo environment runs as a service container. Its baked-in default
# APP_URL is already http://localhost:8000, matching what we test against, so
# no overrides are needed. The image has no Docker HEALTHCHECK, so readiness
# is polled in a step below rather than via `options: --health-*`.
services:
shopware:
image: ghcr.io/friendsofshopware/shopware-demo-environment:6.7
ports:
- 8000:8000

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup shopware-cli
uses: shopware/shopware-cli-action@v3

- name: Build extension zip
id: zip
uses: shopwareLabs/build-extension-zip-action@main
with:
output-directory: dist

- 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
exit 1

- name: Upload and activate the plugin
run: shopware-cli project extension upload dist/FroshAdminDashboard.zip --activate

- name: Setup Node
uses: actions/setup-node@v6
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@v7
with:
name: playwright-report
path: tests/playwright-report
retention-days: 14

- name: Dump Shopware logs on failure
if: failure()
run: |
# Service containers get a generated name; find it by image.
cid=$(docker ps -a --filter "ancestor=ghcr.io/friendsofshopware/shopware-demo-environment:6.7" --format '{{.ID}}' | head -n1)
if [ -n "$cid" ]; then docker logs "$cid"; fi
4 changes: 2 additions & 2 deletions .github/workflows/store-info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Install shopware-cli
uses: shopware/shopware-cli-action@v1
uses: shopware/shopware-cli-action@v3
- name: Build
shell: bash
run: shopware-cli account producer extension info push .
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/vendor/
/node_modules/
/src/Resources/public/
/src/Storefront/Resources/public/
/dist/
*.log
.DS_Store
.idea/
Expand Down
5 changes: 5 additions & 0 deletions .shopware-extension.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
compatibility_date: '2026-02-11'
build:
zip:
assets:
enable_asset_caching: true

store:
availabilities:
- German
Expand Down
1 change: 0 additions & 1 deletion tests/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
node_modules/
package-lock.json
.env
/playwright-report/
/test-results/
Expand Down
19 changes: 19 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,22 @@ npm run test:report # open the last HTML report

Selectors live in `pages/dashboard.page.ts` and use the plugin's BEM classes;
widget labels assume the English admin locale.

## Continuous integration

`.github/workflows/playwright.yml` runs the suite on every push/PR:

1. `shopware-cli extension zip` packages the plugin (compiling the admin assets).
2. The [shopware-demo-environment](https://github.com/FriendsOfShopware/shopware-demo-environment)
container is started on `http://localhost:8000`.
3. `shopware-cli project extension upload dist/FroshAdminDashboard.zip --activate`
installs the plugin through the Admin API. The connection is taken straight
from the `SHOPWARE_CLI_API_URL`, `SHOPWARE_CLI_API_USERNAME` and
`SHOPWARE_CLI_API_PASSWORD` environment variables — no config file or
committed credentials needed.
4. Playwright runs against the container and the HTML report is uploaded.

You can reproduce this locally: start the demo container, then from the plugin
root run `SHOPWARE_CLI_API_URL=… SHOPWARE_CLI_API_USERNAME=admin
SHOPWARE_CLI_API_PASSWORD=shopware shopware-cli project extension upload . --activate`.

12 changes: 12 additions & 0 deletions tests/e2e/accessibility.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ test.describe('Frosh Admin Dashboard — accessibility', () => {
const dashboard = new DashboardPage(AdminPage);
await dashboard.goto();

// The default dashboard renders two time-series widgets (Total sales,
// Number of orders). Their core sw-chart-card range <select> appears only
// once data has loaded, and the widget then labels it asynchronously
// (the runtime workaround for the unlabelled core select). Wait until both
// selects exist AND are labelled before scanning — checking only
// "no unlabelled selects" would pass while none have rendered yet and let
// axe race the labelling on slower (CI) machines (false select-name).
const labelledSelects = AdminPage.locator(
'.frosh-dashboard-grid select[aria-label]:not([aria-label=""]), .frosh-dashboard-grid select[aria-labelledby]',
);
await expect.poll(async () => labelledSelects.count(), { timeout: 30_000 }).toBe(2);

const results = await new AxeBuilder({ page: AdminPage })
.include('.frosh-dashboard-grid')
.withTags(['wcag2a', 'wcag2aa'])
Expand Down
Loading
Loading