Skip to content

feat: add global async query playwright tests - #43004

Open
drivaspreset wants to merge 4 commits into
apache:masterfrom
preset-io:test-global-async-query-playwright
Open

feat: add global async query playwright tests#43004
drivaspreset wants to merge 4 commits into
apache:masterfrom
preset-io:test-global-async-query-playwright

Conversation

@drivaspreset

Copy link
Copy Markdown

SUMMARY

Adds Playwright E2E coverage for Global Async Queries (GAQ) on dashboards and SQL Lab.

Eight dashboard-level tests cover the GAQ request/response lifecycle end to end:

  • Forced dashboard refresh going through the full 202 → poll → done cycle
  • Reloading an already-cached dashboard being served synchronously (never touching the async channel)
  • A broken chart surfacing a clean error under GAQ instead of hanging, and recovering once fixed
  • Rapidly switching a filter value without a superseded (stale) response clobbering the screen
  • A dashboard with many charts resolving every chart independently and correctly
  • Losing the async-token cookie bouncing chart refresh to /login even though the underlying session stays valid
  • Navigating away mid-load and back producing no console errors and re-rendering cleanly
  • A native filter's value dropdown populating through the same async pipeline as chart data

A ninth test (global-async-query-sqllab.spec.ts) runs under the separate chromium-sqllab Playwright project and asserts that a simple SELECT in SQL Lab still runs synchronously with GLOBAL_ASYNC_QUERIES enabled, i.e. that the flag doesn't accidentally route SQL Lab traffic through the GAQ polling endpoint.

dashboard-test-helpers.ts gains an optional chartWidth on createDashboardWithCharts, so tests can lay out more charts per row than the default single-row 12-column grid would otherwise allow (needed for the many-charts test).

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A - test-only change, no UI behavior modified.

TESTING INSTRUCTIONS

Requires GLOBAL_ASYNC_QUERIES enabled, plus Redis and a running Celery worker (all tests except the cache-hit-reload case, which is served synchronously and only needs the feature flag).

cd superset-frontend

# dashboard GAQ suite
npx playwright test tests/dashboard/global-async-query.spec.ts

# SQL Lab smoke test (separate project)
npx playwright test tests/sqllab/global-async-query-sqllab.spec.ts --project=chromium-sqllab

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags: GLOBAL_ASYNC_QUERIES
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@dosubot dosubot Bot added the global:async-query Related to Async Queries feature label Aug 10, 2026
@bito-code-review

bito-code-review Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Bito Review Skipped - Source Branch Not Found

Bito didn’t review this change because the pull request is no longer valid. It may have been merged, or the source/target branch may no longer exist.

@netlify

netlify Bot commented Aug 10, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit c77f542
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a79fc9dcf0a1800089c4960
😎 Deploy Preview https://deploy-preview-43004--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Comment on lines +583 to +586
await expect(value).toHaveText(/\d/, {
timeout: RACE_DELAY_MS - 500,
});
const raceResultText = await value.textContent();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The race assertion does not establish that the fast girl request completed. If that request fails or never starts, the pre-existing unfiltered chart value can still satisfy toHaveText(/\d/), remain unchanged during the wait, and differ from the later boy result, causing the test to pass without exercising the intended superseded-response scenario. Capture and assert the expected girl count or correlate the second request and its completion before checking for stale overwrites. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ Race regression test may pass without testing `girl` results.
- ⚠️ Failures in the second filter request can be masked.
- ⚠️ GAQ stale-response coverage becomes unreliable.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/playwright/tests/dashboard/global-async-query.spec.ts
**Line:** 583:586
**Comment:**
	*Incorrect Condition Logic: The race assertion does not establish that the fast `girl` request completed. If that request fails or never starts, the pre-existing unfiltered chart value can still satisfy `toHaveText(/\d/)`, remain unchanged during the wait, and differ from the later `boy` result, causing the test to pass without exercising the intended superseded-response scenario. Capture and assert the expected `girl` count or correlate the second request and its completion before checking for stale overwrites.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +745 to +749
expect(
new Set(displayedValues).size,
'each chart filters on a different name, so their counts should not all collapse to the same number (a sign of misrouted/cross-contaminated results)',
).toBeGreaterThan(1);
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Checking only that the set of displayed values has more than one entry does not verify that each chart displays the result for its own name. Several charts can show the wrong chart's result, or multiple charts can be blank/duplicated while at least two unrelated values remain, and this assertion will still pass. Compare every locator with the expected count for the corresponding NAMES entry, or correlate each chart's final response to its slice. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ GAQ multi-chart test can miss swapped chart results.
- ⚠️ Incorrect dashboard values may appear individually plausible.
- ⚠️ Concurrent response-routing coverage remains incomplete.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/playwright/tests/dashboard/global-async-query.spec.ts
**Line:** 745:749
**Comment:**
	*Incorrect Condition Logic: Checking only that the set of displayed values has more than one entry does not verify that each chart displays the result for its own name. Several charts can show the wrong chart's result, or multiple charts can be blank/duplicated while at least two unrelated values remain, and this assertion will still pass. Compare every locator with the expected count for the corresponding `NAMES` entry, or correlate each chart's final response to its slice.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +956 to +957
await dashboard.forceRefresh();
await page.goto('/superset/welcome/');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: forceRefresh() only waits for the menu click to finish; it does not wait for a chart-data request or loading state. The following navigation can therefore tear down the page before the refresh request is submitted, so the test may pass without testing navigation during an in-flight GAQ request. Arm a request/loading-state promise before triggering the refresh and await that signal before navigating away. [race condition]

