diff --git a/package.json b/package.json
index a00b0a9052e..9e82a5ffd94 100644
--- a/package.json
+++ b/package.json
@@ -19,10 +19,10 @@
"test:prepare": "rimraf temp && lerna run build",
"test:open": "CYPRESS_COVERAGE=false cypress open --component --browser chrome",
"test": "yarn test:prepare && cypress run --component --browser chrome --spec packages",
- "test:pw": "playwright test -c playwright-ct.config.ts",
- "test:pw:open": "playwright test -c playwright-ct.config.ts --ui",
- "test:pw:ci": "playwright test -c playwright-ct.config.ts --project chromium",
- "clean": "tsc --build --clean && tsc --build tsconfig.build.json --clean && rimraf temp .out test-results playwright-report playwright-ct && lerna run clean",
+ "test:pw": "playwright test",
+ "test:pw:open": "playwright test --ui",
+ "test:pw:ci": "PW_COVERAGE=true playwright test --project chromium && nyc report --temp-dir temp/.nyc_output_playwright --report-dir temp/playwright-coverage --reporter lcovonly",
+ "clean": "tsc --build --clean && tsc --build tsconfig.build.json --clean && rimraf temp .out test-results playwright-report && lerna run clean",
"clean:remove-modules": "yarn clean && rimraf node_modules",
"prettier:all": "prettier --write --config ./prettier.config.js \"**/*\"",
"lint": "eslint .",
@@ -68,7 +68,6 @@
"@cypress/code-coverage": "4.0.3",
"@eslint/compat": "2.1.0",
"@eslint/js": "9.39.5",
- "@playwright/experimental-ct-react": "1.62.1",
"@playwright/test": "1.62.1",
"@semantic-release/github": "12.0.9",
"@testing-library/cypress": "10.1.3",
@@ -98,8 +97,8 @@
"husky": "9.1.7",
"lerna": "10.0.0",
"lint-staged": "17.2.0",
- "monocart-reporter": "2.12.4",
"npm-run-all2": "9.0.3",
+ "nyc": "18.0.0",
"pagefind": "1.5.2",
"postcss": "8.5.25",
"postcss-cli": "11.0.1",
@@ -140,6 +139,10 @@
"**/src/enums/*",
"**/*.stories.tsx",
"**/*.test.{ts,tsx}",
+ "**/*.spec.{ts,tsx}",
+ "**/*.gallery.tsx",
+ "**/test/**",
+ "packages/charts/src/test-utils/**",
"**/*.module.css.ts",
"**/node_modules/**",
"**/dist/**",
diff --git a/packages/charts/src/components/BarChart/test/BarChartTestComponents.tsx b/packages/charts/src/components/BarChart/test/BarChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/BarChart/test/BarChartTestComponents.tsx
rename to packages/charts/src/components/BarChart/test/BarChart.gallery.tsx
diff --git a/packages/charts/src/components/BarChart/test/BarChart.spec.tsx b/packages/charts/src/components/BarChart/test/BarChart.spec.tsx
index f93932c16ea..154d4e85a43 100644
--- a/packages/charts/src/components/BarChart/test/BarChart.spec.tsx
+++ b/packages/charts/src/components/BarChart/test/BarChart.spec.tsx
@@ -1,38 +1,23 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import { complexDataSet } from '../../../resources/DemoProps.js';
import {
testLoadingStates,
testPassThroughProps,
testStackAggregateTotals,
testZoomingTool,
-} from '../../../test-utils/sharedTests.js';
-import { BarChart } from '../index.js';
-import {
- BarChartClickTest,
- BarChartDataPointClickTest,
- BarChartHighlightColorTest,
- BarChartLegendConfigTest,
- BarChartSecondYAxisTest,
-} from './BarChartTestComponents.js';
-
-const dimensions = [{ accessor: 'name', interval: 0 }];
-const measures = [
- { accessor: 'users', label: 'Users' },
- { accessor: 'sessions', label: 'Active Sessions' },
- { accessor: 'volume', label: 'Vol.' },
-];
-const baseProps = { dataset: complexDataSet, dimensions, measures };
+} from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('BarChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount();
+ await mount('ChartHarness/Chart', { chart: 'BarChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-bar')).toHaveCount(3);
await expect(page.locator('.recharts-bar-rectangles')).toHaveCount(3);
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('BarChart/BarChartClickTest');
await page.getByText('January').click();
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -49,24 +34,21 @@ test.describe('BarChart', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('volume');
});
- testLoadingStates(BarChart, baseProps, { dimensions: [], measures: [] }, '.recharts-bar');
+ testLoadingStates('BarChart', '.recharts-bar');
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('BarChart/BarChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
- testZoomingTool(BarChart, baseProps);
+ testZoomingTool('BarChart');
- testPassThroughProps(BarChart, { dimensions: [], measures: [] });
+ testPassThroughProps('BarChart');
- testStackAggregateTotals(BarChart, { dataset: complexDataSet.slice(0, 3), dimensions }, [
- { accessor: 'users', stackId: 'A', label: 'Users' },
- { accessor: 'sessions', stackId: 'A', label: 'Active Sessions' },
- ]);
+ testStackAggregateTotals('BarChart');
test('onDataPointClick', async ({ mount, page }) => {
- await mount();
+ await mount('BarChart/BarChartDataPointClickTest');
await page.locator('[name="January"]').first().click();
await expect(page.getByTestId('dp-click-count')).toHaveText('1');
@@ -77,7 +59,7 @@ test.describe('BarChart', () => {
});
test('highlightColor', async ({ mount, page }) => {
- await mount();
+ await mount('BarChart/BarChartHighlightColorTest');
// January has users=100 (<=200 → green), February has users=230 (>200 → red)
await expect(page.locator('.recharts-bar-rectangle [fill="green"]').first()).toBeAttached();
@@ -85,7 +67,7 @@ test.describe('BarChart', () => {
});
test('secondYAxis', async ({ mount, page }) => {
- await mount();
+ await mount('BarChart/BarChartSecondYAxisTest');
// BarChart is horizontal so the secondary "Y" axis renders as an additional XAxis
await expect(page.locator('.recharts-xAxis')).toHaveCount(2);
});
diff --git a/packages/charts/src/components/BulletChart/test/BulletChartTestComponents.tsx b/packages/charts/src/components/BulletChart/test/BulletChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/BulletChart/test/BulletChartTestComponents.tsx
rename to packages/charts/src/components/BulletChart/test/BulletChart.gallery.tsx
diff --git a/packages/charts/src/components/BulletChart/test/BulletChart.spec.tsx b/packages/charts/src/components/BulletChart/test/BulletChart.spec.tsx
index 61bf1350da0..96c27fbeff3 100644
--- a/packages/charts/src/components/BulletChart/test/BulletChart.spec.tsx
+++ b/packages/charts/src/components/BulletChart/test/BulletChart.spec.tsx
@@ -1,42 +1,18 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import { complexDataSet } from '../../../resources/DemoProps.js';
-import { testLoadingStates, testPassThroughProps, testZoomingTool } from '../../../test-utils/sharedTests.js';
-import { BulletChart } from '../index.js';
-import {
- BulletChartClickTest,
- BulletChartDataPointClickTest,
- BulletChartLegendConfigTest,
- BulletChartVerticalLayoutTest,
-} from './BulletChartTestComponents.js';
-
-const dimensions = [{ accessor: 'name', interval: 0 }];
-const measures = [
- { accessor: 'users', label: 'Users', type: 'primary' as const },
- { accessor: 'sessions', label: 'Active Sessions', type: 'comparison' as const },
- { accessor: 'volume', label: 'Vol.', type: 'additional' as const },
-];
-const baseProps = { dataset: complexDataSet, dimensions, measures };
+import { testLoadingStates, testPassThroughProps, testZoomingTool } from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('BulletChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount(
- ,
- );
+ await mount('ChartHarness/Chart', { chart: 'BulletChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-bar')).toHaveCount(3);
await expect(page.locator('.recharts-bar-rectangles')).toHaveCount(3);
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('BulletChart/BulletChartClickTest');
await page.getByText('January').click();
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -53,28 +29,19 @@ test.describe('BulletChart', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('volume');
});
- testLoadingStates(
- BulletChart,
- {
- dataset: complexDataSet,
- dimensions: [{ accessor: 'name', interval: 0 }],
- measures: [{ accessor: 'users', label: 'Users', type: 'primary' as const }],
- },
- { dimensions: [], measures: [] },
- '.recharts-bar',
- );
+ testLoadingStates('BulletChart', '.recharts-bar');
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('BulletChart/BulletChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
- testZoomingTool(BulletChart, baseProps);
+ testZoomingTool('BulletChart');
- testPassThroughProps(BulletChart, { dimensions: [], measures: [] });
+ testPassThroughProps('BulletChart');
test('onDataPointClick', async ({ mount, page }) => {
- await mount();
+ await mount('BulletChart/BulletChartDataPointClickTest');
// make sure not to click the label, as currently the event is only fired when the actual bar is clicked.
const firstBar = page.locator('.recharts-bar-rectangle path').first();
@@ -87,7 +54,7 @@ test.describe('BulletChart', () => {
});
test('layout="vertical"', async ({ mount, page }) => {
- await mount();
+ await mount('BulletChart/BulletChartVerticalLayoutTest');
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
// Vertical layout renders bars along Y axis and uses XAxis for values
await expect(page.locator('.recharts-bar')).toHaveCount(3);
diff --git a/packages/charts/src/components/ColumnChart/test/ColumnChartTestComponents.tsx b/packages/charts/src/components/ColumnChart/test/ColumnChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/ColumnChart/test/ColumnChartTestComponents.tsx
rename to packages/charts/src/components/ColumnChart/test/ColumnChart.gallery.tsx
diff --git a/packages/charts/src/components/ColumnChart/test/ColumnChart.spec.tsx b/packages/charts/src/components/ColumnChart/test/ColumnChart.spec.tsx
index 8dccf2ff1e0..48eb48333d1 100644
--- a/packages/charts/src/components/ColumnChart/test/ColumnChart.spec.tsx
+++ b/packages/charts/src/components/ColumnChart/test/ColumnChart.spec.tsx
@@ -1,48 +1,23 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import { complexDataSet } from '../../../resources/DemoProps.js';
import {
testLoadingStates,
testPassThroughProps,
testStackAggregateTotals,
testZoomingTool,
-} from '../../../test-utils/sharedTests.js';
-import { ColumnChart } from '../index.js';
-import {
- ColumnChartClickTest,
- ColumnChartDataPointClickTest,
- ColumnChartHighlightColorTest,
- ColumnChartLegendConfigTest,
- ColumnChartSecondYAxisTest,
-} from './ColumnChartTestComponents.js';
-
-const dimensions = [{ accessor: 'name', interval: 0 }];
-const measures = [
- { accessor: 'users', label: 'Users' },
- { accessor: 'sessions', label: 'Active Sessions' },
- { accessor: 'volume', label: 'Vol.' },
-];
-const baseProps = { dataset: complexDataSet, dimensions, measures };
+} from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('ColumnChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount(
- ,
- );
+ await mount('ChartHarness/Chart', { chart: 'ColumnChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-bar')).toHaveCount(3);
await expect(page.locator('.recharts-bar-rectangles')).toHaveCount(3);
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('ColumnChart/ColumnChartClickTest');
await page.getByText('January').click();
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -59,33 +34,21 @@ test.describe('ColumnChart', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('volume');
});
- testLoadingStates(
- ColumnChart,
- {
- dataset: complexDataSet,
- dimensions: [{ accessor: 'name', interval: 0 }],
- measures: [{ accessor: 'users', label: 'Users' }],
- },
- { dimensions: [], measures: [] },
- '.recharts-bar',
- );
+ testLoadingStates('ColumnChart', '.recharts-bar');
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('ColumnChart/ColumnChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
- testZoomingTool(ColumnChart, baseProps);
+ testZoomingTool('ColumnChart');
- testPassThroughProps(ColumnChart, { dimensions: [], measures: [] });
+ testPassThroughProps('ColumnChart');
- testStackAggregateTotals(ColumnChart, { dataset: complexDataSet.slice(0, 3), dimensions }, [
- { accessor: 'users', stackId: 'A', label: 'Users' },
- { accessor: 'sessions', stackId: 'A', label: 'Active Sessions' },
- ]);
+ testStackAggregateTotals('ColumnChart');
test('onDataPointClick', async ({ mount, page }) => {
- await mount();
+ await mount('ColumnChart/ColumnChartDataPointClickTest');
await page.locator('[name="January"]').first().click();
await expect(page.getByTestId('dp-click-count')).toHaveText('1');
@@ -96,7 +59,7 @@ test.describe('ColumnChart', () => {
});
test('highlightColor', async ({ mount, page }) => {
- await mount();
+ await mount('ColumnChart/ColumnChartHighlightColorTest');
// January has users=100 (<=200 → green), February has users=230 (>200 → red)
const greenCells = page.locator('.recharts-bar-rectangle [fill="green"]');
@@ -106,7 +69,7 @@ test.describe('ColumnChart', () => {
});
test('secondYAxis', async ({ mount, page }) => {
- await mount();
+ await mount('ColumnChart/ColumnChartSecondYAxisTest');
// ColumnChart is vertical so secondYAxis renders as an additional YAxis
await expect(page.locator('.recharts-yAxis')).toHaveCount(2);
diff --git a/packages/charts/src/components/ColumnChartWithTrend/test/ColumnChartWithTrendTestComponents.tsx b/packages/charts/src/components/ColumnChartWithTrend/test/ColumnChartWithTrend.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/ColumnChartWithTrend/test/ColumnChartWithTrendTestComponents.tsx
rename to packages/charts/src/components/ColumnChartWithTrend/test/ColumnChartWithTrend.gallery.tsx
diff --git a/packages/charts/src/components/ColumnChartWithTrend/test/ColumnChartWithTrend.spec.tsx b/packages/charts/src/components/ColumnChartWithTrend/test/ColumnChartWithTrend.spec.tsx
index 390ae0656e2..bacde971776 100644
--- a/packages/charts/src/components/ColumnChartWithTrend/test/ColumnChartWithTrend.spec.tsx
+++ b/packages/charts/src/components/ColumnChartWithTrend/test/ColumnChartWithTrend.spec.tsx
@@ -1,23 +1,10 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
-import { complexDataSet } from '../../../resources/DemoProps.js';
-import { testLoadingStates, testPassThroughProps, testZoomingTool } from '../../../test-utils/sharedTests.js';
-import { ColumnChartWithTrend } from '../index.js';
-import {
- ColumnChartWithTrendClickTest,
- ColumnChartWithTrendGridTest,
- ColumnChartWithTrendLegendConfigTest,
-} from './ColumnChartWithTrendTestComponents.js';
-
-const dimensions = [{ accessor: 'name', interval: 0 }];
-const measures = [
- { accessor: 'users', label: 'Users', type: 'line' as const },
- { accessor: 'sessions', label: 'Active Sessions', type: 'bar' as const },
-];
-const baseProps = { dataset: complexDataSet, dimensions, measures };
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
+import { testLoadingStates, testPassThroughProps, testZoomingTool } from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('ColumnChartWithTrend', () => {
test('Basic', async ({ mount, page }) => {
- await mount();
+ await mount('ChartHarness/Chart', { chart: 'ColumnChartWithTrend' });
await expect(page.locator('.recharts-responsive-container').first()).toBeVisible();
await expect(page.locator('.recharts-bar')).toHaveCount(1);
await expect(page.locator('.recharts-line')).toHaveCount(1);
@@ -26,7 +13,7 @@ test.describe('ColumnChartWithTrend', () => {
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('ColumnChartWithTrend/ColumnChartWithTrendClickTest');
await page.getByText('January').click();
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -42,22 +29,10 @@ test.describe('ColumnChartWithTrend', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('users');
});
- testLoadingStates(
- ColumnChartWithTrend,
- {
- dataset: complexDataSet,
- dimensions: [{ accessor: 'name', interval: 0 }],
- measures: [
- { accessor: 'users', label: 'Users', type: 'line' as const },
- { accessor: 'sessions', label: 'Active Sessions', type: 'bar' as const },
- ],
- },
- { dimensions: [], measures: [] },
- '.recharts-bar',
- );
+ testLoadingStates('ColumnChartWithTrend', '.recharts-bar');
test('in Grid', async ({ mount, page }) => {
- await mount();
+ await mount('ColumnChartWithTrend/ColumnChartWithTrendGridTest');
const chart = page.getByTestId('ccwt');
await expect(chart).toBeVisible();
const box = await chart.boundingBox();
@@ -66,11 +41,11 @@ test.describe('ColumnChartWithTrend', () => {
});
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('ColumnChartWithTrend/ColumnChartWithTrendLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
- testZoomingTool(ColumnChartWithTrend, baseProps);
+ testZoomingTool('ColumnChartWithTrend');
- testPassThroughProps(ColumnChartWithTrend, { dimensions: [], measures: [] });
+ testPassThroughProps('ColumnChartWithTrend');
});
diff --git a/packages/charts/src/components/ComposedChart/test/ComposedChartTestComponents.tsx b/packages/charts/src/components/ComposedChart/test/ComposedChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/ComposedChart/test/ComposedChartTestComponents.tsx
rename to packages/charts/src/components/ComposedChart/test/ComposedChart.gallery.tsx
diff --git a/packages/charts/src/components/ComposedChart/test/ComposedChart.spec.tsx b/packages/charts/src/components/ComposedChart/test/ComposedChart.spec.tsx
index a8c73b7697a..396c586fa60 100644
--- a/packages/charts/src/components/ComposedChart/test/ComposedChart.spec.tsx
+++ b/packages/charts/src/components/ComposedChart/test/ComposedChart.spec.tsx
@@ -1,41 +1,16 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import { complexDataSet } from '../../../resources/DemoProps.js';
import {
testLoadingStates,
testPassThroughProps,
testStackAggregateTotals,
testZoomingTool,
-} from '../../../test-utils/sharedTests.js';
-import { ComposedChart } from '../index.js';
-import {
- ComposedChartClickTest,
- ComposedChartDataPointClickTest,
- ComposedChartLegendConfigTest,
- ComposedChartSecondYAxisTest,
- ComposedChartVerticalLayoutTest,
-} from './ComposedChartTestComponents.js';
-
-const dimensions = [{ accessor: 'name', interval: 0 }];
-const measures = [
- { accessor: 'users', label: 'Users', type: 'line' as const },
- { accessor: 'sessions', label: 'Active Sessions', type: 'bar' as const },
- { accessor: 'volume', label: 'Vol.', type: 'area' as const },
-];
-const baseProps = { dataset: complexDataSet, dimensions, measures };
+} from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('ComposedChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount(
- ,
- );
+ await mount('ChartHarness/Chart', { chart: 'ComposedChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-line')).toHaveCount(1);
await expect(page.locator('.recharts-bar')).toHaveCount(1);
@@ -46,7 +21,7 @@ test.describe('ComposedChart', () => {
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('ComposedChart/ComposedChartClickTest');
await page.getByText('January').click();
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -60,33 +35,21 @@ test.describe('ComposedChart', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('users');
});
- testLoadingStates(
- ComposedChart,
- {
- dataset: complexDataSet,
- dimensions: [{ accessor: 'name', interval: 0 }],
- measures: [{ accessor: 'users', label: 'Users', type: 'bar' }],
- },
- { dimensions: [], measures: [] },
- '.recharts-bar',
- );
+ testLoadingStates('ComposedChart', '.recharts-bar');
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('ComposedChart/ComposedChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
- testZoomingTool(ComposedChart, baseProps);
+ testZoomingTool('ComposedChart');
- testPassThroughProps(ComposedChart, { dimensions: [], measures: [] });
+ testPassThroughProps('ComposedChart');
- testStackAggregateTotals(ComposedChart, { dataset: complexDataSet.slice(0, 3), dimensions }, [
- { accessor: 'users', stackId: 'A', label: 'Users', type: 'bar' as const },
- { accessor: 'sessions', stackId: 'A', label: 'Active Sessions', type: 'bar' as const },
- ]);
+ testStackAggregateTotals('ComposedChart');
test('layout="vertical"', async ({ mount, page }) => {
- await mount();
+ await mount('ComposedChart/ComposedChartVerticalLayoutTest');
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
// Vertical layout swaps axes: measure axis becomes XAxis (type=number)
await expect(page.locator('.recharts-xAxis')).toBeAttached();
@@ -97,7 +60,7 @@ test.describe('ComposedChart', () => {
});
test('onDataPointClick', async ({ mount, page }) => {
- await mount();
+ await mount('ComposedChart/ComposedChartDataPointClickTest');
await page.locator('[name="January"]').first().click();
await expect(page.getByTestId('dp-click-count')).toHaveText('1');
@@ -108,7 +71,7 @@ test.describe('ComposedChart', () => {
});
test('secondYAxis', async ({ mount, page }) => {
- await mount();
+ await mount('ComposedChart/ComposedChartSecondYAxisTest');
// ComposedChart renders secondYAxis as an additional YAxis
await expect(page.locator('.recharts-yAxis')).toHaveCount(2);
diff --git a/packages/charts/src/components/DonutChart/test/DonutChartTestComponents.tsx b/packages/charts/src/components/DonutChart/test/DonutChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/DonutChart/test/DonutChartTestComponents.tsx
rename to packages/charts/src/components/DonutChart/test/DonutChart.gallery.tsx
diff --git a/packages/charts/src/components/DonutChart/test/DonutChart.spec.tsx b/packages/charts/src/components/DonutChart/test/DonutChart.spec.tsx
index 368e42b74da..053159a1462 100644
--- a/packages/charts/src/components/DonutChart/test/DonutChart.spec.tsx
+++ b/packages/charts/src/components/DonutChart/test/DonutChart.spec.tsx
@@ -1,31 +1,18 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import { simpleDataSet } from '../../../resources/DemoProps.js';
-import { testLoadingStates, testPassThroughProps } from '../../../test-utils/sharedTests.js';
-import { DonutChart } from '../index.js';
-import {
- DonutChartClickTest,
- DonutChartLegendConfigTest,
- DonutChartSectorFocusActiveTest,
- DonutChartSectorFocusDatasetShrinkTest,
- DonutChartSectorFocusEmptyTest,
- DonutChartSectorFocusHandlersTest,
- DonutChartSectorFocusOutOfBoundsTest,
- DonutChartSectorFocusTest,
-} from './DonutChartTestComponents.js';
-
-const dimension = { accessor: 'name' };
-const measure = { accessor: 'users' };
+import { testLoadingStates, testPassThroughProps } from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('DonutChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount();
+ await mount('ChartHarness/Chart', { chart: 'DonutChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-pie')).toHaveCount(1);
await expect(page.locator('.recharts-pie-sector')).toHaveCount(12);
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('DonutChart/DonutChartClickTest');
await page.locator('[name="January"]').first().click({ force: true });
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -36,23 +23,18 @@ test.describe('DonutChart', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('users');
});
- testLoadingStates(
- DonutChart,
- { dataset: simpleDataSet, dimension, measure },
- { dimension: {}, measure: {} },
- '.recharts-pie',
- );
+ testLoadingStates('DonutChart', '.recharts-pie');
- testPassThroughProps(DonutChart, { dimension: {}, measure: {} });
+ testPassThroughProps('DonutChart');
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('DonutChart/DonutChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
test.describe('Sector Focus - keyboard navigation', () => {
test('Tab, arrows, Enter, wrap-around', async ({ mount, page }) => {
- await mount();
+ await mount('DonutChart/DonutChartSectorFocusTest');
// Focus "before" button then Tab into chart container
await page.getByText('before').focus();
@@ -95,7 +77,7 @@ test.describe('DonutChart', () => {
});
test('activeSegment with Enter and Space', async ({ mount, page }) => {
- await mount();
+ await mount('DonutChart/DonutChartSectorFocusActiveTest');
// Initial activeSegment is 2
await expect(page.getByTestId('active-segment')).toHaveText('2');
@@ -137,7 +119,7 @@ test.describe('DonutChart', () => {
});
test('empty dataset is non-interactive', async ({ mount, page }) => {
- await mount();
+ await mount('DonutChart/DonutChartSectorFocusEmptyTest');
// The chart container should have tabindex 0 but no role="application"
const chartContainer = page.locator('[aria-roledescription="chart"]');
@@ -146,7 +128,7 @@ test.describe('DonutChart', () => {
});
test('consumer event handlers are composed', async ({ mount, page }) => {
- await mount();
+ await mount('DonutChart/DonutChartSectorFocusHandlersTest');
// Focus the chart container directly (triggers onFocus)
const chartContainer = page.locator('[aria-roledescription="chart"]');
@@ -167,7 +149,7 @@ test.describe('DonutChart', () => {
});
test('activeSegment out of bounds is clamped', async ({ mount, page }) => {
- await mount();
+ await mount('DonutChart/DonutChartSectorFocusOutOfBoundsTest');
await page.getByText('before').focus();
await page.keyboard.press('Tab');
@@ -176,7 +158,7 @@ test.describe('DonutChart', () => {
});
test('dataset shrink resets keyboard state', async ({ mount, page }) => {
- await mount();
+ await mount('DonutChart/DonutChartSectorFocusDatasetShrinkTest');
// Tab past "shrink" button into chart, then into sector mode
await page.getByText('before').focus();
diff --git a/packages/charts/src/components/LineChart/test/LineChartTestComponents.tsx b/packages/charts/src/components/LineChart/test/LineChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/LineChart/test/LineChartTestComponents.tsx
rename to packages/charts/src/components/LineChart/test/LineChart.gallery.tsx
diff --git a/packages/charts/src/components/LineChart/test/LineChart.spec.tsx b/packages/charts/src/components/LineChart/test/LineChart.spec.tsx
index 83e7c89f75b..1a6fbf90d79 100644
--- a/packages/charts/src/components/LineChart/test/LineChart.spec.tsx
+++ b/packages/charts/src/components/LineChart/test/LineChart.spec.tsx
@@ -1,34 +1,11 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import { complexDataSet } from '../../../resources/DemoProps.js';
-import { testLoadingStates, testPassThroughProps, testZoomingTool } from '../../../test-utils/sharedTests.js';
-import { LineChart } from '../index.js';
-import {
- LineChartClickTest,
- LineChartDataPointClickTest,
- LineChartLegendConfigTest,
-} from './LineChartTestComponents.js';
-
-const dimensions = [{ accessor: 'name', interval: 0 }];
-const measures = [
- { accessor: 'users', label: 'Users' },
- { accessor: 'sessions', label: 'Active Sessions' },
- { accessor: 'volume', label: 'Vol.' },
-];
-const baseProps = { dataset: complexDataSet, dimensions, measures };
+import { testLoadingStates, testPassThroughProps, testZoomingTool } from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('LineChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount(
- ,
- );
+ await mount('ChartHarness/Chart', { chart: 'LineChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-line')).toHaveCount(3);
await expect(page.locator('.recharts-line-curve')).toHaveCount(3);
@@ -36,7 +13,7 @@ test.describe('LineChart', () => {
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('LineChart/LineChartClickTest');
await page.locator('.recharts-line-dot[name="Users"]').first().click({ force: true });
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -47,28 +24,19 @@ test.describe('LineChart', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('users');
});
- testLoadingStates(
- LineChart,
- {
- dataset: complexDataSet,
- dimensions: [{ accessor: 'name', interval: 0 }],
- measures: [{ accessor: 'users', label: 'Users' }],
- },
- { dimensions: [], measures: [] },
- '.recharts-line',
- );
+ testLoadingStates('LineChart', '.recharts-line');
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('LineChart/LineChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
- testZoomingTool(LineChart, baseProps);
+ testZoomingTool('LineChart');
- testPassThroughProps(LineChart, { dimensions: [], measures: [] });
+ testPassThroughProps('LineChart');
test('onDataPointClick', async ({ mount, page }) => {
- await mount();
+ await mount('LineChart/LineChartDataPointClickTest');
// LineChart fires onDataPointClick via activeDot — hover to trigger the active dot, then click it
const firstDot = page.locator('.recharts-line-dot[name="Users"]').first();
diff --git a/packages/charts/src/components/PieChart/test/PieChartTestComponents.tsx b/packages/charts/src/components/PieChart/test/PieChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/PieChart/test/PieChartTestComponents.tsx
rename to packages/charts/src/components/PieChart/test/PieChart.gallery.tsx
diff --git a/packages/charts/src/components/PieChart/test/PieChart.spec.tsx b/packages/charts/src/components/PieChart/test/PieChart.spec.tsx
index 6e9ed5e1ab2..8849eeddfe1 100644
--- a/packages/charts/src/components/PieChart/test/PieChart.spec.tsx
+++ b/packages/charts/src/components/PieChart/test/PieChart.spec.tsx
@@ -1,32 +1,18 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import { simpleDataSet } from '../../../resources/DemoProps.js';
-import { testLoadingStates, testPassThroughProps } from '../../../test-utils/sharedTests.js';
-import { PieChart } from '../index.js';
-import {
- PieChartClickTest,
- PieChartCustomLabelTest,
- PieChartLegendConfigTest,
- PieChartSectorFocusActiveTest,
- PieChartSectorFocusDatasetShrinkTest,
- PieChartSectorFocusEmptyTest,
- PieChartSectorFocusHandlersTest,
- PieChartSectorFocusOutOfBoundsTest,
- PieChartSectorFocusTest,
-} from './PieChartTestComponents.js';
-
-const dimension = { accessor: 'name' };
-const measure = { accessor: 'users' };
+import { testLoadingStates, testPassThroughProps } from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('PieChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount();
+ await mount('ChartHarness/Chart', { chart: 'PieChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-pie')).toHaveCount(1);
await expect(page.locator('.recharts-pie-sector')).toHaveCount(12);
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartClickTest');
await page.locator('[name="January"]').first().click({ force: true });
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -37,28 +23,23 @@ test.describe('PieChart', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('users');
});
- testLoadingStates(
- PieChart,
- { dataset: simpleDataSet, dimension, measure },
- { dimension: {}, measure: {} },
- '.recharts-pie',
- );
+ testLoadingStates('PieChart', '.recharts-pie');
- testPassThroughProps(PieChart, { dimension: {}, measure: {} });
+ testPassThroughProps('PieChart');
test('custom label', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartCustomLabelTest');
await expect(page.getByText('CustomLabel')).toHaveCount(12);
});
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
test.describe('Sector Focus - keyboard navigation', () => {
test('Tab, arrows, Enter, wrap-around', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartSectorFocusTest');
// Focus "before" button then Tab into chart container
await page.getByText('before').focus();
@@ -101,7 +82,7 @@ test.describe('PieChart', () => {
});
test('activeSegment with Enter and Space', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartSectorFocusActiveTest');
// Initial activeSegment is 2
await expect(page.getByTestId('active-segment')).toHaveText('2');
@@ -143,7 +124,7 @@ test.describe('PieChart', () => {
});
test('empty dataset is non-interactive', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartSectorFocusEmptyTest');
// The chart container should have tabindex 0 but no role="application"
const chartContainer = page.locator('[aria-roledescription="chart"]');
@@ -152,7 +133,7 @@ test.describe('PieChart', () => {
});
test('consumer event handlers are composed', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartSectorFocusHandlersTest');
// Focus the chart container directly (triggers onFocus)
const chartContainer = page.locator('[aria-roledescription="chart"]');
@@ -173,7 +154,7 @@ test.describe('PieChart', () => {
});
test('activeSegment out of bounds is clamped', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartSectorFocusOutOfBoundsTest');
await page.getByText('before').focus();
await page.keyboard.press('Tab');
@@ -182,7 +163,7 @@ test.describe('PieChart', () => {
});
test('dataset shrink resets keyboard state', async ({ mount, page }) => {
- await mount();
+ await mount('PieChart/PieChartSectorFocusDatasetShrinkTest');
// Tab past "shrink" button into chart, then into sector mode
await page.getByText('before').focus();
diff --git a/packages/charts/src/components/RadarChart/test/RadarChartTestComponents.tsx b/packages/charts/src/components/RadarChart/test/RadarChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/RadarChart/test/RadarChartTestComponents.tsx
rename to packages/charts/src/components/RadarChart/test/RadarChart.gallery.tsx
diff --git a/packages/charts/src/components/RadarChart/test/RadarChart.spec.tsx b/packages/charts/src/components/RadarChart/test/RadarChart.spec.tsx
index 3f4a73765e8..8abcce41c61 100644
--- a/packages/charts/src/components/RadarChart/test/RadarChart.spec.tsx
+++ b/packages/charts/src/components/RadarChart/test/RadarChart.spec.tsx
@@ -1,33 +1,17 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
-import { complexDataSet } from '../../../resources/DemoProps.js';
-import { testLoadingStates, testPassThroughProps } from '../../../test-utils/sharedTests.js';
-import { RadarChart } from '../index.js';
-import {
- RadarChartClickTest,
- RadarChartDataPointClickTest,
- RadarChartLegendConfigTest,
-} from './RadarChartTestComponents.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
+import { testLoadingStates, testPassThroughProps } from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('RadarChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount(
- ,
- );
+ await mount('ChartHarness/Chart', { chart: 'RadarChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-radar')).toHaveCount(3);
await expect(page.locator('.recharts-radar-polygon')).toHaveCount(3);
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('RadarChart/RadarChartClickTest');
await page.getByText('January').click();
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -40,26 +24,17 @@ test.describe('RadarChart', () => {
await expect(page.getByTestId('last-legend-datakey')).toHaveText('users');
});
- testLoadingStates(
- RadarChart,
- {
- dataset: complexDataSet,
- dimensions: [{ accessor: 'name', interval: 0 }],
- measures: [{ accessor: 'users', label: 'Users' }],
- },
- { dimensions: [], measures: [] },
- '.recharts-radar',
- );
+ testLoadingStates('RadarChart', '.recharts-radar');
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('RadarChart/RadarChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
- testPassThroughProps(RadarChart, { dimensions: [], measures: [] });
+ testPassThroughProps('RadarChart');
test('onDataPointClick', async ({ mount, page }) => {
- await mount();
+ await mount('RadarChart/RadarChartDataPointClickTest');
// RadarChart fires onDataPointClick via activeDot on .
// Hover the chart to activate a data index, making the active dot appear.
diff --git a/packages/charts/src/components/RadialChart/test/RadialChartTestComponents.tsx b/packages/charts/src/components/RadialChart/test/RadialChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/RadialChart/test/RadialChartTestComponents.tsx
rename to packages/charts/src/components/RadialChart/test/RadialChart.gallery.tsx
diff --git a/packages/charts/src/components/RadialChart/test/RadialChart.spec.tsx b/packages/charts/src/components/RadialChart/test/RadialChart.spec.tsx
index d6a6f96ce03..158be4c801c 100644
--- a/packages/charts/src/components/RadialChart/test/RadialChart.spec.tsx
+++ b/packages/charts/src/components/RadialChart/test/RadialChart.spec.tsx
@@ -1,11 +1,10 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
-import { testLoadingStates, testPassThroughProps } from '../../../test-utils/sharedTests.js';
-import { RadialChart } from '../index.js';
-import { RadialChartClickTest } from './RadialChartTestComponents.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
+import { testLoadingStates, testPassThroughProps } from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
test.describe('RadialChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount();
+ await mount('ChartHarness/Chart', { chart: 'RadialChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-area')).toHaveCount(1);
await expect(page.locator('.recharts-radial-bar-sectors')).toHaveCount(1);
@@ -13,7 +12,7 @@ test.describe('RadialChart', () => {
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('RadialChart/RadialChartClickTest');
const sector = page.locator('.recharts-radial-bar-sector');
await expect(sector).toBeVisible();
await sector.dispatchEvent('click');
@@ -21,7 +20,7 @@ test.describe('RadialChart', () => {
await expect(page.getByTestId('last-payload-value')).toHaveText('67');
});
- testPassThroughProps(RadialChart, {});
+ testPassThroughProps('RadialChart');
- testLoadingStates(RadialChart, { value: 67, displayValue: '67%' }, {}, '.recharts-radial-bar-sectors');
+ testLoadingStates('RadialChart', '.recharts-radial-bar-sectors');
});
diff --git a/packages/charts/src/components/ScatterChart/test/ScatterChartTestComponents.tsx b/packages/charts/src/components/ScatterChart/test/ScatterChart.gallery.tsx
similarity index 100%
rename from packages/charts/src/components/ScatterChart/test/ScatterChartTestComponents.tsx
rename to packages/charts/src/components/ScatterChart/test/ScatterChart.gallery.tsx
diff --git a/packages/charts/src/components/ScatterChart/test/ScatterChart.spec.tsx b/packages/charts/src/components/ScatterChart/test/ScatterChart.spec.tsx
index 5377c91efab..4388a29267e 100644
--- a/packages/charts/src/components/ScatterChart/test/ScatterChart.spec.tsx
+++ b/packages/charts/src/components/ScatterChart/test/ScatterChart.spec.tsx
@@ -1,23 +1,8 @@
import type { Page } from '@playwright/test';
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import { scatterComplexDataSet } from '../../../resources/DemoProps.js';
-import { testLoadingStates, testPassThroughProps } from '../../../test-utils/sharedTests.js';
-import { ScatterChart } from '../index.js';
-import {
- ScatterChartAccessibilityTest,
- ScatterChartClickTest,
- ScatterChartEmptyAccessibilityTest,
- ScatterChartEmptyTest,
- ScatterChartLegendConfigTest,
- ScatterChartMultiDatasetAccessibilityTest,
- ScatterChartMultipleChartsTest,
-} from './ScatterChartTestComponents.js';
-
-const measures = [
- { accessor: 'users', label: 'Number', axis: 'x' as const },
- { accessor: 'sessions', label: 'Sessions', axis: 'y' as const },
- { accessor: 'volume', axis: 'z' as const },
-];
+import { testLoadingStates, testPassThroughProps } from '../../../test-utils/chartGalleryTests.js';
+import type { Chart } from '../../../test-utils/ChartHarness.gallery.js';
async function expectActivePointLabel(page: Page, containerSelector: string, ...matchers: string[]) {
const container = page.locator(containerSelector).first();
@@ -32,14 +17,14 @@ async function expectActivePointLabel(page: Page, containerSelector: string, ...
test.describe('ScatterChart', () => {
test('Basic', async ({ mount, page }) => {
- await mount();
+ await mount('ChartHarness/Chart', { chart: 'ScatterChart' });
await expect(page.locator('.recharts-responsive-container')).toBeVisible();
await expect(page.locator('.recharts-scatter')).toHaveCount(2);
await expect(page.locator('.recharts-symbols[name="APJ"]')).toHaveCount(12);
});
test('click handlers', async ({ mount, page }) => {
- await mount();
+ await mount('ScatterChart/ScatterChartClickTest');
await page.locator('[name="Users"]').first().click();
await expect(page.getByTestId('click-count')).toHaveText('1');
@@ -50,10 +35,10 @@ test.describe('ScatterChart', () => {
await expect(page.getByTestId('last-legend-value')).toHaveText('Users');
});
- testLoadingStates(ScatterChart, { dataset: scatterComplexDataSet, measures }, { measures: [] }, '.recharts-scatter');
+ testLoadingStates('ScatterChart', '.recharts-scatter');
test('accessibilityLayer: keyboard navigation, Enter, blur/re-focus, consumer handlers', async ({ mount, page }) => {
- await mount();
+ await mount('ScatterChart/ScatterChartAccessibilityTest');
const containerSelector = '[aria-roledescription="chart"]';
await expect(page.locator('[role="img"][aria-label]')).toHaveCount(3);
@@ -114,7 +99,7 @@ test.describe('ScatterChart', () => {
});
test('accessibilityLayer: multi-dataset points sorted by X then datasetIndex', async ({ mount, page }) => {
- await mount();
+ await mount('ScatterChart/ScatterChartMultiDatasetAccessibilityTest');
const containerSelector = '[aria-roledescription="chart"]';
await expect(page.locator('[role="img"][aria-label]')).toHaveCount(3);
@@ -130,7 +115,7 @@ test.describe('ScatterChart', () => {
});
test('accessibilityLayer: multiple charts', async ({ mount, page }) => {
- await mount();
+ await mount('ScatterChart/ScatterChartMultipleChartsTest');
// Verify unique IDs across all points
const ids = await page.locator('[role="img"][id]').evaluateAll((els) => els.map((el) => el.id));
@@ -149,13 +134,13 @@ test.describe('ScatterChart', () => {
});
test('empty dataset (accessibilityLayer: false)', async ({ mount, page }) => {
- await mount();
+ await mount('ScatterChart/ScatterChartEmptyTest');
await expect(page.locator('.recharts-scatter')).not.toBeAttached();
await expect(page.getByText('Loading...')).toBeAttached();
});
test('empty dataset (accessibilityLayer: true)', async ({ mount, page }) => {
- await mount();
+ await mount('ScatterChart/ScatterChartEmptyAccessibilityTest');
await expect(page.locator('.recharts-scatter')).not.toBeAttached();
await expect(page.getByText('Loading...')).toBeAttached();
const chart = page.locator('[aria-roledescription="chart"]');
@@ -164,9 +149,9 @@ test.describe('ScatterChart', () => {
});
test('legendConfig', async ({ mount, page }) => {
- await mount();
+ await mount('ScatterChart/ScatterChartLegendConfigTest');
await expect(page.getByTestId('catval').first()).toBeVisible();
});
- testPassThroughProps(ScatterChart, { measures: [] });
+ testPassThroughProps('ScatterChart');
});
diff --git a/packages/charts/src/components/TimelineChart/test/TimelineChartTestComponents.tsx b/packages/charts/src/components/TimelineChart/test/TimelineChart.gallery.tsx
similarity index 96%
rename from packages/charts/src/components/TimelineChart/test/TimelineChartTestComponents.tsx
rename to packages/charts/src/components/TimelineChart/test/TimelineChart.gallery.tsx
index edd454768c3..0a1295b55a3 100644
--- a/packages/charts/src/components/TimelineChart/test/TimelineChartTestComponents.tsx
+++ b/packages/charts/src/components/TimelineChart/test/TimelineChart.gallery.tsx
@@ -6,6 +6,16 @@ import { TimelineChartAnnotation } from '../TimelineChartAnnotation.js';
// --- Basic rendering tests (no state needed, used directly in spec) ---
+export function BasicDatasetTest() {
+ return (
+
+ );
+}
+
// --- Connection layer toggle ---
export function ConnectionLayerToggle() {
const [showConn, toggleShowConn] = useReducer((prev) => !prev, undefined);
diff --git a/packages/charts/src/components/TimelineChart/test/TimelineChart.spec.tsx b/packages/charts/src/components/TimelineChart/test/TimelineChart.spec.tsx
index 230c6780a22..0513858265a 100644
--- a/packages/charts/src/components/TimelineChart/test/TimelineChart.spec.tsx
+++ b/packages/charts/src/components/TimelineChart/test/TimelineChart.spec.tsx
@@ -1,5 +1,4 @@
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
-import { TimelineChart } from '../index.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
import {
HOVER_OPACITY,
MOUSE_CURSOR_AUTO,
@@ -7,25 +6,6 @@ import {
MOUSE_CURSOR_GRABBING,
NORMAL_OPACITY,
} from '../util/constants.js';
-import {
- AnnotationLayerToggle,
- AnnotationPositionTest,
- ColumnLabelsContinuousTest,
- ColumnLabelsDiscreteTest,
- ColumnLabelsDiscreteWithLabelsTest,
- ConnectionLayerToggle,
- IllegalConnectionTest1,
- IllegalConnectionTest2,
- InvalidDiscreteLabelTest,
- LayerStructureTest,
- MouseCursorTest,
- RowHeightTest,
- StartPropTest,
- TooltipHiddenTest,
- TooltipOpacityTest,
- UnitAndTitlesTest,
- ValueFormatTest,
-} from './TimelineChartTestComponents.js';
// Tests dropped during the Cypress → Playwright migration:
// - "TimelineChartBody: scales when the mouse wheel event happens": the wheel
@@ -36,13 +16,7 @@ import {
test.describe('TimelineChart', () => {
test('renders TimelineChart with dataset', async ({ mount, page }) => {
- await mount(
- ,
- );
+ await mount('TimelineChart/BasicDatasetTest');
const tlc = page.getByTestId('tlc');
await expect(tlc).toBeVisible();
const tagName = await tlc.evaluate((el) => el.tagName);
@@ -50,21 +24,21 @@ test.describe('TimelineChart', () => {
});
test('calls the valueFormat callback & renders labels', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/ValueFormatTest');
for (let i = 0; i <= 150; i += 30) {
await expect(page.getByText(`${i}-formatted`, { exact: true })).toBeVisible();
}
});
test('render connection layer', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/ConnectionLayerToggle');
await expect(page.locator('[data-component-name="TimelineChartConnectionLayer"]')).not.toBeAttached();
await page.getByText('Toggle Connection').click();
await expect(page.locator('[data-component-name="TimelineChartConnectionLayer"]')).toBeVisible();
});
test('render annotation layer', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/AnnotationLayerToggle');
await expect(page.locator('[data-component-name="TimelineChartAnnotationLayer"]')).not.toBeAttached();
await page.getByText('Toggle Annotations', { exact: true }).click();
await expect(page.locator('[data-component-name="TimelineChartAnnotationLayer"]')).not.toBeAttached();
@@ -79,7 +53,9 @@ test.describe('TimelineChart', () => {
test('throws InvalidDiscreteLabelError', async ({ mount, page }) => {
const errors: Error[] = [];
page.on('pageerror', (err) => errors.push(err));
- await mount();
+ // The component throws in an effect; with flushSync that can reject mount() too — the error
+ // still reaches `pageerror`, which is what we assert on, so ignore the mount rejection.
+ await mount('TimelineChart/InvalidDiscreteLabelTest').catch(() => undefined);
await expect
.poll(() => errors.some((e) => e.name === 'InvalidDiscreteLabelError' || e.message.includes('discreteLabels')))
.toBe(true);
@@ -88,7 +64,7 @@ test.describe('TimelineChart', () => {
test('throws IllegalConnectionError (1)', async ({ mount, page }) => {
const errors: Error[] = [];
page.on('pageerror', (err) => errors.push(err));
- await mount();
+ await mount('TimelineChart/IllegalConnectionTest1').catch(() => undefined);
await expect
.poll(() => errors.some((e) => e.name === 'IllegalConnectionError' || e.message.includes('connection')))
.toBe(true);
@@ -97,14 +73,14 @@ test.describe('TimelineChart', () => {
test('throws IllegalConnectionError (2)', async ({ mount, page }) => {
const errors: Error[] = [];
page.on('pageerror', (err) => errors.push(err));
- await mount();
+ await mount('TimelineChart/IllegalConnectionTest2').catch(() => undefined);
await expect
.poll(() => errors.some((e) => e.name === 'IllegalConnectionError' || e.message.includes('connection')))
.toBe(true);
});
test('shows the right mouse cursor', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/MouseCursorTest');
const bodyContainer = page.locator('[data-component-name="TimelineChartBodyContainer"]');
await expect(bodyContainer).toHaveCSS('cursor', MOUSE_CURSOR_AUTO);
@@ -124,7 +100,7 @@ test.describe('TimelineChart', () => {
});
test('TimelineChartAnnotation: positions itself correctly in the parent', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/AnnotationPositionTest');
await expect(page.locator('[data-component-name="TimelineChartAnnotation"]')).toHaveCSS(
'inset-block-start',
'40px',
@@ -132,7 +108,7 @@ test.describe('TimelineChart', () => {
});
test('TimelineChartRow: tooltip & opacity for tasks', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/TooltipOpacityTest');
const task = page.locator('[data-component-name="TimelineChartTask"]').first();
await expect(task).toHaveCSS('opacity', `${NORMAL_OPACITY}`);
@@ -146,7 +122,7 @@ test.describe('TimelineChart', () => {
});
test('TimelineChartRow: tooltip & opacity for milestones', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/TooltipOpacityTest');
const milestoneRect = page.locator('[data-component-name="TimelineChartMilestone"] > rect');
await expect(milestoneRect).toHaveCSS('opacity', `${NORMAL_OPACITY}`);
@@ -159,7 +135,7 @@ test.describe('TimelineChart', () => {
});
test('TimelineChartRow: hideTooltip still changes opacity', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/TooltipHiddenTest');
const task = page.locator('[data-component-name="TimelineChartTask"]').first();
await expect(task).toHaveCSS('opacity', `${NORMAL_OPACITY}`);
@@ -179,7 +155,7 @@ test.describe('TimelineChart', () => {
});
test('TimelineChartLayer', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/LayerStructureTest');
const gridLayer = page.locator('[data-component-name="TimelineChartGridLayer"]');
await expect(gridLayer).toHaveCSS('pointer-events', 'none');
@@ -203,7 +179,7 @@ test.describe('TimelineChart', () => {
});
test('TimelineChartHeaders: ColumnLabels continuous', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/ColumnLabelsContinuousTest');
for (let i = 0; i <= 10; i += 2) {
await expect(page.getByText(`${i}`, { exact: true })).toBeVisible();
}
@@ -211,7 +187,7 @@ test.describe('TimelineChart', () => {
});
test('TimelineChartHeaders: ColumnLabels discrete', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/ColumnLabelsDiscreteTest');
for (let i = 0; i <= 9; i++) {
await expect(page.getByText(`${i}`, { exact: true })).toBeVisible();
}
@@ -219,19 +195,19 @@ test.describe('TimelineChart', () => {
});
test('TimelineChartHeaders: ColumnLabels discrete with labels', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/ColumnLabelsDiscreteWithLabelsTest');
await expect(page.getByText('label')).toHaveCount(8);
await expect(page.getByText('one')).toBeVisible();
await expect(page.getByText('two')).toBeVisible();
});
test('rowHeight', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/RowHeightTest');
await expect(page.locator('[data-component-name="TimelineChartRow"]').first()).toHaveAttribute('height', '200');
});
test('unit and titles', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/UnitAndTitlesTest');
await expect(page.getByText('Activities')).not.toBeAttached();
await expect(page.getByText('Duration')).not.toBeAttached();
await expect(page.getByText('columnTitle (unit)')).toBeVisible();
@@ -239,7 +215,7 @@ test.describe('TimelineChart', () => {
});
test('start', async ({ mount, page }) => {
- await mount();
+ await mount('TimelineChart/StartPropTest');
for (let i = 5; i <= 15; i += 2) {
await expect(page.getByText(`${i}`, { exact: true })).toBeVisible();
}
diff --git a/packages/charts/src/hooks/test/HookTestComponents.tsx b/packages/charts/src/hooks/test/HookTestComponents.gallery.tsx
similarity index 100%
rename from packages/charts/src/hooks/test/HookTestComponents.tsx
rename to packages/charts/src/hooks/test/HookTestComponents.gallery.tsx
diff --git a/packages/charts/src/hooks/test/useLabelFormatter.spec.tsx b/packages/charts/src/hooks/test/useLabelFormatter.spec.tsx
index c128a6e0317..de383e5c466 100644
--- a/packages/charts/src/hooks/test/useLabelFormatter.spec.tsx
+++ b/packages/charts/src/hooks/test/useLabelFormatter.spec.tsx
@@ -1,19 +1,18 @@
-import { expect, test } from '../../../../../playwright/fixtures/main-fixtures.js';
-import { LabelFormatterInvalid, LabelFormatterNull, LabelFormatterValid } from './HookTestComponents.js';
+import { expect, test } from '../../../../../playwright/fixtures/gallery-fixtures.js';
test.describe('useLabelFormatter', () => {
test('should return value when no formatter is present', async ({ mount }) => {
- const component = await mount();
+ const component = await mount('HookTestComponents/LabelFormatterNull');
await expect(component.getByText('100')).toBeVisible();
});
test('should not crash on invalid formatter', async ({ mount }) => {
- const component = await mount();
+ const component = await mount('HookTestComponents/LabelFormatterInvalid');
await expect(component.getByText('100')).toBeVisible();
});
test('should format the value with a valid formatter', async ({ mount }) => {
- const component = await mount();
+ const component = await mount('HookTestComponents/LabelFormatterValid');
await expect(component.getByText('10')).toBeVisible();
});
});
diff --git a/packages/charts/src/hooks/test/usePrepareDimensionsAndMeasures.spec.tsx b/packages/charts/src/hooks/test/usePrepareDimensionsAndMeasures.spec.tsx
index 95536903127..755efc551d4 100644
--- a/packages/charts/src/hooks/test/usePrepareDimensionsAndMeasures.spec.tsx
+++ b/packages/charts/src/hooks/test/usePrepareDimensionsAndMeasures.spec.tsx
@@ -1,13 +1,8 @@
-import { expect, test } from '../../../../../playwright/fixtures/main-fixtures.js';
-import {
- PrepareDimensionsDefault,
- PrepareDimensionsNoOverwrite,
- PrepareDimensionsWithDefaults,
-} from './HookTestComponents.js';
+import { expect, test } from '../../../../../playwright/fixtures/gallery-fixtures.js';
test.describe('usePrepareDimensionsAndMeasures', () => {
test('should not throw an error when no defaults are passed', async ({ mount, page }) => {
- await mount();
+ await mount('HookTestComponents/PrepareDimensionsDefault');
const resultText = await page.getByTestId('result').textContent();
const result = JSON.parse(resultText);
expect(result).toEqual({
@@ -19,7 +14,7 @@ test.describe('usePrepareDimensionsAndMeasures', () => {
});
test('should merge defaults', async ({ mount, page }) => {
- await mount();
+ await mount('HookTestComponents/PrepareDimensionsWithDefaults');
const resultText = await page.getByTestId('result').textContent();
const result = JSON.parse(resultText);
expect(result).toEqual({
@@ -31,7 +26,7 @@ test.describe('usePrepareDimensionsAndMeasures', () => {
});
test('should merge defaults but not overwrite existing properties', async ({ mount, page }) => {
- await mount();
+ await mount('HookTestComponents/PrepareDimensionsNoOverwrite');
const resultText = await page.getByTestId('result').textContent();
const result = JSON.parse(resultText);
expect(result).toEqual({
diff --git a/packages/charts/src/hooks/test/useTooltipFormatter.spec.tsx b/packages/charts/src/hooks/test/useTooltipFormatter.spec.tsx
index 9ce93831e69..2905c60926c 100644
--- a/packages/charts/src/hooks/test/useTooltipFormatter.spec.tsx
+++ b/packages/charts/src/hooks/test/useTooltipFormatter.spec.tsx
@@ -1,19 +1,18 @@
-import { expect, test } from '../../../../../playwright/fixtures/main-fixtures.js';
-import { TooltipFormatterInvalid, TooltipFormatterNoFormatter, TooltipFormatterValid } from './HookTestComponents.js';
+import { expect, test } from '../../../../../playwright/fixtures/gallery-fixtures.js';
test.describe('useTooltipFormatter', () => {
test('should return value when no formatter is present', async ({ mount }) => {
- const component = await mount();
+ const component = await mount('HookTestComponents/TooltipFormatterNoFormatter');
await expect(component.getByText('100')).toBeVisible();
});
test('should not crash on invalid formatter', async ({ mount }) => {
- const component = await mount();
+ const component = await mount('HookTestComponents/TooltipFormatterInvalid');
await expect(component.getByText('100')).toBeVisible();
});
test('should format the value with a valid formatter', async ({ mount }) => {
- const component = await mount();
+ const component = await mount('HookTestComponents/TooltipFormatterValid');
await expect(component.getByText('10')).toBeVisible();
});
});
diff --git a/packages/charts/src/resources/chartHarnessData.ts b/packages/charts/src/resources/chartHarnessData.ts
new file mode 100644
index 00000000000..576bd49af69
--- /dev/null
+++ b/packages/charts/src/resources/chartHarnessData.ts
@@ -0,0 +1,123 @@
+/**
+ * @fileoverview Per-chart prop sets consumed by the shared chart gallery harness.
+ *
+ * Add an entry here for every chart whose spec uses the shared tests in `chartGalleryTests.tsx`:
+ * `baseProps` (populated chart), `emptyProps` (placeholder path), and `stack` (only for charts
+ * whose spec calls `testStackAggregateTotals`). Keep values plain and serializable — no React,
+ * no functions — so both the browser harness and the Node-side test helpers can import this.
+ */
+
+import { complexDataSet, scatterComplexDataSet, simpleDataSet } from '../resources/DemoProps.js';
+
+// Serializable chart prop sets for the gallery harness + the Node-side shared tests.
+// Plain data only, so both the browser harness and `chartGalleryTests.tsx` can import it.
+export interface ChartHarnessEntry {
+ baseProps: Record;
+ emptyProps: Record;
+ stack?: {
+ baseProps: Record;
+ measures: Array<{ accessor: string; stackId?: string; label?: string; type?: string }>;
+ };
+}
+
+const catDimensions = [{ accessor: 'name', interval: 0 }];
+const catMeasures = [
+ { accessor: 'users', label: 'Users' },
+ { accessor: 'sessions', label: 'Active Sessions' },
+ { accessor: 'volume', label: 'Vol.' },
+];
+const stackMeasures = [
+ { accessor: 'users', stackId: 'A', label: 'Users' },
+ { accessor: 'sessions', stackId: 'A', label: 'Active Sessions' },
+];
+const stackBaseProps = { dataset: complexDataSet.slice(0, 3), dimensions: catDimensions };
+
+// dimension/measure (singular) charts
+const singularDimension = { accessor: 'name' };
+const singularMeasure = { accessor: 'users' };
+
+// scatter measures
+const scatterMeasures = [
+ { accessor: 'users', label: 'Number', axis: 'x' as const },
+ { accessor: 'sessions', label: 'Sessions', axis: 'y' as const },
+ { accessor: 'volume', axis: 'z' as const },
+];
+
+export const chartHarnessData: Record = {
+ BarChart: {
+ baseProps: { dataset: complexDataSet, dimensions: catDimensions, measures: catMeasures },
+ emptyProps: { dimensions: [], measures: [] },
+ stack: { baseProps: stackBaseProps, measures: stackMeasures },
+ },
+ ColumnChart: {
+ baseProps: { dataset: complexDataSet, dimensions: catDimensions, measures: catMeasures },
+ emptyProps: { dimensions: [], measures: [] },
+ stack: { baseProps: stackBaseProps, measures: stackMeasures },
+ },
+ ComposedChart: {
+ baseProps: {
+ dataset: complexDataSet,
+ dimensions: catDimensions,
+ measures: [
+ { accessor: 'users', label: 'Users', type: 'line' as const },
+ { accessor: 'sessions', label: 'Active Sessions', type: 'bar' as const },
+ { accessor: 'volume', label: 'Vol.', type: 'area' as const },
+ ],
+ },
+ emptyProps: { dimensions: [], measures: [] },
+ stack: {
+ baseProps: stackBaseProps,
+ measures: [
+ { accessor: 'users', stackId: 'A', label: 'Users', type: 'bar' as const },
+ { accessor: 'sessions', stackId: 'A', label: 'Active Sessions', type: 'bar' as const },
+ ],
+ },
+ },
+ BulletChart: {
+ baseProps: {
+ dataset: complexDataSet,
+ dimensions: catDimensions,
+ measures: [
+ { accessor: 'users', label: 'Users', type: 'primary' as const },
+ { accessor: 'sessions', label: 'Active Sessions', type: 'comparison' as const },
+ { accessor: 'volume', label: 'Vol.', type: 'additional' as const },
+ ],
+ },
+ emptyProps: { dimensions: [], measures: [] },
+ },
+ ColumnChartWithTrend: {
+ baseProps: {
+ dataset: complexDataSet,
+ dimensions: catDimensions,
+ measures: [
+ { accessor: 'users', label: 'Users', type: 'line' as const },
+ { accessor: 'sessions', label: 'Active Sessions', type: 'bar' as const },
+ ],
+ },
+ emptyProps: { dimensions: [], measures: [] },
+ },
+ LineChart: {
+ baseProps: { dataset: complexDataSet, dimensions: catDimensions, measures: catMeasures },
+ emptyProps: { dimensions: [], measures: [] },
+ },
+ RadarChart: {
+ baseProps: { dataset: complexDataSet, dimensions: catDimensions, measures: catMeasures },
+ emptyProps: { dimensions: [], measures: [] },
+ },
+ DonutChart: {
+ baseProps: { dataset: simpleDataSet, dimension: singularDimension, measure: singularMeasure },
+ emptyProps: { dimension: {}, measure: {} },
+ },
+ PieChart: {
+ baseProps: { dataset: simpleDataSet, dimension: singularDimension, measure: singularMeasure },
+ emptyProps: { dimension: {}, measure: {} },
+ },
+ RadialChart: {
+ baseProps: { value: 67, displayValue: '67%' },
+ emptyProps: {},
+ },
+ ScatterChart: {
+ baseProps: { dataset: scatterComplexDataSet, measures: scatterMeasures },
+ emptyProps: { measures: [] },
+ },
+};
diff --git a/packages/charts/src/test-utils/ChartHarness.gallery.tsx b/packages/charts/src/test-utils/ChartHarness.gallery.tsx
new file mode 100644
index 00000000000..c2d2df9d083
--- /dev/null
+++ b/packages/charts/src/test-utils/ChartHarness.gallery.tsx
@@ -0,0 +1,69 @@
+/**
+ * @fileoverview Browser-side gallery story shared by every chart's parametric tests.
+ *
+ * Exposes one story (`Chart`) that renders a chart resolved by string key — the mount boundary
+ * only carries serializable data, so specs pass the chart name, not the component. When adding a
+ * chart to the shared tests, register its component in `chartRegistry` here and its prop sets in
+ * `chartHarnessData.ts`. Keep this file limited to resolution + rendering; per-chart data and the
+ * test logic live in `chartHarnessData.ts` and `chartGalleryTests.tsx` respectively.
+ */
+
+import type { ComponentType } from 'react';
+import { BarChart } from '../components/BarChart/index.js';
+import { BulletChart } from '../components/BulletChart/index.js';
+import { ColumnChart } from '../components/ColumnChart/index.js';
+import { ColumnChartWithTrend } from '../components/ColumnChartWithTrend/index.js';
+import { ComposedChart } from '../components/ComposedChart/index.js';
+import { DonutChart } from '../components/DonutChart/index.js';
+import { LineChart } from '../components/LineChart/index.js';
+import { PieChart } from '../components/PieChart/index.js';
+import { RadarChart } from '../components/RadarChart/index.js';
+import { RadialChart } from '../components/RadialChart/index.js';
+import { ScatterChart } from '../components/ScatterChart/index.js';
+import { chartHarnessData } from '../resources/chartHarnessData.js';
+
+// Shared gallery story for the parametric chart tests. Specs pass a chart's string key
+// (components can't cross the mount boundary); it resolves the component + prop set here.
+const chartRegistry: Record> = {
+ BarChart,
+ BulletChart,
+ ColumnChart,
+ ColumnChartWithTrend,
+ ComposedChart,
+ DonutChart,
+ LineChart,
+ PieChart,
+ RadarChart,
+ RadialChart,
+ ScatterChart,
+};
+
+type HarnessProps = {
+ chart: string;
+ variant?: 'base' | 'empty' | 'stack';
+ overrides?: Record;
+};
+
+function resolve(chart: string) {
+ const Component = chartRegistry[chart];
+ const data = chartHarnessData[chart];
+ if (!Component || !data) {
+ throw new Error(
+ `ChartHarness: unknown chart "${chart}". Register it in ChartHarness.gallery.tsx + chartHarnessData.ts.`,
+ );
+ }
+ return { Component, data };
+}
+
+export const Chart = ({ chart, variant = 'base', overrides = {} }: HarnessProps) => {
+ const { Component, data } = resolve(chart);
+ let props: Record;
+ if (variant === 'empty') {
+ props = data.emptyProps;
+ } else if (variant === 'stack') {
+ props = { ...data.stack.baseProps, measures: data.stack.measures };
+ } else {
+ props = data.baseProps;
+ }
+ return ;
+};
diff --git a/packages/charts/src/test-utils/chartGalleryTests.tsx b/packages/charts/src/test-utils/chartGalleryTests.tsx
new file mode 100644
index 00000000000..995793e9acc
--- /dev/null
+++ b/packages/charts/src/test-utils/chartGalleryTests.tsx
@@ -0,0 +1,111 @@
+/**
+ * @fileoverview Shared parametric chart tests, reused across chart specs.
+ *
+ * Each helper registers Playwright tests for one concern (loading states, zooming tool,
+ * pass-through props, stack totals) and is called from a chart's spec with that chart's string
+ * key. Add a new helper here when a behavior is shared by multiple charts; keep chart-specific
+ * tests in the individual spec files. Helpers mount the shared `ChartHarness/Chart` story, so the
+ * chart must be registered in `ChartHarness.gallery.tsx` + `chartHarnessData.ts` first.
+ */
+
+import { expect, test } from '../../../../playwright/fixtures/gallery-fixtures.js';
+import { chartHarnessData } from '../resources/chartHarnessData.js';
+import { assertPassThroughProps, passThroughProps } from './shared.js';
+
+// Gallery-model shared chart tests. Each helper takes a chart's string key and mounts the
+// shared `ChartHarness/Chart` story; the component + base props are resolved in the harness.
+const HARNESS = 'ChartHarness/Chart';
+
+/** Verifies the chart forwards standard HTML props onto its rendered root element. */
+export function testPassThroughProps(chart: string) {
+ test('Pass Through HTML Standard Props', async ({ mount, page }) => {
+ await mount(HARNESS, { chart, variant: 'empty', overrides: passThroughProps() });
+ await assertPassThroughProps(page);
+ });
+}
+
+/** Verifies the three ChartContainer paths: empty, empty+loading (no-op), data+loading (BusyIndicator). */
+export function testLoadingStates(chart: string, chartElementSelector: string) {
+ test('loading states', async ({ mount, page }) => {
+ const busyIndicator = page.locator('[data-component-name="ChartContainerBusyIndicator"]').first();
+ const chartElement = page.locator(chartElementSelector);
+ const loadingText = page.getByText('Loading...').first();
+
+ let result = await mount(HARNESS, { chart, variant: 'empty' });
+ await expect(loadingText).toBeAttached();
+ await expect(chartElement).not.toBeAttached();
+ await expect(busyIndicator).not.toBeAttached();
+ await result.unmount();
+
+ result = await mount(HARNESS, { chart, variant: 'empty', overrides: { loading: true } });
+ await expect(loadingText).toBeAttached();
+ await expect(chartElement).not.toBeAttached();
+ await expect(busyIndicator).not.toBeAttached();
+ await result.unmount();
+
+ await mount(HARNESS, { chart, variant: 'base', overrides: { loading: true } });
+ await expect(busyIndicator).toBeAttached();
+ });
+}
+
+/** Verifies `chartConfig.zoomingTool`: true → brush, false → none, custom → styled brush. */
+export function testZoomingTool(chart: string) {
+ test.describe('zoomingTool', () => {
+ test('enabled', async ({ mount, page }) => {
+ await mount(HARNESS, { chart, overrides: { chartConfig: { zoomingTool: true } } });
+ await expect(page.locator('.recharts-brush')).toBeVisible();
+ });
+
+ test('disabled', async ({ mount, page }) => {
+ await mount(HARNESS, { chart, overrides: { chartConfig: { zoomingTool: false } } });
+ await expect(page.locator('.recharts-brush')).not.toBeAttached();
+ });
+
+ test('custom config', async ({ mount, page }) => {
+ await mount(HARNESS, { chart, overrides: { chartConfig: { zoomingTool: { stroke: 'red' } } } });
+ await expect(page.locator('.recharts-brush')).toBeVisible();
+ await expect(page.locator('.recharts-brush [stroke="red"]')).toBeVisible();
+ });
+ });
+}
+
+/** Verifies `chartConfig.showStackAggregateTotals`: enabled → bold totals + tooltip, disabled → none. */
+export function testStackAggregateTotals(chart: string) {
+ const stack = chartHarnessData[chart]?.stack;
+ if (!stack) {
+ throw new Error(`testStackAggregateTotals: chart "${chart}" has no stack data in chartHarnessData.ts`);
+ }
+ const stackedAccessors = stack.measures.filter((m) => m.stackId).map((m) => m.accessor);
+ const expectedTotals = (stack.baseProps.dataset as Record[]).map((entry) =>
+ stackedAccessors.reduce((sum, acc) => sum + (Number(entry[acc]) || 0), 0),
+ );
+
+ test.describe('showStackAggregateTotals', () => {
+ test('enabled', async ({ mount, page }) => {
+ await mount(HARNESS, { chart, variant: 'stack', overrides: { chartConfig: { showStackAggregateTotals: true } } });
+
+ for (const total of expectedTotals) {
+ await expect(page.locator(`text[font-weight="bold"]`).filter({ hasText: String(total) })).toBeAttached();
+ }
+
+ const wrapper = page.locator('.recharts-wrapper');
+ await wrapper.hover({ position: { x: 200, y: 100 }, force: true });
+ const tooltipTotal = page.locator('.recharts-tooltip-item').last();
+ await expect(tooltipTotal).toContainText('Total');
+ await expect(tooltipTotal).toHaveCSS('font-weight', '700');
+ const tooltipText = await tooltipTotal.textContent();
+ const totalValue = Number(tooltipText.replace(/\D/g, ''));
+ expect(expectedTotals).toContain(totalValue);
+ });
+
+ test('disabled', async ({ mount, page }) => {
+ await mount(HARNESS, {
+ chart,
+ variant: 'stack',
+ overrides: { chartConfig: { showStackAggregateTotals: false } },
+ });
+ await expect(page.locator('.recharts-bar-rectangles').first()).toBeAttached();
+ await expect(page.locator('text[font-weight="bold"]')).not.toBeAttached();
+ });
+ });
+}
diff --git a/packages/charts/src/test-utils/shared.tsx b/packages/charts/src/test-utils/shared.tsx
index 99d782a1067..a412ceb568d 100644
--- a/packages/charts/src/test-utils/shared.tsx
+++ b/packages/charts/src/test-utils/shared.tsx
@@ -1,4 +1,4 @@
-import { expect } from '@playwright/experimental-ct-react';
+import { expect } from '@playwright/test';
import type { Page } from '@playwright/test';
export async function assertPassThroughProps(page: Page) {
diff --git a/packages/charts/src/test-utils/sharedTests.tsx b/packages/charts/src/test-utils/sharedTests.tsx
deleted file mode 100644
index b836b614c1f..00000000000
--- a/packages/charts/src/test-utils/sharedTests.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import type { ComponentType } from 'react';
-import { expect, test } from '../../../../playwright/fixtures/main-fixtures.js';
-import { assertPassThroughProps, passThroughProps } from './shared.js';
-
-/**
- * Registers a `Pass Through HTML Standard Props` test that verifies that the chart forwards
- * the standard HTML props (data-testid, data-*, aria-*, id, className, style.pointerEvents,
- * title, custom attribute) onto its rendered root element.
- */
-export function testPassThroughProps>(Chart: ComponentType, emptyProps: T) {
- test('Pass Through HTML Standard Props', async ({ mount, page }) => {
- await mount();
- await assertPassThroughProps(page);
- });
-}
-
-/**
- * Registers a `loading states` test that verifies the three distinct rendering paths in
- * ChartContainer:
- * - empty dataset → Placeholder, no BusyIndicator, no chart elements (loading prop has no effect)
- * - empty dataset + loading=true → identical to empty (loading is a no-op without data)
- * - has data + loading=true → BusyIndicator overlay on top of the rendered chart
- *
- * @param chartElementSelector A selector unique to the chart's rendered shape, e.g. `.recharts-bar`
- * for BarChart or `.recharts-pie` for PieChart. Used to assert the chart isn't rendered in the
- * placeholder path.
- */
-export function testLoadingStates>(
- Chart: ComponentType,
- baseProps: T,
- emptyProps: T,
- chartElementSelector: string,
-) {
- test('loading states', async ({ mount, page }) => {
- const busyIndicator = page.locator('[data-component-name="ChartContainerBusyIndicator"]').first();
- const chartElement = page.locator(chartElementSelector);
- const loadingText = page.getByText('Loading...').first();
-
- let result = await mount();
- await expect(loadingText).toBeAttached();
- await expect(chartElement).not.toBeAttached();
- await expect(busyIndicator).not.toBeAttached();
- await result.unmount();
-
- result = await mount();
- await expect(loadingText).toBeAttached();
- await expect(chartElement).not.toBeAttached();
- await expect(busyIndicator).not.toBeAttached();
- await result.unmount();
-
- await mount();
- await expect(busyIndicator).toBeAttached();
- });
-}
-
-/**
- * Registers `zoomingTool` describe block with three sub-tests verifying the chart's
- * `chartConfig.zoomingTool` prop:
- * - `true` → recharts brush is rendered
- * - `false` → no brush
- * - `{ stroke: 'red' }` → brush rendered with the custom stroke color
- */
-export function testZoomingTool>(Chart: ComponentType, baseProps: T) {
- test.describe('zoomingTool', () => {
- test('enabled', async ({ mount, page }) => {
- await mount();
- await expect(page.locator('.recharts-brush')).toBeVisible();
- });
-
- test('disabled', async ({ mount, page }) => {
- await mount();
- await expect(page.locator('.recharts-brush')).not.toBeAttached();
- });
-
- test('custom config', async ({ mount, page }) => {
- await mount();
- await expect(page.locator('.recharts-brush')).toBeVisible();
- await expect(page.locator('.recharts-brush [stroke="red"]')).toBeVisible();
- });
- });
-}
-
-/**
- * Registers a `showStackAggregateTotals` describe block with two sub-tests verifying the
- * `chartConfig.showStackAggregateTotals` prop:
- * - enabled → stack totals rendered as bold labels, tooltip shows "Total : "
- * - disabled → no bold totals; bars still render
- *
- * Expected stack totals are computed from the dataset and the stacked measure accessors.
- */
-export function testStackAggregateTotals>(
- Chart: ComponentType,
- baseProps: T,
- stackMeasures: Array<{ accessor: string; stackId?: string; label?: string; type?: string }>,
-) {
- const stackedAccessors = stackMeasures.filter((m) => m.stackId).map((m) => m.accessor);
- const expectedTotals = (baseProps.dataset as Record[]).map((entry) =>
- stackedAccessors.reduce((sum, acc) => sum + (Number(entry[acc]) || 0), 0),
- );
-
- test.describe('showStackAggregateTotals', () => {
- test('enabled', async ({ mount, page }) => {
- await mount();
-
- for (const total of expectedTotals) {
- await expect(page.locator(`text[font-weight="bold"]`).filter({ hasText: String(total) })).toBeAttached();
- }
-
- const wrapper = page.locator('.recharts-wrapper');
- await wrapper.hover({ position: { x: 200, y: 100 }, force: true });
- const tooltipTotal = page.locator('.recharts-tooltip-item').last();
- await expect(tooltipTotal).toContainText('Total');
- await expect(tooltipTotal).toHaveCSS('font-weight', '700');
- const tooltipText = await tooltipTotal.textContent();
- const totalValue = Number(tooltipText.replace(/\D/g, ''));
- expect(expectedTotals).toContain(totalValue);
- });
-
- test('disabled', async ({ mount, page }) => {
- await mount();
- await expect(page.locator('.recharts-bar-rectangles').first()).toBeAttached();
- await expect(page.locator('text[font-weight="bold"]')).not.toBeAttached();
- });
- });
-}
diff --git a/packages/main/src/components/SelectDialog/test/SelectDialogTestComponents.tsx b/packages/main/src/components/SelectDialog/test/SelectDialog.gallery.tsx
similarity index 100%
rename from packages/main/src/components/SelectDialog/test/SelectDialogTestComponents.tsx
rename to packages/main/src/components/SelectDialog/test/SelectDialog.gallery.tsx
diff --git a/packages/main/src/components/SelectDialog/test/SelectDialog.spec.tsx b/packages/main/src/components/SelectDialog/test/SelectDialog.spec.tsx
index 0ba1ae03474..a1bd46b0873 100644
--- a/packages/main/src/components/SelectDialog/test/SelectDialog.spec.tsx
+++ b/packages/main/src/components/SelectDialog/test/SelectDialog.spec.tsx
@@ -1,19 +1,8 @@
-import { expect } from '@playwright/experimental-ct-react';
-import { test } from '../../../../../../playwright/fixtures/main-fixtures.js';
-import {
- SelectDialogBasicTestComp,
- SelectDialogHeaderTestComp,
- SelectDialogSelectionWithToggleTestComp,
- SelectDialogSearchTestComp,
- SelectDialogConfirmButtonTextTestComp,
- SelectDialogNumberOfSelectedItemsTestComp,
- SelectDialogCancelWithToggleTestComp,
- SelectDialogConfirmButtonPropsTestComp,
-} from './SelectDialogTestComponents.js';
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
test.describe('SelectDialog', () => {
test('Basic', async ({ mount, page }) => {
- await mount();
+ await mount('SelectDialog/SelectDialogBasicTestComp');
await expect(page.locator('[ui5-dialog]')).toBeVisible();
await expect(page.locator('[ui5-input][placeholder="Search"]')).toBeVisible();
await page.getByText('Cancel').click();
@@ -21,7 +10,7 @@ test.describe('SelectDialog', () => {
});
test('with headerText', async ({ mount, page, ui5wc }) => {
- await mount();
+ await mount('SelectDialog/SelectDialogHeaderTestComp');
const header = page.getByText('Select Dialog');
await expect(header).toHaveCSS('grid-column-start', 'titleStart');
await expect(header).toHaveCSS('grid-column-end', 'titleCenter');
@@ -40,7 +29,7 @@ test.describe('SelectDialog', () => {
});
test('selection', async ({ mount, page, ui5wc }) => {
- await mount();
+ await mount('SelectDialog/SelectDialogSelectionWithToggleTestComp');
const list = page.locator('[ui5-list]');
// Single mode - no rememberSelections
@@ -125,7 +114,7 @@ test.describe('SelectDialog', () => {
});
test('Search', async ({ mount, page, ui5wc }) => {
- await mount();
+ await mount('SelectDialog/SelectDialogSearchTestComp');
await expect(page.locator('[accessible-name="Reset"][ui5-icon]')).not.toBeVisible();
const input = page.locator('[ui5-input]');
@@ -163,7 +152,7 @@ test.describe('SelectDialog', () => {
});
test('confirmButtonText', async ({ mount, page }) => {
- await mount();
+ await mount('SelectDialog/SelectDialogConfirmButtonTextTestComp');
await expect(page.locator('[ui5-dialog]')).toBeVisible();
await page.getByText('Exterminate').click();
await expect(page.getByTestId('confirm-count')).toHaveText('1');
@@ -171,12 +160,12 @@ test.describe('SelectDialog', () => {
});
test('numberOfSelectedItems', async ({ mount, page }) => {
- await mount();
+ await mount('SelectDialog/SelectDialogNumberOfSelectedItemsTestComp');
await expect(page.getByText('Selected: 1337')).toBeVisible();
});
test('onCancel', async ({ mount, page, ui5wc }) => {
- await mount();
+ await mount('SelectDialog/SelectDialogCancelWithToggleTestComp');
// Single mode
await page.getByTestId('open-btn').click();
@@ -201,7 +190,7 @@ test.describe('SelectDialog', () => {
});
test('confirmButtonProps', async ({ mount, page }) => {
- await mount();
+ await mount('SelectDialog/SelectDialogConfirmButtonPropsTestComp');
const btn = page.getByTestId('confirmBtn');
await expect(btn).toBeVisible();
await expect(btn).toHaveAttribute('disabled');
diff --git a/packages/main/src/components/SplitterLayout/test/SplitterLayoutTestComponents.tsx b/packages/main/src/components/SplitterLayout/test/SplitterLayout.gallery.tsx
similarity index 84%
rename from packages/main/src/components/SplitterLayout/test/SplitterLayoutTestComponents.tsx
rename to packages/main/src/components/SplitterLayout/test/SplitterLayout.gallery.tsx
index d9a80f9a899..1a323264255 100644
--- a/packages/main/src/components/SplitterLayout/test/SplitterLayoutTestComponents.tsx
+++ b/packages/main/src/components/SplitterLayout/test/SplitterLayout.gallery.tsx
@@ -40,27 +40,25 @@ export const SplitterMoveResetTestComp = ({
);
};
-export const SplitterMultipleElementsTestComp = ({
- vertical,
- onBtnClick,
-}: {
- vertical: boolean;
- onBtnClick?: () => void;
-}) => {
+export const SplitterMultipleElementsTestComp = ({ vertical }: { vertical: boolean }) => {
+ const [clickCount, setClickCount] = useState(0);
return (
-
-
-
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ {clickCount}
+ >
);
};
diff --git a/packages/main/src/components/SplitterLayout/test/SplitterLayout.spec.tsx b/packages/main/src/components/SplitterLayout/test/SplitterLayout.spec.tsx
index 832eea03164..0b268c9a830 100644
--- a/packages/main/src/components/SplitterLayout/test/SplitterLayout.spec.tsx
+++ b/packages/main/src/components/SplitterLayout/test/SplitterLayout.spec.tsx
@@ -1,11 +1,10 @@
import type { Page } from '@playwright/test';
-import { expect, test } from '../../../../../../playwright/fixtures/main-fixtures.js';
-import {
+import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js';
+import type {
+ SplitterControlledTestComp,
SplitterMoveResetTestComp,
SplitterMultipleElementsTestComp,
- SplitterEmptyTestComp,
- SplitterControlledTestComp,
-} from './SplitterLayoutTestComponents.js';
+} from './SplitterLayout.gallery.js';
/**
* Move first separator 10 times via ArrowUp/ArrowLeft (RTL aware)
@@ -31,7 +30,7 @@ test.describe('SplitterLayout', () => {
for (const vertical of [false, true]) {
test(`Splitter Move & Reset - ${dir} - vertical: ${vertical}`, async ({ mount, page }) => {
await page.setViewportSize({ width: 2000, height: 2000 });
- await mount();
+ await mount('SplitterLayout/SplitterMoveResetTestComp', { vertical, dir });
const se1 = page.getByTestId('se1');
const se2 = page.getByTestId('se2');
@@ -69,18 +68,12 @@ test.describe('SplitterLayout', () => {
for (const vertical of [false, true]) {
test(`SplitterLayout w/ multiple SplitterElements - vertical: ${vertical}`, async ({ mount, page }) => {
- let clickCount = 0;
- await mount(
- {
- clickCount++;
- }}
- />,
- );
+ await mount('SplitterLayout/SplitterMultipleElementsTestComp', {
+ vertical,
+ });
await page.getByTestId('btn').click();
- expect(clickCount).toBe(1);
+ await expect(page.getByTestId('btn-click-count')).toHaveText('1');
// only one separator (resizable=false for #2)
const separators = page.locator('[role="separator"]');
@@ -103,7 +96,7 @@ test.describe('SplitterLayout', () => {
}
test('empty content', async ({ mount, page }) => {
- await mount();
+ await mount('SplitterLayout/SplitterEmptyTestComp');
const sl = page.getByTestId('sl');
await expect(sl).toBeAttached();
await expect(sl).not.toBeVisible();
@@ -111,7 +104,7 @@ test.describe('SplitterLayout', () => {
for (const vertical of [true, false]) {
test(`controlled width (${vertical ? 'vertical' : 'horizontal'})`, async ({ mount, page }) => {
- await mount();
+ await mount('SplitterLayout/SplitterControlledTestComp', { vertical });
await expect(page.getByTestId('resize-count')).toHaveText('0');
await expect(page.getByTestId('0')).toHaveText('200px');
diff --git a/playwright-ct.config.ts b/playwright-ct.config.ts
deleted file mode 100644
index 45b9ae31ee6..00000000000
--- a/playwright-ct.config.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { fileURLToPath } from 'node:url';
-import { defineConfig, devices } from '@playwright/experimental-ct-react';
-import react from '@vitejs/plugin-react';
-import tsconfigPaths from 'vite-tsconfig-paths';
-
-export default defineConfig({
- testDir: '.',
- testMatch: [
- '**/packages/main/src/components/**/test/*.spec.tsx',
- '**/packages/charts/src/**/test/*.spec.tsx',
- '**/playwright/test/**/*.spec.tsx',
- ],
- testIgnore: ['**/*.cy.tsx', '**/*.cy.ts', '**/*.stories.tsx', '**/*.mdx'],
- fullyParallel: true,
- forbidOnly: !!process.env.CI,
- retries: process.env.CI ? 1 : 0,
- workers: process.env.CI ? '100%' : undefined,
- // https://github.com/microsoft/playwright/issues/14511#issuecomment-1552589959
- reporter: process.env.CI
- ? [
- ['list'],
- ['github'],
- [
- 'monocart-reporter',
- {
- name: 'Playwright Coverage Report',
- outputFile: 'temp/playwright-coverage/report.html',
- coverage: {
- sourceFilter: (sourcePath: string) => {
- const included =
- sourcePath.includes('packages/main/src/components/SelectDialog') ||
- sourcePath.includes('packages/main/src/components/Splitter') ||
- (sourcePath.includes('packages/charts/src/') &&
- !sourcePath.includes('packages/charts/src/resources/') &&
- !sourcePath.includes('packages/charts/src/test-utils/') &&
- !sourcePath.includes('packages/charts/src/interfaces/') &&
- !sourcePath.includes('packages/charts/src/enums/'));
- return (
- included &&
- !sourcePath.includes('node_modules') &&
- !sourcePath.includes('/dist/') &&
- !sourcePath.includes('/test/') &&
- !sourcePath.endsWith('.stories.tsx') &&
- !sourcePath.endsWith('.module.css.ts') &&
- !/packages\/[^/]+\/src\/index\.ts$/.test(sourcePath)
- );
- },
- reports: ['lcovonly'],
- outputDir: 'temp/playwright-coverage',
- },
- },
- ],
- ]
- : 'html',
- timeout: 10_000,
- expect: { timeout: 4000 },
- use: {
- trace: 'on-first-retry',
- ctViteConfig: {
- plugins: [
- react(),
- tsconfigPaths({
- projects: [fileURLToPath(new URL('tsconfig.base.json', import.meta.url))],
- }),
- ],
- optimizeDeps: {
- esbuildOptions: {
- target: 'esnext',
- },
- exclude: ['**/*.cy.tsx', '**/*.cy.ts', '**/*.stories.tsx'],
- },
- build: {
- target: 'esnext',
- rollupOptions: {
- external: [/\.cy\.tsx$/, /\.cy\.ts$/, /\.stories\.tsx$/],
- },
- },
- },
- },
- projects: [
- { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
- { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
- { name: 'webkit', use: { ...devices['Desktop Safari'] } },
- ],
-});
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 00000000000..13653851d13
--- /dev/null
+++ b/playwright.config.ts
@@ -0,0 +1,38 @@
+import { defineConfig, devices } from '@playwright/test';
+
+const GALLERY_PORT = 9876;
+const GALLERY_URL = `http://localhost:${GALLERY_PORT}/`;
+
+export default defineConfig({
+ testDir: '.',
+ testMatch: [
+ 'packages/main/src/components/**/test/*.spec.tsx',
+ 'packages/charts/src/**/test/*.spec.tsx',
+ 'playwright/test/**/*.spec.tsx',
+ ],
+ testIgnore: ['**/*.cy.tsx', '**/*.cy.ts', '**/*.stories.tsx', '**/*.mdx'],
+ fullyParallel: true,
+ forbidOnly: !!process.env.CI,
+ retries: process.env.CI ? 1 : 0,
+ workers: process.env.CI ? '100%' : undefined,
+ reporter: process.env.CI ? [['list'], ['github']] : 'html',
+ timeout: 10_000,
+ expect: { timeout: 4000 },
+ webServer: {
+ command: `yarn vite --config playwright/gallery/vite.config.ts --port ${GALLERY_PORT} --strictPort`,
+ url: GALLERY_URL,
+ reuseExistingServer: !process.env.CI,
+ timeout: 120_000,
+ },
+ use: {
+ baseURL: GALLERY_URL,
+ serviceWorkers: 'block',
+ reuseContext: true,
+ trace: 'on-first-retry',
+ },
+ projects: [
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
+ { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
+ { name: 'webkit', use: { ...devices['Desktop Safari'] } },
+ ],
+});
diff --git a/playwright/fixtures/gallery-fixtures.ts b/playwright/fixtures/gallery-fixtures.ts
new file mode 100644
index 00000000000..6a7640d0d4a
--- /dev/null
+++ b/playwright/fixtures/gallery-fixtures.ts
@@ -0,0 +1,46 @@
+import { mkdir, writeFile } from 'node:fs/promises';
+import path from 'node:path';
+import { test as base, expect } from '@playwright/test';
+import { UI5WCInternalHelpers } from './ui5-fixtures-internal.js';
+
+// Gallery-model fixtures: `ui5wc` helpers + an auto `coverage` fixture that dumps Istanbul's `window.__coverage__` (present when served with `PW_COVERAGE=true`) for nyc.
+const COVERAGE = process.env.PW_COVERAGE === 'true';
+const NYC_OUTPUT_DIR = path.resolve(process.cwd(), 'temp/.nyc_output_playwright');
+
+type IstanbulCoverage = Record;
+
+export interface UI5WCGalleryFixtures {
+ coverage: void;
+ ui5wc: UI5WCInternalHelpers;
+}
+
+export const test = base.extend({
+ coverage: [
+ async ({ page }, use, testInfo) => {
+ await use();
+
+ if (!COVERAGE) {
+ return;
+ }
+
+ const data = await page
+ .evaluate(
+ () => (globalThis as unknown as { __coverage__?: IstanbulCoverage }).__coverage__,
+ )
+ .catch(() => undefined);
+
+ if (data && Object.keys(data).length > 0) {
+ await mkdir(NYC_OUTPUT_DIR, { recursive: true });
+ const safeId = testInfo.testId.replace(/[^a-z0-9_-]/gi, '_');
+ await writeFile(path.join(NYC_OUTPUT_DIR, `${safeId}.json`), JSON.stringify(data), 'utf8');
+ }
+ },
+ { scope: 'test', auto: true },
+ ],
+ ui5wc: async ({ page }, use) => {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ await use(new UI5WCInternalHelpers(page));
+ },
+});
+
+export { expect };
diff --git a/playwright/fixtures/main-fixtures.ts b/playwright/fixtures/main-fixtures.ts
deleted file mode 100644
index 10ac56e2f9e..00000000000
--- a/playwright/fixtures/main-fixtures.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import type { TestInfo } from '@playwright/test';
-import { addCoverageReport } from 'monocart-reporter';
-import { test as base, expect, UI5WCInternalHelpers } from './ui5-fixtures-internal';
-
-export interface UI5WCComponentTestFixtures {
- autoTestFixture: string;
- ui5wc: UI5WCInternalHelpers;
-}
-
-export const test = base.extend({
- autoTestFixture: [
- async ({ page }, use: (arg: string) => Promise, testInfo: TestInfo) => {
- const isChromium = testInfo.project.name === 'chromium';
-
- if (isChromium) {
- await page.coverage.startJSCoverage({ resetOnNavigation: false });
- }
-
- await use('autoTestFixture');
-
- if (isChromium) {
- const jsCoverage = await page.coverage.stopJSCoverage();
- await addCoverageReport(jsCoverage, testInfo);
- }
- },
- { scope: 'test', auto: true },
- ],
- ui5wc: async ({ page }, use) => {
- // eslint-disable-next-line react-hooks/rules-of-hooks
- await use(new UI5WCInternalHelpers(page));
- },
-});
-
-export { expect };
diff --git a/playwright/fixtures/ui5-fixtures.ts b/playwright/fixtures/ui5-fixtures.ts
index 11b462501c9..fbe21fa734d 100644
--- a/playwright/fixtures/ui5-fixtures.ts
+++ b/playwright/fixtures/ui5-fixtures.ts
@@ -1,4 +1,4 @@
-import { test as base, expect } from '@playwright/experimental-ct-react';
+import { test as base, expect } from '@playwright/test';
import type { Page, Locator } from '@playwright/test';
export interface UI5WCFixtures {
diff --git a/playwright/index.html b/playwright/gallery/index.html
similarity index 76%
rename from playwright/index.html
rename to playwright/gallery/index.html
index d11d6018986..583938633ac 100644
--- a/playwright/index.html
+++ b/playwright/gallery/index.html
@@ -3,7 +3,7 @@
- Playwright Component Tests
+ Playwright Component Gallery
-
+