Skip to content
Open
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
22 changes: 13 additions & 9 deletions .github/workflows/wrapper_tests_e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,20 +170,24 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Start server for ${{ matrix.framework }}
working-directory: e2e/wrappers
run: |
pnpm run start:${{ matrix.framework }} &
sleep 10

- name: Run tests for ${{ matrix.framework }}
working-directory: e2e/wrappers
run: pnpm run test:${{ matrix.framework }}

- name: Upload test artifacts on failure
# The report and the traces are uploaded separately: each artifact then unpacks into the
# layout "playwright show-report" and "playwright show-trace" expect.
- name: Upload test report on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v7
with:
name: playwright-report-${{matrix.framework}}
path: e2e/wrappers/playwright-report
if-no-files-found: ignore

- name: Upload test traces on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v7
with:
name: test-fails-${{matrix.framework}}
path: e2e/wrappers/screenshots
name: playwright-traces-${{matrix.framework}}
path: e2e/wrappers/test-results
if-no-files-found: ignore
1 change: 0 additions & 1 deletion apps/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
"webpack-dev-server": "5.2.6"
},
"scripts": {
"test": "node runner.js",
"start": "webpack-dev-server",
"build": "webpack"
},
Expand Down
1 change: 0 additions & 1 deletion apps/react/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"!{projectRoot}/public/js/app/bundle*",
"{projectRoot}/*.tsx",
"{projectRoot}/*.js",
"!{projectRoot}/test.js",
"{projectRoot}/tsconfig.json",
"{workspaceRoot}/tsconfig.json"
],
Expand Down
17 changes: 0 additions & 17 deletions apps/react/runner.js

This file was deleted.

8 changes: 0 additions & 8 deletions apps/react/test.js

This file was deleted.

