diff --git a/superset-frontend/.gitignore b/superset-frontend/.gitignore index a7027112bccb..358cfa80b1e9 100644 --- a/superset-frontend/.gitignore +++ b/superset-frontend/.gitignore @@ -5,3 +5,4 @@ src/temp .temp_cache/ .tsbuildinfo .swc/ +playwright/.auth diff --git a/superset-frontend/playwright.config.ts b/superset-frontend/playwright.config.ts index 585ccbb96f72..af5a004e6890 100644 --- a/superset-frontend/playwright.config.ts +++ b/superset-frontend/playwright.config.ts @@ -21,6 +21,10 @@ // eslint-disable-next-line import/no-extraneous-dependencies import { defineConfig } from '@playwright/test'; +import path from 'path'; + +const authFile = path.join(__dirname, 'playwright/.auth/admin.json'); + export default defineConfig({ // Test directory @@ -60,7 +64,7 @@ export default defineConfig({ // Global test setup use: { // Use environment variable for base URL in CI, default to localhost:8088 for local - baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8088', + baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8088/', // Browser settings headless: !!process.env.CI, @@ -76,12 +80,35 @@ export default defineConfig({ }, projects: [ + { + name: 'setup-auth', use: { + headless: true, + }, + testMatch: /.*\.setup\.ts/ + }, { name: 'chromium', use: { browserName: 'chromium', testIdAttribute: 'data-test', }, + // don't pre-authenticate for general tests + testIgnore: 'docs/*.spec.ts' + }, + { + name: 'chromium-authenticated', + use: { + browserName: 'chromium', + testIdAttribute: 'data-test', + storageState: authFile, + headless: true, + actionTimeout: 60000, + navigationTimeout: 60000, + viewport: { width: 1920, height: 1080 }, + }, + // only run the playwright files for docs with pre-authentication + dependencies: ['setup-auth'], + testMatch: 'docs/*.spec.ts' }, ], diff --git a/superset-frontend/playwright/tests/auth.setup.ts b/superset-frontend/playwright/tests/auth.setup.ts new file mode 100644 index 000000000000..66c9c6687b10 --- /dev/null +++ b/superset-frontend/playwright/tests/auth.setup.ts @@ -0,0 +1,38 @@ +/** + * 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. + */ + +import { test as setup } from '@playwright/test'; +import path from 'path'; + +const authFile = path.join(__dirname, '../.auth/admin.json'); + +setup('authenticate', async ({ page }) => { + await page.goto('/login/'); + await page.waitForLoadState('domcontentloaded'); + await page.locator('#username').fill('admin'); + await page.locator('#password').fill('admin'); + await page.locator('[type="submit"]').click(); + + // wait until redirected to any /superset/ URL + await page.waitForURL("http://localhost:8088/superset/welcome/", { timeout: 60000}); + + + await page.context().storageState({ path: authFile }); + console.log(`Authentication state saved to ${authFile}`); +}); diff --git a/superset-frontend/playwright/tests/docs/docs-screenshots.spec.ts b/superset-frontend/playwright/tests/docs/docs-screenshots.spec.ts new file mode 100644 index 000000000000..284f8a02a6d5 --- /dev/null +++ b/superset-frontend/playwright/tests/docs/docs-screenshots.spec.ts @@ -0,0 +1,94 @@ +/** + * 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. + */ + +// @ts-check +import { test, expect } from '@playwright/test'; + +const docsPath: string = '../docs/static/img/screenshots/'; + +test('chart type screenshot', async ({ page }) => { + await page.goto('/chart/add'); + await page.waitForLoadState('domcontentloaded'); + await expect(page.getByText('Choose chart type')).toBeVisible(); + await page.getByRole('tab', { name: 'All charts' }).click(); + await page.addStyleTag({ content: 'body { zoom: 0.8 }' }); + await page.waitForTimeout(1000); // wait for charts to load + await page + .locator('.viz-gallery') + .screenshot({ path: docsPath + 'gallery.jpg', type: 'jpeg' }); +}); + +test('dashboard content screenshot', async ({ page }) => { + await page.goto('/dashboard/list/'); + await page.waitForLoadState('domcontentloaded'); + await page.getByRole('link', { name: 'Slack Dashboard' }).click(); + await page.addStyleTag({ content: 'body { zoom: 0.8 }' }); + await page.waitForTimeout(5000); // wait for chart to load + await page + .locator('[data-test="dashboard-content-wrapper"]') + .screenshot({ path: docsPath + 'slack_dash.jpg', type: 'jpeg' }); +}); + +test('chart editor screenshot', async ({ page }) => { + await page.goto('/chart/list/'); + await page.waitForLoadState('domcontentloaded'); + await page.locator('[data-test="filters-search"]').fill('life'); + await page.locator('[data-test="filters-search"]').press('Enter'); + await page + .locator('[data-test="Life Expectancy VS Rural %-list-chart-title"]') + .click(); + await page.waitForTimeout(10000); // wait for charts to load + await page.screenshot({ + path: docsPath + 'explore.jpg', + type: 'jpeg', + fullPage: true, + }); +}); + +test('sqllab screenshot', async ({ page }) => { + await page.goto('/sqllab'); + await page.waitForLoadState('domcontentloaded'); + await page + .getByRole('combobox', { name: 'Select schema or type to' }) + .fill('main'); + await page + .getByRole('combobox', { name: 'Select schema or type to' }) + .press('Enter'); + await page.waitForTimeout(1000); + await page + .getByRole('combobox', { name: 'Select table or type to' }) + .fill('covid'); + await page + .getByRole('combobox', { name: 'Select table or type to' }) + .press('Enter'); + await page.locator('.ace_content').click(); + await page + .getByRole('textbox', { name: 'Cursor at row' }) + .fill( + 'SELECT "developer_researcher",\n"stage_of_development",\n"product_category",\n"country_name",\nFROM main.covid_vaccines', + ); + await page.locator('[data-test="run-query-action"]').click(); + await page.locator('.ace_content').click(); + await page.waitForTimeout(3000); // wait for charts to load + await page.screenshot({ + path: docsPath + 'sql_lab.jpg', + type: 'jpeg', + fullPage: true, + }); +});