diff --git a/.github/actions/variables/action.yml b/.github/actions/variables/action.yml
index 8eab5e9dc10a..36f5de31052e 100644
--- a/.github/actions/variables/action.yml
+++ b/.github/actions/variables/action.yml
@@ -41,9 +41,11 @@ runs:
export IS_DEPENDABOT="${{ github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' }}"
export CYPRESS_failOnSnapshotDiff="0"
export CYPRESS_API_URL="http://localhost:1234/"
- export SNAPSHOTS_PATH="./projects/demo-integrations/cypress/snapshots"
+ export CYPRESS_SNAPSHOTS_PATH="./projects/demo-integrations/cypress/snapshots"
+ export PLAYWRIGHT_SNAPSHOTS_PATH="./projects/demo-playwright/tests-results"
export SNAPSHOTS_CACHE_KEY="e2e-cache--${{ github.event.pull_request.head.sha || github.sha }}-${{ github.event.number }}"
- export SNAPSHOTS_ARTIFACTS_KEY="e2e-artifacts--${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_id }}-${{ github.event.number }}"
+ export CYPRESS_SNAPSHOTS_ARTIFACTS_KEY="cypress-e2e-artifacts--${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_id }}-${{ github.event.number }}"
+ export PLAYWRIGHT_SNAPSHOTS_ARTIFACTS_KEY="playwright-e2e-artifacts--${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_id }}-${{ github.event.number }}"
if [[ "$IS_FORK" == "false" && "$IS_DEPENDABOT" == "false" ]]; then
export IS_OWNER_MODE="true"
@@ -74,6 +76,8 @@ runs:
echo "SUPPORT_AUTO_PUSH=$SUPPORT_AUTO_PUSH" >> $GITHUB_ENV
echo "CYPRESS_failOnSnapshotDiff=$CYPRESS_failOnSnapshotDiff" >> $GITHUB_ENV
echo "CYPRESS_API_URL=$CYPRESS_API_URL" >> $GITHUB_ENV
- echo "SNAPSHOTS_PATH=$SNAPSHOTS_PATH" >> $GITHUB_ENV
- echo "SNAPSHOTS_ARTIFACTS_KEY=$SNAPSHOTS_ARTIFACTS_KEY" >> $GITHUB_ENV
+ echo "CYPRESS_SNAPSHOTS_PATH=$CYPRESS_SNAPSHOTS_PATH" >> $GITHUB_ENV
+ echo "PLAYWRIGHT_SNAPSHOTS_PATH=$PLAYWRIGHT_SNAPSHOTS_PATH" >> $GITHUB_ENV
+ echo "CYPRESS_SNAPSHOTS_ARTIFACTS_KEY=$CYPRESS_SNAPSHOTS_ARTIFACTS_KEY" >> $GITHUB_ENV
+ echo "PLAYWRIGHT_SNAPSHOTS_ARTIFACTS_KEY=$PLAYWRIGHT_SNAPSHOTS_ARTIFACTS_KEY" >> $GITHUB_ENV
echo "SNAPSHOTS_CACHE_KEY=$SNAPSHOTS_CACHE_KEY" >> $GITHUB_ENV
diff --git a/.github/screenshot-bot.config.toml b/.github/screenshot-bot.config.toml
index 6eccf2fffbf2..18d2a586d5dd 100644
--- a/.github/screenshot-bot.config.toml
+++ b/.github/screenshot-bot.config.toml
@@ -1,13 +1,14 @@
# array of RegExp strings to match workflow names
# which should be watched by bot
workflowWithTests = [
- '.*E2E Summary.*', # all workflows with sub-string "e2e" in their names will be watched by bot
+ '.*E2E*', # all workflows with sub-string "e2e" in their names will be watched by bot
]
# array of RegExp strings to match images inside artifacts (by their path or file name)
# which shows difference between two screenshot and which will be added to bot report comment
screenshotsDiffsPaths = [
'.*__diff_output__.*', # it is default cypress folder name into which snapshot diffs are put
+ '.*-diff.png'
]
# RegExp string to match images inside artifacts (by their path or file name)
diff --git a/.github/workflows/e2e-summary.yml b/.github/workflows/e2e-summary.yml
deleted file mode 100644
index 46e1e2efe2db..000000000000
--- a/.github/workflows/e2e-summary.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: ⚙️ E2E Summary report
-on:
- pull_request:
-
-jobs:
- report:
- runs-on: ubuntu-latest
- if: ${{ !contains(github.head_ref , 'release/') }}
- steps:
- - uses: actions/checkout@v3.5.3
- - name: Setup global variables
- uses: ./.github/actions/variables
-
- - name: Wait for e2e tests to succeed
- uses: codex-/await-local-workflow-run@v1
- with:
- token: ${{ github.token }}
- workflow: e2e.yml
- timeout_mins: 120
- poll_interval_ms: 60000
-
- - name: Download cache / ${{ env.SNAPSHOTS_CACHE_KEY }}
- uses: actions/cache/restore@v3.3.1
- with:
- path: ${{ env.SNAPSHOTS_PATH }}
- key: ${{ env.SNAPSHOTS_CACHE_KEY }}
-
- - name: Debug output
- continue-on-error: true
- run: tree ${{ env.SNAPSHOTS_PATH }}
-
- - name: Check if diff-output exists
- id: diff-checker
- run: |
- echo "diff_exist=$(find ${{ env.SNAPSHOTS_PATH }} -regex '.*\.diff\.png$' | wc -l | sed -e 's/^[[:space:]]*//')" >> $GITHUB_OUTPUT
-
- - name: Upload artifacts / ${{ env.SNAPSHOTS_ARTIFACTS_KEY }}
- if: ${{ steps.diff-checker.outputs.diff_exist != '0' }}
- continue-on-error: true
- uses: actions/upload-artifact@v3.1.2
- with:
- name: ${{ env.SNAPSHOTS_ARTIFACTS_KEY }}
- path: ${{ env.SNAPSHOTS_PATH }}
-
- - name: Fall with an error if diff-output exists
- if: ${{ steps.diff-checker.outputs.diff_exist != '0' }}
- run: |
- find ${{ env.SNAPSHOTS_PATH }} -regex '.*\.diff\.png$' -exec echo "{}" \;
- exit 1
-
-concurrency:
- group: e2e-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index ab125f76b318..095ba7836e23 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -22,7 +22,7 @@ jobs:
uses: ./.github/actions/nodejs
- name: Building demo-app of git-branch without cache
- run: npx nx build demo -c next --output-path ${{ env.DIST }}
+ run: npx nx build demo -c next --output-path ${{ env.DIST }} --base-href='/'
- name: Upload cache / ${{ env.CACHE_DIST_KEY }}
uses: actions/cache/save@v3.3.1
@@ -97,40 +97,118 @@ jobs:
- name: Clean up resources
run: npx kill-port --port ${{ env.NG_SERVER_PORT }}
- - name: Upload artifacts / ${{ env.SNAPSHOTS_ARTIFACTS_KEY }}
+ - name: Debug output
+ continue-on-error: true
+ run: tree ${{ env.CYPRESS_SNAPSHOTS_PATH }}
+
+ - name: Upload artifacts / ${{ env.CYPRESS_SNAPSHOTS_ARTIFACTS_KEY }}
+ uses: actions/upload-artifact@v3.1.2
+ with:
+ path: ${{ env.CYPRESS_SNAPSHOTS_PATH }}/**/*.diff.png
+ name: ${{ env.CYPRESS_SNAPSHOTS_ARTIFACTS_KEY }}
+ if-no-files-found: ignore
+ retention-days: 1
+
+ playwright:
+ if: ${{ !contains(github.head_ref , 'release/') }}
+ runs-on: ubuntu-latest
+ needs: [build-demo]
+ steps:
+ - uses: actions/checkout@v3.5.3
+ - name: Setup global variables
+ uses: ./.github/actions/variables
+ - name: Setup Node.js and Cache
+ uses: ./.github/actions/nodejs
+
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+
+ - name: Download cache / ${{ env.CACHE_DIST_KEY }}
+ uses: actions/cache/restore@v3.3.1
+ with:
+ path: dist/demo
+ key: ${{ env.CACHE_DIST_KEY }}
+
+ - name: Serve ${{ env.DIST }} in background
+ run: npx nx serve-compiled demo --path ${{ env.DIST }} --port ${{ env.NG_SERVER_PORT }}
+
+ - name: Run screenshot tests on ${{ env.DIST }}
+ run: npx nx e2e demo-playwright -- --update-snapshots
+
+ - name: Clean up resources
+ run: npx kill-port --port ${{ env.NG_SERVER_PORT }}
+
+ - name: Download ${{ env.DIST_NEXT }} for serve locally
+ run: |
+ git clone \
+ --depth 1 \
+ --branch snapshots/demo/next/${{ github.base_ref }} \
+ https://github.com/Tinkoff/taiga-ui.git ${{ env.DIST_NEXT }}
+
+ - name: Find and replace baseHref for next snapshot
+ uses: jacobtomlinson/gha-find-replace@v2
+ with:
+ find: ''
+ replace: ''
+ include: '${{ env.DIST_NEXT }}/index.html'
+ regex: false
+
+ - name: Serve ${{ env.DIST_NEXT }} in background
+ run: npx nx serve-compiled demo --path ${{ env.DIST_NEXT }} --port ${{ env.NG_SERVER_PORT }}
+
+ - name: Run screenshot tests on ${{ env.DIST_NEXT }}
+ continue-on-error: true
+ run: npx nx e2e demo-playwright
+
+ - name: Clean up resources
+ run: npx kill-port --port ${{ env.NG_SERVER_PORT }}
+
+ - name: Debug output
+ continue-on-error: true
+ run: tree ${{ env.PLAYWRIGHT_SNAPSHOTS_PATH }}
+
+ - name: Upload artifacts / ${{ env.PLAYWRIGHT_SNAPSHOTS_ARTIFACTS_KEY }}
uses: actions/upload-artifact@v3.1.2
with:
- path: ${{ env.SNAPSHOTS_PATH }}/**/*.diff.png
- name: ${{ env.SNAPSHOTS_ARTIFACTS_KEY }}
+ path: ${{ env.PLAYWRIGHT_SNAPSHOTS_PATH }}/**/*-diff.png
+ name: ${{ env.PLAYWRIGHT_SNAPSHOTS_ARTIFACTS_KEY }}
if-no-files-found: ignore
retention-days: 1
result:
if: ${{ !contains(github.head_ref , 'release/') }}
name: result
- needs: [cypress]
+ needs: [cypress, playwright]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.5.3
- name: Setup global variables
uses: ./.github/actions/variables
- - name: Download artifacts / ${{ env.SNAPSHOTS_ARTIFACTS_KEY }}
+ - name: Download artifacts / ${{ env.CYPRESS_SNAPSHOTS_ARTIFACTS_KEY }}
continue-on-error: true
uses: actions/download-artifact@v3.0.2
with:
- name: ${{ env.SNAPSHOTS_ARTIFACTS_KEY }}
- path: ${{ env.SNAPSHOTS_PATH }}
+ name: ${{ env.CYPRESS_SNAPSHOTS_ARTIFACTS_KEY }}
+ path: ./total/cypress
- - name: Upload cache / ${{ env.SNAPSHOTS_CACHE_KEY }}
- uses: actions/cache/save@v3.3.1
+ - name: Download artifacts / ${{ env.PLAYWRIGHT_SNAPSHOTS_ARTIFACTS_KEY }}
+ continue-on-error: true
+ uses: actions/download-artifact@v3.0.2
with:
- path: ${{ env.SNAPSHOTS_PATH }}
- key: ${{ env.SNAPSHOTS_CACHE_KEY }}
+ name: ${{ env.PLAYWRIGHT_SNAPSHOTS_ARTIFACTS_KEY }}
+ path: ./total/playwright
- - name: Debug output
- continue-on-error: true
- run: tree ${{ env.SNAPSHOTS_PATH }}
+ - name: Check if diff-output exists
+ id: diff-checker
+ run: |
+ echo "diff_exist=$(find ./total -regex '.*diff\.png$' | wc -l | sed -e 's/^[[:space:]]*//')" >> $GITHUB_OUTPUT
+
+ - name: Fall with an error if diff-output exists
+ if: ${{ steps.diff-checker.outputs.diff_exist != '0' }}
+ run: |
+ find ./total -regex '.*diff\.png$' -exec echo "{}" \;
+ exit 1
concurrency:
group: e2e-${{ github.workflow }}-${{ github.ref }}
diff --git a/.gitignore b/.gitignore
index ba6015e7f6d8..f7026e28cc88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,7 @@ dist
RELEASE_BODY.md
*tsbuildinfo
.angular
+/projects/demo-playwright/tests-results/
+/projects/demo-playwright/tests-report/
+/projects/demo-playwright/snapshots/
+/projects/demo-playwright/playwright/.cache/
diff --git a/package-lock.json b/package-lock.json
index 370cc33171ef..39cede597fb1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -45,6 +45,7 @@
"@nx/jest": "16.6.0",
"@nx/node": "16.6.0",
"@nx/workspace": "16.6.0",
+ "@playwright/test": "^1.36.2",
"@testing-library/cypress": "9.0.0",
"@tinkoff/prettier-config": "1.52.1",
"@tinkoff/stylelint-config": "1.52.1",
@@ -6776,6 +6777,25 @@
"typescript": "^3 || ^4 || ^5"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.36.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.2.tgz",
+ "integrity": "sha512-2rVZeyPRjxfPH6J0oGJqE8YxiM1IBRyM8hyrXYK7eSiAqmbNhxwcLa7dZ7fy9Kj26V7FYia5fh9XJRq4Dqme+g==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.36.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -30354,6 +30374,18 @@
"node": ">=8"
}
},
+ "node_modules/playwright-core": {
+ "version": "1.36.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.2.tgz",
+ "integrity": "sha512-sQYZt31dwkqxOrP7xy2ggDfEzUxM1lodjhsQ3NMMv5uGTRDsLxU0e4xf4wwMkF2gplIxf17QMBCodSFgm6bFVQ==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/plist": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
diff --git a/package.json b/package.json
index 816e99099997..c25adbdd3e5e 100644
--- a/package.json
+++ b/package.json
@@ -101,6 +101,7 @@
"@nx/jest": "16.6.0",
"@nx/node": "16.6.0",
"@nx/workspace": "16.6.0",
+ "@playwright/test": "^1.36.2",
"@testing-library/cypress": "9.0.0",
"@tinkoff/prettier-config": "1.52.1",
"@tinkoff/stylelint-config": "1.52.1",
diff --git a/projects/demo-playwright/playwright.config.ts b/projects/demo-playwright/playwright.config.ts
new file mode 100644
index 000000000000..ae340822aed7
--- /dev/null
+++ b/projects/demo-playwright/playwright.config.ts
@@ -0,0 +1,39 @@
+import {defineConfig, devices} from '@playwright/test';
+import {ViewportSize} from 'playwright-core';
+
+const DEFAULT_VIEWPORT: ViewportSize = {width: 700, height: 700};
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: __dirname,
+ testMatch: `**/*.spec.ts`,
+ outputDir: `tests-results`,
+ snapshotDir: `snapshots`,
+ reporter: process.env.CI ? `github` : [[`html`, {outputFolder: `tests-report`}]],
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ retries: process.env.CI ? 0 : 0,
+ use: {
+ baseURL: `http://localhost:${process.env.NG_SERVER_PORT || 3333}`,
+ trace: `on-first-retry`,
+ viewport: DEFAULT_VIEWPORT,
+ },
+ projects: [
+ {
+ name: `chromium`,
+ use: {
+ ...devices[`Desktop Chrome`],
+ viewport: DEFAULT_VIEWPORT,
+ },
+ },
+ ],
+ expect: {
+ toHaveScreenshot: {
+ animations: `disabled`,
+ caret: `hide`,
+ },
+ },
+});
diff --git a/projects/demo-playwright/project.json b/projects/demo-playwright/project.json
new file mode 100644
index 000000000000..04e370922122
--- /dev/null
+++ b/projects/demo-playwright/project.json
@@ -0,0 +1,33 @@
+{
+ "name": "demo-playwright",
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "projects/demo-playwright/cypress",
+ "projectType": "application",
+ "prefix": "app",
+ "targets": {
+ "lint": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": "eslint --no-error-on-unmatched-pattern \"**/projects/demo-playwright/**/*\""
+ }
+ },
+ "stylelint": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": "stylelint \"**/demo-playwright/**/*.{css,less}\" --allow-empty-input"
+ }
+ },
+ "e2e": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": "playwright test --config projects/demo-playwright/playwright.config.ts"
+ }
+ },
+ "e2e-ui": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": "nx e2e demo-playwright -- --ui"
+ }
+ }
+ }
+}
diff --git a/projects/demo-playwright/tests/example.spec.ts b/projects/demo-playwright/tests/example.spec.ts
new file mode 100644
index 000000000000..fa7afbe9d16b
--- /dev/null
+++ b/projects/demo-playwright/tests/example.spec.ts
@@ -0,0 +1,17 @@
+import {expect, test} from '@playwright/test';
+
+test(`has title`, async ({page}) => {
+ await page.goto(`/getting-started`);
+
+ await expect(page).toHaveTitle(`Taiga UI: Getting started`);
+});
+
+test(`debug screenshot`, async ({page}) => {
+ await page.goto(`/components/mobile-calendar`);
+
+ await page.locator(`tui-mobile-calendar-example-1 button`).click();
+
+ await expect(page.locator(`tui-dialog tui-mobile-calendar`)).toHaveScreenshot(
+ `test-playwright-screenshot.png`,
+ );
+});
diff --git a/projects/demo/src/modules/components/mobile-calendar/examples/1/index.ts b/projects/demo/src/modules/components/mobile-calendar/examples/1/index.ts
index 479402ffe083..c22bb502e12a 100644
--- a/projects/demo/src/modules/components/mobile-calendar/examples/1/index.ts
+++ b/projects/demo/src/modules/components/mobile-calendar/examples/1/index.ts
@@ -18,7 +18,7 @@ import {map} from 'rxjs/operators';
encapsulation,
})
export class TuiMobileCalendarExample1 {
- private readonly control = new FormControl(new TuiDay(2024, 9, 3));
+ private readonly control = new FormControl(new TuiDay(2024, 10, 3));
private readonly dialog$: Observable;