1 change: 0 additions & 1 deletion apps/vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"webpack-dev-server": "5.2.6"
},
"scripts": {
"test": "node runner.js",
"start": "webpack-dev-server",
"build": "webpack"
}
Expand Down
1 change: 0 additions & 1 deletion apps/vue/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"{projectRoot}/*.ts",
"{projectRoot}/*.vue",
"{projectRoot}/*.js",
"!{projectRoot}/test.js",
"{projectRoot}/tsconfig.json"
],
"outputs": [
Expand Down
17 changes: 0 additions & 17 deletions apps/vue/runner.js

This file was deleted.

8 changes: 0 additions & 8 deletions apps/vue/test.js

This file was deleted.

2 changes: 2 additions & 0 deletions e2e/wrappers/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
playwright-report/
test-results/
80 changes: 80 additions & 0 deletions e2e/wrappers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Wrappers E2E tests

End-to-end tests that check DevExtreme components inside the React, Vue and Angular wrappers.
The tests are written with [Playwright](https://playwright.dev/) and use the system Google Chrome
(`channel: 'chrome'`), so no browser download is needed.

## Layout

| Path | Purpose |
|------------------------|----------------------------------------------------------------------|
| `builders/*` | Host applications that render the examples for each framework |
| `examples/*` | Examples under test, one folder per scenario and framework |
| `tests/*.spec.ts` | Playwright specs |
| `fixtures.ts` | `test`/`expect` with the `framework` option added |
| `playwright.config.ts` | Per-framework port, base URL and dev server |
| `serve.js` | Static server for a built application |
| `docker/` | Container that matches the CI environment |

## Run locally

```bash
pnpm install --frozen-lockfile
pnpm nx all:build-testing workflows

cd e2e/wrappers
pnpm run build:react19 # or build:vue3 / build:angular / build:all

pnpm run test:react19 # or test:vue3 / test:angular
```

`playwright test` starts `serve.js` on its own, so no separate server is needed.
Useful flags: `--headed`, `--debug`, `--ui`, `--reporter=html`.

Examples that exist for a single framework only (Chat, Gantt) are skipped in the other frameworks.

## Run in the CI environment

The container mirrors the OS, Node and Google Chrome the tests get on CI. Dependencies and
application builds are taken from the host:

```bash
cd e2e/wrappers
docker/run.sh react19
```

The browser runs in the container while you watch and re-run the tests from the browser on your
machine — start the UI mode and open `http://localhost:9323`:

```bash
docker/run.sh --ui react19
```

The repository is mounted, so edits made on the host are picked up and the watch mode re-runs
the affected tests. Plain `playwright test --ui` is not used on purpose: it opens the interface
in the bundled Chromium, which is deliberately not installed.

## Investigate a failed CI run

The `Wrappers E2E Tests` workflow uploads two artifacts per framework when tests fail: the HTML
report and the traces. Download them and open with the tools Playwright ships:

```bash
gh run download <run-id> -n playwright-report-react19 -D ci-report
gh run download <run-id> -n playwright-traces-react19 -D ci-traces

pnpm exec playwright show-report ci-report
pnpm exec playwright show-trace ci-traces/<test-name>/trace.zip --port 0
```

The trace is a step-by-step timeline: the DOM before and after every action, network, console
output and the error, so a failure that only happens on CI can be inspected without reproducing
it. To reproduce it anyway, run the same test in the container — the environment matches.

## Rendering

The default Chrome scrollbars must stay visible: they take layout space, so a page rendered
without them differs from what a user sees and from the etalons the TestCafe tests produce.
Playwright hides scrollbars in headless mode by default, so `--hide-scrollbars` is removed in
`playwright.config.ts`. Measured in the CI-like container, the scrollbar is 15px wide both here
and in TestCafe.
19 changes: 19 additions & 0 deletions e2e/wrappers/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Mirrors the environment the tests get on CI: Ubuntu 24.04, Node from .node-version and
# Google Chrome pinned to the version the workflow installs. Keep the font packages in sync
# with the CI runner image — they affect text rendering and, later, screenshot comparison.
FROM ubuntu:24.04

ARG NODE_VERSION
ARG CHROME_VERSION

RUN echo 'APT::Install-Recommends "0";' > /etc/apt/apt.conf.d/dx-no-recommends \
&& apt-get update \
&& apt-get install -y ca-certificates curl fonts-noto-color-emoji \
&& curl -fsSL "https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}-1_amd64.deb" -o /tmp/chrome.deb \
&& apt-get install -y /tmp/chrome.deb \
&& curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" -o /tmp/node.tar.gz \
&& tar -xzf /tmp/node.tar.gz -C /usr/local --strip-components=1 \
&& rm /tmp/chrome.deb /tmp/node.tar.gz \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /repo/e2e/wrappers
70 changes: 70 additions & 0 deletions e2e/wrappers/docker/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash
# Runs the wrappers e2e tests in a container that matches the CI environment: the OS, Node and
# Google Chrome are the ones the tests get on CI. The repository is mounted as is, so
# dependencies must be installed and the app under test built on the host first:
#
# pnpm install --frozen-lockfile
# pnpm nx all:build-testing workflows
# cd e2e/wrappers && pnpm run build:react19
#
# Usage:
# docker/run.sh [react19|vue3|angular] [extra playwright args]
# docker/run.sh --ui [react19|vue3|angular] open the UI mode at http://localhost:9323

set -euo pipefail

UI_PORT=9323
UI=false

if [ "${1:-}" = "--ui" ]; then
UI=true
shift
fi

FRAMEWORK="${1:-react19}"
shift || true

DOCKER_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$DOCKER_DIR/../../.." && pwd)"
WORKFLOW="$REPO_ROOT/.github/workflows/wrapper_tests_e2e.yml"

NODE_VERSION="$(cat "$REPO_ROOT/.node-version")"
CHROME_VERSION="$(grep -m1 'chrome-version:' "$WORKFLOW" | cut -d: -f2 | tr -d " '\"")"

if [ -z "$NODE_VERSION" ] || [ -z "$CHROME_VERSION" ]; then
echo "❌ Cannot read the Node version from .node-version or the Chrome version from $WORKFLOW." >&2
exit 1
fi

echo "Node $NODE_VERSION, Google Chrome $CHROME_VERSION"

# Google Chrome for Linux ships for amd64 only, so the platform is pinned as on CI.
PLATFORM=linux/amd64

docker build --platform "$PLATFORM" \
--build-arg "NODE_VERSION=$NODE_VERSION" \
--build-arg "CHROME_VERSION=$CHROME_VERSION" \
-t devextreme-wrappers-e2e "$DOCKER_DIR"

# Shared memory and seccomp are set as the CI runner container gets them.
DOCKER_ARGS=(
--rm
--platform "$PLATFORM"
--shm-size=2gb
--security-opt seccomp=unconfined
-v "$REPO_ROOT:/repo"
-w /repo/e2e/wrappers
-e "FRAMEWORK=$FRAMEWORK"
)
PLAYWRIGHT_ARGS=(test)

if [ "$UI" = true ]; then
DOCKER_ARGS+=(-p "$UI_PORT:$UI_PORT")
PLAYWRIGHT_ARGS+=(--ui-host=0.0.0.0 "--ui-port=$UI_PORT")
echo "UI mode for $FRAMEWORK: open http://localhost:$UI_PORT"
else
DOCKER_ARGS+=(-e CI=true)
PLAYWRIGHT_ARGS+=("$@")
fi

docker run "${DOCKER_ARGS[@]}" devextreme-wrappers-e2e node_modules/.bin/playwright "${PLAYWRIGHT_ARGS[@]}"
13 changes: 13 additions & 0 deletions e2e/wrappers/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { test as base } from '@playwright/test';

export type Framework = 'react19' | 'vue3' | 'angular';

export interface TestOptions {
framework: Framework;
}

export const test = base.extend<TestOptions>({
framework: ['react19', { option: true }],
});

export { expect } from '@playwright/test';
9 changes: 5 additions & 4 deletions e2e/wrappers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"start:vue3": "pnpm run dev:vue3 -- --host",
"start:angular": "cd builders/angular && pnpm run start",
"build:all": "pnpm run build:react19 && pnpm run build:vue3 && pnpm run build:angular",
"test:react19": "node ./runner.js --framework=react",
"test:vue3": "node ./runner.js --framework=vue",
"test:angular": "node ./runner.js --framework=angular"
"test:react19": "cross-env FRAMEWORK=react19 playwright test",
"test:vue3": "cross-env FRAMEWORK=vue3 playwright test",
"test:angular": "cross-env FRAMEWORK=angular playwright test"
},
"dependencies": {
"@angular/common": "catalog:angular",
Expand Down Expand Up @@ -59,11 +59,13 @@
"@angular/cli": "catalog:angular",
"@angular/compiler-cli": "catalog:angular",
"@eslint/js": "catalog:",
"@playwright/test": "catalog:",
"@types/jasmine": "5.1.4",
"@types/react": "19.1.2",
"@types/react-dom": "19.1.3",
"@vitejs/plugin-react": "4.7.0",
"@vitejs/plugin-vue": "5.2.4",
"cross-env": "7.0.3",
"eslint": "9.39.4",
"eslint-plugin-react-hooks": "7.0.1",
"eslint-plugin-react-refresh": "0.5.2",
Expand All @@ -74,7 +76,6 @@
"karma-coverage": "2.2.1",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.1.0",
"testcafe": "catalog:",
"typescript": "5.8.3",
"vite": "8.0.16"
},
Expand Down
51 changes: 51 additions & 0 deletions e2e/wrappers/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { defineConfig } from '@playwright/test';

import type { Framework, TestOptions } from './fixtures';

const FRAMEWORK_PORTS: Record<Framework, number> = {
react19: 3030,
angular: 3031,
vue3: 3032,
};

const framework = (process.env.FRAMEWORK ?? 'react19') as Framework;
const port = FRAMEWORK_PORTS[framework];

if(!port) {
throw new Error(`Unsupported framework: ${framework}. Expected one of: ${Object.keys(FRAMEWORK_PORTS).join(', ')}.`);
}

const baseURL = `http://localhost:${port}`;

export default defineConfig<TestOptions>({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
workers: 1,
reporter: process.env.CI
? [['list'], ['html', { open: 'never' }]]
: [['list']],
timeout: 30000,
expect: { timeout: 5000 },
use: {
framework,
baseURL,
channel: 'chrome',
headless: true,
viewport: { width: 1200, height: 800 },
launchOptions: {
args: ['--no-sandbox', '--disable-gpu'],
// Playwright hides scrollbars in headless by default, so the page gets no scrollbar
// gutter and its layout differs from a real browser and from the TestCafe etalons.
ignoreDefaultArgs: ['--hide-scrollbars'],
},
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
},
webServer: {
command: `node ./serve.js --framework=${framework} --port=${port}`,
url: baseURL,
reuseExistingServer: !process.env.CI,
timeout: 30000,
},
});
Loading
Loading