Severity Level: Major ⚠️
- ⚠️ Navigation test may skip the in-flight refresh scenario.
- ⚠️ GAQ cancellation and teardown behavior may remain untested.
- ⚠️ Passing results can provide false lifecycle confidence.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/playwright/tests/dashboard/global-async-query.spec.ts
**Line:** 956:957
**Comment:**
	*Race Condition: `forceRefresh()` only waits for the menu click to finish; it does not wait for a chart-data request or loading state. The following navigation can therefore tear down the page before the refresh request is submitted, so the test may pass without testing navigation during an in-flight GAQ request. Arm a request/loading-state promise before triggering the refresh and await that signal before navigating away.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +1121 to +1137
let filterValueSubmitStatus: number | undefined;
let sawAsyncEventPoll = false;
page.on('response', response => {
const request = response.request();
const url = response.url();

if (
request.method() === 'POST' &&
url.includes('/api/v1/chart/data') &&
sliceIdFromChartDataUrl(url) === undefined
) {
filterValueSubmitStatus = response.status();
return;
}
if (request.method() === 'GET' && url.includes('/api/v1/async_event/')) {
sawAsyncEventPoll = true;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The response listener is installed before dashboard navigation, while FilterValue fetches native-filter options during dashboard initialization. That initialization request has no slice_id and can set filterValueSubmitStatus and sawAsyncEventPoll before the dropdown is opened, allowing the final assertions to pass even if the click does not use GAQ. Reset or attach the listener only after the initial dashboard/filter loading has completed, then correlate the request to the dropdown action. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ Dropdown test may pass without exercising its click path.
- ⚠️ Native filter GAQ coverage can be falsely attributed.
- ⚠️ Regressions in dropdown-triggered fetching may go undetected.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/playwright/tests/dashboard/global-async-query.spec.ts
**Line:** 1121:1137
**Comment:**
	*Incorrect Condition Logic: The response listener is installed before dashboard navigation, while `FilterValue` fetches native-filter options during dashboard initialization. That initialization request has no `slice_id` and can set `filterValueSubmitStatus` and `sawAsyncEventPoll` before the dropdown is opened, allowing the final assertions to pass even if the click does not use GAQ. Reset or attach the listener only after the initial dashboard/filter loading has completed, then correlate the request to the dropdown action.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.41%. Comparing base (b815640) to head (8205063).

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43004      +/-   ##
==========================================
+ Coverage   65.60%   66.41%   +0.80%     
==========================================
  Files        2859     2859              
  Lines      161460   161460              
  Branches    37191    37191              
==========================================
+ Hits       105929   107232    +1303     
+ Misses      53499    52190    -1309     
- Partials     2032     2038       +6     
Flag Coverage Δ
javascript 73.27% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@drivaspreset drivaspreset changed the title add global async query playwright tests feat: add global async query playwright tests Aug 10, 2026
Comment on lines +83 to +108
const { dashboardId, charts } = await createDashboardWithCharts(
page,
testAssets,
testWithAssets.info(),
{
datasetName: 'birth_names',
chartNamePrefix: 'gaq_tc1_cold_cache',
dashboardTitlePrefix: 'gaq_tc1_cold_cache',
chartSpecs: [
{
viz_type: 'big_number_total',
params: { metric: 'count' },
},
],
},
);
const [chart] = charts;

const dashboard = new DashboardPage(page);
await dashboard.gotoById(dashboardId);
await dashboard.waitForLoad();

const value = dashboard
.getChart(chart.id)
.locator('.superset-legacy-chart-big-number .header-line');
await expect(value).toBeVisible({ timeout: TIMEOUT.CHART_RENDER });

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing this in multiple places. Can this be placed as a function or in a before method so it can be reused?

Comment on lines +116 to +138
page.on('response', response => {
const request = response.request();
const url = response.url();

if (
request.method() === 'POST' &&
url.includes('/api/v1/chart/data') &&
sliceIdFromChartDataUrl(url) === chart.id
) {
chartDataSubmitStatus = response.status();
return;
}
if (request.method() === 'GET' && url.includes('/api/v1/async_event/')) {
sawAsyncEventPoll = true;
return;
}
if (
request.method() === 'GET' &&
/\/api\/v1\/chart\/data\/qc-/.test(url)
) {
sawFinalCachedFetch = true;
}
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block of code seems to be repeated elsewhere, can we create a helper function within the test to do this?

Comment on lines +434 to +437
const filterCombobox = page
.locator('[data-test="form-item-value"]')
.first()
.locator('[role="combobox"]');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not have a combo box filter component we can reuse?

Comment on lines +449 to +451
const applyBtn = page.locator(
'[data-test="filter-bar__apply-button"], [data-test="filterbar-action-buttons"] button[type="submit"]',
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably can use the button component here

const dashboardId: number = dashBody.result?.id ?? dashBody.id;
testAssets.trackDashboard(dashboardId);

const linkResp = await apiPut(page, `api/v1/chart/${chartId}`, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use apiPutChart?

const dashboardId: number = dashBody.result?.id ?? dashBody.id;
testAssets.trackDashboard(dashboardId);

const linkResp = await apiPut(page, `api/v1/chart/${chartId}`, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use apiPutChart?

metric: 'count',
adhoc_filters: [],
};
const chartResp = await apiPost(page, 'api/v1/chart/', {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use apiPostChart?

metric: 'count',
adhoc_filters: [],
};
const chartResp = await apiPost(page, 'api/v1/chart/', {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use apiPostChart?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

global:async-query Related to Async Queries feature size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants