This is an automated email from the ASF dual-hosted git repository. rusackas pushed a commit to branch misc-charts-examples in repository https://gitbox.apache.org/repos/asf/superset.git
commit ecb4aa93f6d139b5c47669474d4881e9e8d516a5 Author: Claude Code <[email protected]> AuthorDate: Wed Jul 22 22:53:57 2026 -0700 feat(playwright): add a viz-picker thumbnail capture tool A CAPTURE_THUMBNAILS-gated Playwright spec that renders each migrated legacy chart's example standalone at 512x512 and overwrites the plugin's gallery thumbnail, plus an npm run playwright:thumbnails entry point. Covers the Misc Charts examples and the existing chord, horizon, parallel coordinates, country map and world map examples; the Line percent-change capture lands as a new gallery image to be registered in the plugin metadata after capture. Co-Authored-By: Claude Fable 5 <[email protected]> --- superset-frontend/package.json | 1 + .../tests/tools/capture-viz-thumbnails.spec.ts | 169 +++++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/superset-frontend/package.json b/superset-frontend/package.json index faff57480cb..f861246e8fd 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -78,6 +78,7 @@ "playwright:debug": "playwright test --debug", "playwright:report": "playwright show-report", "docs:screenshots": "playwright test --config=playwright/generators/playwright.config.ts docs/", + "playwright:thumbnails": "CAPTURE_THUMBNAILS=1 playwright test tests/tools/capture-viz-thumbnails.spec.ts --project chromium", "prettier": "npm run _prettier -- --write", "prettier-check": "npm run _prettier -- --check", "prod": "npm run build", diff --git a/superset-frontend/playwright/tests/tools/capture-viz-thumbnails.spec.ts b/superset-frontend/playwright/tests/tools/capture-viz-thumbnails.spec.ts new file mode 100644 index 00000000000..92308e472f9 --- /dev/null +++ b/superset-frontend/playwright/tests/tools/capture-viz-thumbnails.spec.ts @@ -0,0 +1,169 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/** + * Captures viz-picker gallery thumbnails from live example charts. + * + * This is a maintenance tool, not a test: it renders each example chart + * standalone and overwrites the target plugin image with a fresh 512x512 + * screenshot. It only runs when CAPTURE_THUMBNAILS=1 is set, so the + * regular Playwright suites never execute it. + * + * Usage (requires a running Superset with examples loaded): + * CAPTURE_THUMBNAILS=1 npx playwright test tests/tools/capture-viz-thumbnails.spec.ts --project chromium + * + * Notes: + * - Only light-theme images are captured; `thumbnail-dark.png` variants + * are left untouched. + * - New gallery images (e.g. the Line percent-change example) still need + * to be registered in the plugin's metadata before they render in the + * gallery; the capture logs a reminder for any non-thumbnail output. + */ +import * as fs from 'fs'; +import * as path from 'path'; +import { test, expect } from '@playwright/test'; + +const THUMBNAIL_SIZE = 512; +const RENDERED_CHART_SELECTOR = + '[data-test="chart-container"]:has(svg, canvas, table):not(:has([data-test="loading-indicator"]))'; + +/** superset-frontend root, resolved from this spec's location. */ +const FRONTEND_ROOT = path.resolve(__dirname, '..', '..', '..'); + +interface CaptureTarget { + /** slice_name of the example chart to render */ + sliceName: string; + /** output path relative to superset-frontend */ + output: string; +} + +const TARGETS: CaptureTarget[] = [ + // Charts from the Misc Charts example dashboard + { + sliceName: 'Total Sales Bullet', + output: 'plugins/plugin-chart-echarts/src/Bullet/images/thumbnail.png', + }, + { + sliceName: 'Sales Period Pivot', + output: 'plugins/plugin-chart-echarts/src/TimePivot/images/thumbnail.png', + }, + { + sliceName: 'Population Nightingale Rose', + output: 'plugins/plugin-chart-rose/src/images/thumbnail.png', + }, + { + sliceName: 'Sales Calendar Heatmap', + output: 'plugins/plugin-chart-calendar/src/images/thumbnail.png', + }, + { + sliceName: 'Population Partition', + output: 'plugins/plugin-chart-partition/src/images/thumbnail.png', + }, + { + sliceName: 'Population Paired t-Test', + output: 'plugins/plugin-chart-paired-t-test/src/images/thumbnail.png', + }, + { + sliceName: 'Region Population Time Table', + output: 'src/visualizations/TimeTable/images/thumbnail.png', + }, + { + // Gallery example for the Line chart's percent-change mode; register + // it in the Line plugin metadata after capturing. + sliceName: 'Population Percent Change', + output: + 'plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/images/Line3.png', + }, + // Migrated charts whose examples live on other dashboards + { + sliceName: 'Parallel Coordinates', + output: + 'plugins/plugin-chart-parallel-coordinates/src/images/thumbnail.png', + }, + { + sliceName: 'Birth in France by department in 2016', + output: 'plugins/plugin-chart-country-map/src/images/thumbnail.png', + }, + { + sliceName: 'Cross Channel Relationship', + output: 'plugins/plugin-chart-chord/src/images/thumbnail.png', + }, + { + sliceName: 'Seasonality of Revenue (per Product Line)', + output: 'plugins/plugin-chart-horizon/src/images/thumbnail.png', + }, + { + sliceName: '% Rural', + output: 'plugins/plugin-chart-world-map/src/images/thumbnail.png', + }, +]; + +test.describe('capture viz thumbnails', () => { + test.skip( + !process.env.CAPTURE_THUMBNAILS, + 'Thumbnail capture only runs with CAPTURE_THUMBNAILS=1', + ); + + for (const target of TARGETS) { + test(`captures ${target.sliceName}`, async ({ page }) => { + test.setTimeout(90_000); + + // Look up the slice id by name via the chart API + const query = encodeURIComponent( + `(filters:!((col:slice_name,opr:eq,value:'${target.sliceName.replace( + /'/g, + "''", + )}')))`, + ); + const response = await page.request.get(`/api/v1/chart/?q=${query}`); + expect(response.ok()).toBeTruthy(); + const { result } = await response.json(); + expect( + result.length, + `example chart "${target.sliceName}" not found — are examples loaded?`, + ).toBeGreaterThan(0); + const sliceId = result[0].id; + + // Render the chart alone at thumbnail size and wait for real content + await page.setViewportSize({ + width: THUMBNAIL_SIZE, + height: THUMBNAIL_SIZE, + }); + await page.goto(`/explore/?slice_id=${sliceId}&standalone=1`); + await page + .locator(RENDERED_CHART_SELECTOR) + .first() + .waitFor({ state: 'visible', timeout: 60_000 }); + // Give animations/tiles a moment to settle before the still + await page.waitForTimeout(2_000); + + const outputPath = path.join(FRONTEND_ROOT, target.output); + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + await page.screenshot({ path: outputPath }); + + // eslint-disable-next-line no-console + console.log(`captured ${target.sliceName} -> ${target.output}`); + if (!target.output.endsWith('thumbnail.png')) { + // eslint-disable-next-line no-console + console.log( + `NOTE: ${target.output} is a new gallery image — register it in the plugin metadata (exampleGallery) to surface it.`, + ); + } + }); + } +});
