test(dashboard): fix flaky tests in deployed environment (#9511)

### **User description**
…riables


___

### **PR Type**
Tests, Enhancement


___

### **Description**
- Refine chart-render wait logic with class checks

- Integrate values-stream helper in variable dropdown

- Standardize relative time ranges to 30m

- Improve streaming test with API wait loops


___



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Formatting</strong></td><td><details><summary>2
files</summary><table>
<tr>
<td><strong>apiCleanup.js</strong><dd><code>Remove extraneous newline at
EOF</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-537fe76c0d8e26e81c5f77c83bd0ee35daaa973822105c809bf83171a26b33c5">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>cleanup.spec.js</strong><dd><code>Remove trailing blank
line</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-c214275a512fa285a7e4f2ab61dfcb42d71a0436b0e4241f94d7b45693587983">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>4
files</summary><table>
<tr>
<td><strong>dashboard-panel-actions.js</strong><dd><code>Refine
waitForChartToRender class checks</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-d29713fdcc11cbc93bda29da5261da389226cd718e4292a1c2cd96629f573a80">+27/-10</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>dashboard-variables.js</strong><dd><code>Add stream helper
and input typing delay</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-67d38d39bb67f4a0cbfa473ee81ad50b1fffebc40c635417e58905f3c865a6e2">+18/-3</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>dashboard-streaming.spec.js</strong><dd><code>Add API wait
loops in streaming test</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-92f44296937f53cb448371d1c61510c8c910fbecab83b02b8e89490d5832be19">+36/-4</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>streaming-helpers.js</strong><dd><code>Implement
waitForValuesStreamComplete utility</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-ba793998d7c077815ee18543001a519b7d45a9236990b60d256124a4b30d48cb">+65/-1</a>&nbsp;
&nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>6
files</summary><table>
<tr>
<td><strong>dashboard-geoMap.spec.js</strong><dd><code>Update time range
to 30m</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-6ae8394cd6b915e6d55a179cf6c7dc32992b8ebbd5d9aac177c0cbd1b9f351b3">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>dashboard-html-chart.spec.js</strong><dd><code>Set html
chart time to 30m</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-02c503aa87193168f5aa83c8105c376ea0c4dc45e6c7bd6f255b693bf7ebc462">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>dashboard-multi-y-axis.spec.js</strong><dd><code>Update
multi-y-axis time to 30m</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-e90516a105c0b17c8e04c18e2b67ac10af213dbdd208fa4414cdcb7e377e6ee5">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>dashboard-transpose.spec.js</strong><dd><code>Change
transpose time range to 30m</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-da631007cea76a4bc59e4acd1195b59ae4131cd5208d21a9c1f94d3913911dc4">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>dashboard.spec.js</strong><dd><code>Adjust gauge and table
time ranges to 15m</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-d0fa0042783dc109334b05266edce228bad97adcb568dd1e447920596238e9b4">+5/-5</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>dashboard2.spec.js</strong><dd><code>Standardize dashboard2
time to 30m</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-8c9189e9e3950fff987c45ee8116d89f15f8fdec5baad209bef6f5858f3b3045">+7/-7</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Configuration
changes</strong></td><td><details><summary>1 files</summary><table>
<tr>
<td><strong>playwright.yml</strong><dd><code>Fix typo in run_files
list</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/9511/files#diff-7afcd2d8f7b49bda74843f209eefb7b2da45f7e7803bf2e4bd636699b76aa2d3">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

</details>

___

---------

Co-authored-by: ktx-abhay <abhay.padamani@kiara.tech>
This commit is contained in:
ktx-akshay
2025-12-17 15:23:43 +05:30
committed by GitHub
co-authored by ktx-abhay
parent 51bea7ea3d
commit e96db6332b
14 changed files with 166 additions and 36 deletions
+1
View File
@@ -168,6 +168,7 @@ jobs:
"dashboard-folder.spec.js",
"dashboard-import.spec.js",
"maxquery.spec.js",
"dashboard-chartJson.spec.js",
]
- testfolder: "Dashboards-Settings"
browser: "chrome"
+1 -1
View File
@@ -2611,4 +2611,4 @@ class APICleanup {
}
}
module.exports = APICleanup;
module.exports = APICleanup;
@@ -37,18 +37,35 @@ export default class DashboardactionPage {
async waitForChartToRender() {
// Wait for button or parent to have light button class (render complete)
// Supports both repo structures: button.o2-primary-button-light OR parent.o2-secondary-button-light
// The parent div.q-btn-group has classes o2-primary-button and o2-secondary-button-light
await this.page.waitForFunction(() => {
const btn = document.querySelector('[data-test="dashboard-apply"]');
return (
btn &&
(btn.classList.contains("o2-primary-button-light") ||
btn.classList.contains("o2-secondary-button-light") ||
(btn.parentElement &&
(btn.parentElement.classList.contains("o2-primary-button-light") ||
btn.parentElement.classList.contains("o2-secondary-button-light"))))
);
});
if (!btn) return false;
// Check button itself
if (btn.classList.contains("o2-primary-button-light") ||
btn.classList.contains("o2-secondary-button-light")) {
return true;
}
// Check immediate parent (button group container)
const parent = btn.parentElement;
if (parent &&
(parent.classList.contains("o2-primary-button-light") ||
parent.classList.contains("o2-secondary-button-light"))) {
return true;
}
// Check if parent has both o2-primary-button AND o2-secondary-button-light
// (which indicates the button group is in the rendered state)
if (parent &&
parent.classList.contains("o2-primary-button") &&
parent.classList.contains("o2-secondary-button-light")) {
return true;
}
return false;
}, { timeout: 30000 });
}
//Dashboard panel actions(Edit, Layout, Duplicate, Inspector, Move, Delete)
@@ -2,6 +2,7 @@
//methods: addDashboardVariable, selectValueFromVariableDropDown
//addDashboardVariable params: name, streamtype, streamName, field, customValueSearch, filterConfig, showMultipleValues
import { expect } from "@playwright/test";
import { waitForValuesStreamComplete } from "../../playwright-tests/utils/streaming-helpers.js";
export default class DashboardVariables {
constructor(page) {
@@ -56,13 +57,11 @@ export default class DashboardVariables {
await this.page
.getByRole("option", { name: streamName, exact: true })
.click();
// Select Field
const fieldSelect = await this.page.locator('[data-test="dashboard-variable-field-select"]');
await fieldSelect.click();
await this.page.keyboard.type(field);
await this.page.keyboard.type(field, {delay:100});
// Wait for dropdown to have options available
await this.page.waitForFunction(
() => {
@@ -225,9 +224,25 @@ export default class DashboardVariables {
async selectValueFromVariableDropDown(label, value) {
const input = this.page.getByLabel(label, { exact: true });
await input.waitFor({ state: "visible", timeout: 10000 });
// Wait for _values_stream API call when clicking on the dropdown
// Start listening before the action to ensure we capture the stream
const valuesStreamPromise = waitForValuesStreamComplete(this.page);
await input.click();
// Wait for the values stream to complete (waits for 'data: [[DONE]]' marker)
await valuesStreamPromise;
// Wait for _values_stream API call when filling/searching
// Start listening before filling to capture the search stream
// const searchStreamPromise = waitForValuesStreamComplete(this.page);
await input.fill(value);
// // Wait for search stream to complete (waits for 'data: [[DONE]]' marker)
// await searchStreamPromise;
const option = this.page.getByRole("option", { name: value });
await option.waitFor({ state: "visible", timeout: 10000 });
await option.click();
@@ -196,3 +196,4 @@ test.describe("Pre-Test Cleanup", () => {
testLogger.info('Pre-test cleanup completed successfully');
});
});
@@ -76,7 +76,7 @@ test.describe("dashboard maps testcases", () => {
);
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
// await pm.dashboardPanelActions.applyDashboardBtn();
await pm.dashboardPanelActions.waitForChartToRender();
@@ -136,7 +136,7 @@ test.describe("HTML chart dashboard", () => {
await pm.chartTypeSelector.selectChartType("html");
await pm.dashboardTimeRefresh.setRelative("15", "m");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await page
.locator('[data-test="dashboard-html-editor"]')
@@ -59,7 +59,7 @@ test.describe("dashboard multi y axis testcases", () => {
await pm.chartTypeSelector.searchAndAddField("kubernetes_labels_name", "b");
// Set the relative time range
await pm.dateTimeHelper.setRelativeTimeRange("6-w");
await pm.dateTimeHelper.setRelativeTimeRange("30-m");
// Apply the configuration and wait for the chart to render
await pm.dashboardPanelActions.applyDashboardBtn();
@@ -122,7 +122,7 @@ test.describe("dashboard multi y axis testcases", () => {
await pm.chartTypeSelector.searchAndAddField("kubernetes_labels_name", "b");
// Set the relative time range
await pm.dateTimeHelper.setRelativeTimeRange("6-w");
await pm.dateTimeHelper.setRelativeTimeRange("30-m");
// Apply the configuration and wait for the chart to render
await pm.dashboardPanelActions.applyDashboardBtn();
@@ -87,21 +87,53 @@ test.describe("dashboard streaming testcases", () => {
);
await pm.dashboardPanelActions.applyDashboardBtn();
await pm.dashboardPanelActions.waitForChartToRender();
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.waitForChartToRender();
const variableInput = page.getByLabel("variablename", {
exact: true,
});
await variableInput.waitFor({ state: "visible", timeout: 10000 });
// Wait for the _values API call when clicking on the variable input
const valuesApiPromise = page.waitForResponse(
response => response.url().includes('_values') || response.url().includes('/values'),
{ timeout: 15000 }
);
await variableInput.click();
// Wait for the API response to complete
try {
await valuesApiPromise;
await page.waitForTimeout(500); // Additional wait for dropdown to render
} catch (e) {
console.log("Warning: _values API response not detected, continuing...");
}
// Type partial search terms to trigger multiple _values API calls with streaming enabled
const searchTerms = ["zi", "zio", "ziox"];
for (const term of searchTerms) {
// Wait for the _values API call for each search term
const valuesApiResponse = page.waitForResponse(
response => response.url().includes('_values') || response.url().includes('/values'),
{ timeout: 15000 }
);
await variableInput.fill(term);
// await page.waitForTimeout(1500);
// await page.waitForLoadState("networkidle");
// Wait for the API response to complete
try {
await valuesApiResponse;
await page.waitForTimeout(300); // Wait for dropdown to update with new results
} catch (e) {
console.log(`Warning: _values API response not detected for term "${term}", continuing...`);
}
}
// Select the final value
const option = page.getByRole("option", { name: "ziox" });
@@ -262,7 +294,7 @@ test.describe("dashboard streaming testcases", () => {
await pm.dashboardPanelActions.applyDashboardBtn();
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.waitForChartToRender();
@@ -352,7 +352,7 @@ test.describe("dashboard UI testcases", () => {
await pm.chartTypeSelector.searchAndAddField("xAxis", "y");
// Set relative time range
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.waitForChartToRender();
@@ -419,7 +419,7 @@ test.describe("dashboard UI testcases", () => {
);
// Set date-time and timezone for gauge chart
await pm.dateTimeHelper.setRelativeTimeRange("6-w");
await pm.dateTimeHelper.setRelativeTimeRange("15-m");
await pm.dashboardPanelActions.applyDashboardBtn();
// Verify the gauge chart is visible
@@ -429,7 +429,7 @@ test.describe("dashboard UI testcases", () => {
await pm.chartTypeSelector.selectChartType("table");
// Set timezone for the table chart
await pm.dateTimeHelper.setRelativeTimeRange("1-w");
await pm.dateTimeHelper.setRelativeTimeRange("15-m");
await pm.dashboardPanelActions.applyDashboardBtn();
await pm.dashboardPanelActions.waitForChartToRender();
@@ -471,7 +471,7 @@ test.describe("dashboard UI testcases", () => {
);
// Set date-time filter
await pm.dateTimeHelper.setRelativeTimeRange("5-w");
await pm.dateTimeHelper.setRelativeTimeRange("15-m");
await pm.dashboardPanelActions.applyDashboardBtn();
@@ -521,7 +521,7 @@ test.describe("dashboard UI testcases", () => {
// Set date-time filter and timezone
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.applyDashboardBtn();
// Attempt to save the panel without a name
@@ -579,7 +579,7 @@ test.describe("dashboard UI testcases", () => {
await pm.chartTypeSelector.searchAndAddField("kubernetes_host", "filter");
// Set time range and apply
await pm.dateTimeHelper.setRelativeTimeRange("5-w");
await pm.dateTimeHelper.setRelativeTimeRange("15-m");
await pm.dashboardPanelActions.applyDashboardBtn();
// Apply "Is Null" filter
@@ -101,7 +101,7 @@ test.describe("dashboard UI testcases", () => {
// Set relative time
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.applyDashboardBtn();
// Cancel adding the breakdown field
@@ -155,7 +155,7 @@ test.describe("dashboard UI testcases", () => {
// Set the date-time range and apply changes
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.applyDashboardBtn();
// Change chart types and verify
@@ -220,7 +220,7 @@ test.describe("dashboard UI testcases", () => {
// Set the date-time range and apply changes
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.applyDashboardBtn();
// Save the dashboard panel
@@ -261,7 +261,7 @@ test.describe("dashboard UI testcases", () => {
await pm.chartTypeSelector.searchAndAddField("kubernetes_host", "b");
// Set the date-time range
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.applyDashboardBtn();
// Refresh the page
@@ -307,7 +307,7 @@ test.describe("dashboard UI testcases", () => {
// Set relative date-time and timezone
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
// await pm.dashboardPanelActions.applyDashboardBtn();
@@ -393,7 +393,7 @@ test.describe("dashboard UI testcases", () => {
// Set relative date-time and timezone
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
await pm.dashboardPanelActions.applyDashboardBtn();
await pm.dashboardPanelActions.savePanel();
@@ -436,7 +436,7 @@ test.describe("dashboard UI testcases", () => {
// Set relative date-time
await waitForDateTimeButtonToBeEnabled(page);
await pm.dashboardTimeRefresh.setRelative("6", "w");
await pm.dashboardTimeRefresh.setRelative("30", "m");
// Configure no value replacement
await pm.dashboardPanelConfigs.openConfigPanel();
@@ -6,13 +6,18 @@ const testLogger = require('./test-logger.js');
*
* Usage:
* ```javascript
* import { waitForStreamComplete, waitForTableWithData } from '../utils/streaming-helpers.js';
* import { waitForStreamComplete, waitForValuesStreamComplete, waitForTableWithData } from '../utils/streaming-helpers.js';
*
* // Wait for a streaming API to complete
* const streamPromise = waitForStreamComplete(page);
* await someAction();
* await streamPromise;
*
* // Wait for values stream to complete
* const valuesStreamPromise = waitForValuesStreamComplete(page);
* await page.click('#field-dropdown');
* await valuesStreamPromise;
*
* // Wait for table to have data
* await waitForTableWithData(page);
* ```
@@ -81,6 +86,65 @@ export async function waitForStreamComplete(page, timeout = 15000) {
]);
}
/**
* Waits for a values stream API call to complete by listening for the 'data: [[DONE]]' marker.
* This is specifically designed for the _values_stream endpoint.
*
* The function monitors the _values_stream endpoint and resolves when it receives
* the completion marker 'data: [[DONE]]' in the response body.
*
* @param {import('@playwright/test').Page} page - The Playwright page object
* @param {number} timeout - Maximum time to wait in milliseconds (default: 15000)
* @returns {Promise<void>} Resolves when values stream completes or times out
*
* @example
* // Start listening before triggering the request
* const streamPromise = waitForValuesStreamComplete(page);
* await page.click('#field-dropdown');
* await streamPromise; // Wait for values streaming to finish
*/
export async function waitForValuesStreamComplete(page, timeout = 15000) {
return Promise.race([
new Promise((resolve) => {
const responseHandler = async (response) => {
const url = response.url();
// Check if this is a values stream endpoint
if (url.includes('_values_stream')) {
try {
// Read the response body as text
const body = await response.text();
// Check if stream has completed with 'data: [[DONE]]' marker
if (body.includes('data: [[DONE]]')) {
testLogger.info(`Values stream completed for: ${url.split('/').pop()}`);
page.off('response', responseHandler);
resolve();
}
} catch (error) {
// Response body might not be available or already consumed
testLogger.debug(`Could not read response body: ${error.message}`);
}
}
};
page.on('response', responseHandler);
// Clean up listener if it somehow doesn't resolve
setTimeout(() => {
page.off('response', responseHandler);
}, timeout + 1000);
}),
new Promise((resolve) => {
setTimeout(() => {
testLogger.warn('Values stream wait timeout - continuing anyway');
resolve();
}, timeout);
})
]);
}
/**
* Waits for the table to have data rows before proceeding.
* This ensures the table has been populated with actual data from the stream.