Skip to content

Commit fecfe25

Browse files
pedro93claude
andauthored
fix(test): fix flaky ingestionV3/run_history_v2.js Cypress test (#16913)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d59af61 commit fecfe25

2 files changed

Lines changed: 118 additions & 30 deletions

File tree

smoke-test/tests/cypress/cypress/e2e/ingestionV3/run_history_v2.js

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,75 +11,98 @@ import {
1111
goToIngestionPage,
1212
} from "./utils";
1313

14+
const testId = Math.floor(Math.random() * 100000);
15+
1416
describe("run history tab in manage data sources", () => {
17+
const createdSources = [];
18+
1519
beforeEach(() => {
1620
setIngestionRedesignFlags(true);
1721
cy.login();
1822
goToIngestionPage();
1923
});
2024

21-
Cypress.on("uncaught:exception", (err, runnable) => false);
25+
afterEach(() => {
26+
// Guarantee cleanup even if a test fails mid-way
27+
if (createdSources.length > 0) {
28+
// Use goToIngestionPage() rather than navigateToTab("Sources") so that
29+
// cleanup always starts from a known page state. Tests that navigate to
30+
// the Run History tab leave the Sources tab out of view, causing
31+
// navigateToTab to time out waiting for the tab element.
32+
goToIngestionPage();
33+
createdSources.forEach((name) => {
34+
deleteIngestionSource(name);
35+
});
36+
createdSources.length = 0;
37+
}
38+
});
39+
40+
Cypress.on("uncaught:exception", (err) => {
41+
if (err.message.includes("ResizeObserver")) return false;
42+
// Suppress null focus errors from the app (pre-existing React bug where
43+
// .focus() is called on an unmounted element) to prevent flaky test aborts.
44+
if (
45+
err.message.includes("Cannot read properties of null (reading 'focus')")
46+
)
47+
return false;
48+
return true;
49+
});
2250

2351
it("navigate to run history tab from last run column of sources tab", () => {
24-
const sourceName = "test ingestion source history";
52+
const sourceName = `test ingestion source history ${testId}`;
53+
createdSources.push(sourceName);
2554
createAndRunIngestionSource(sourceName);
26-
cy.wait(2000);
27-
cy.contains("td", sourceName)
55+
cy.contains("td", sourceName, { timeout: 10000 })
2856
.siblings("td")
2957
.find('[data-testid="ingestion-source-last-run"]')
3058
.click();
3159
shouldNavigateToRunHistoryTab();
32-
navigateToTab("Sources");
33-
deleteIngestionSource(sourceName);
3460
});
3561

3662
it("navigate to run history tab from View run history option in dropdown of sources", () => {
37-
const sourceName = "ingestion source history";
63+
const sourceName = `ingestion source history ${testId}`;
64+
createdSources.push(sourceName);
3865
createAndRunIngestionSource(sourceName);
39-
cy.wait(2000);
40-
cy.contains("td", sourceName)
66+
cy.contains("td", sourceName, { timeout: 10000 })
4167
.siblings("td")
4268
.find('[data-testid="ingestion-more-options"]')
4369
.click();
4470
cy.get("body .ant-dropdown-menu").contains("View Run History").click();
4571
shouldNavigateToRunHistoryTab();
46-
navigateToTab("Sources");
47-
deleteIngestionSource(sourceName);
4872
});
4973

5074
it("view past executions in run history tab", () => {
51-
const sourceName = "source for past executions";
75+
const sourceName = `source for past executions ${testId}`;
76+
createdSources.push(sourceName);
5277
createAndRunIngestionSource(sourceName);
53-
cy.wait(2000);
5478
navigateToTab("RunHistory");
5579
cy.get('[data-testid="executions-table"]').within(() => {
56-
cy.contains('[data-testid="ingestion-source-name"]', sourceName).should(
57-
"be.visible",
58-
);
80+
cy.contains('[data-testid="ingestion-source-name"]', sourceName, {
81+
timeout: 10000,
82+
}).should("be.visible");
5983
});
60-
navigateToTab("Sources");
61-
deleteIngestionSource(sourceName);
6284
});
6385

6486
it("navigate to sources tab from source name in run history tab", () => {
65-
const sourceName = "source for tab navigation";
87+
const sourceName = `source for tab navigation ${testId}`;
88+
createdSources.push(sourceName);
6689
createAndRunIngestionSource(sourceName);
67-
cy.wait(2000);
6890
navigateToTab("RunHistory");
6991
cy.get('[data-testid="executions-table"]').within(() => {
70-
cy.contains('[data-testid="ingestion-source-name"]', sourceName)
92+
cy.contains('[data-testid="ingestion-source-name"]', sourceName, {
93+
timeout: 10000,
94+
})
7195
.should("be.visible")
7296
.first()
7397
.click({ force: true });
7498
});
7599
shouldNavigateToSourcesTab();
76-
cy.wait(2000);
77-
deleteIngestionSource(sourceName);
78100
});
79101

80102
it("filter execution requests by source name", () => {
81-
const sourceName1 = "Source1";
82-
const sourceName2 = "Source2";
103+
const sourceName1 = `Source1 ${testId}`;
104+
const sourceName2 = `Source2 ${testId}`;
105+
createdSources.push(sourceName1, sourceName2);
83106

84107
createIngestionSource(sourceName1);
85108
createIngestionSource(sourceName2);
@@ -91,9 +114,5 @@ describe("run history tab in manage data sources", () => {
91114
cy.contains("td", sourceName1).should("be.visible");
92115
cy.contains("td", sourceName2).should("not.exist");
93116
});
94-
95-
navigateToTab("Sources");
96-
deleteIngestionSource(sourceName1);
97-
deleteIngestionSource(sourceName2);
98117
});
99118
});

smoke-test/tests/cypress/cypress/e2e/ingestionV3/utils.js

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ export const setIngestionRedesignFlags = (isOn) => {
1313
res.body.data.appConfig.featureFlags.showNavBarRedesign = isOn;
1414
});
1515
}
16+
if (isOn && hasOperationName(req, "batchGetStepStates")) {
17+
// Override the response to mark all requested step IDs as already seen,
18+
// preventing education modals (e.g. CreateSourceEducationModal) from
19+
// appearing during ingestion tests. These tests don't exercise the
20+
// onboarding flow.
21+
const ids = req.body?.variables?.input?.ids || [];
22+
req.reply((res) => {
23+
res.body.data = {
24+
batchGetStepStates: {
25+
results: ids.map((id) => ({ id, properties: [] })),
26+
},
27+
};
28+
});
29+
}
1630
});
1731
};
1832

@@ -24,6 +38,8 @@ const clickIfTestIdPresent = (dataTestId) => {
2438
cy.get("body", { timeout: 2000 }).then(($body) => {
2539
if ($body.find(`[data-testid="${dataTestId}"]`).length) {
2640
cy.get(`[data-testid="${dataTestId}"]`).click();
41+
// Wait for the element (and its modal) to fully close before proceeding
42+
cy.get(`[data-testid="${dataTestId}"]`).should("not.exist");
2743
}
2844
});
2945
};
@@ -178,7 +194,61 @@ export const updateIngestionSource = (
178194
cy.waitTextVisible("Successfully updated ingestion source!");
179195
};
180196

197+
// Cancel any running execution for an ingestion source before deleting it.
198+
// Without this, the executor keeps processing the run after the source is
199+
// deleted, consuming memory and causing OOM crashes when runs pile up.
200+
const cancelRunningExecution = (sourceName) => {
201+
cy.request({
202+
method: "POST",
203+
url: "/api/v2/graphql",
204+
body: {
205+
query: `query listIngestionSources($input: ListIngestionSourcesInput!) {
206+
listIngestionSources(input: $input) {
207+
ingestionSources {
208+
urn
209+
name
210+
executions(start: 0, count: 1) {
211+
executionRequests {
212+
urn
213+
result { status }
214+
}
215+
}
216+
}
217+
}
218+
}`,
219+
variables: { input: { start: 0, count: 100, query: sourceName } },
220+
},
221+
failOnStatusCode: false,
222+
}).then((resp) => {
223+
if (resp.status !== 200 || !resp.body?.data) return;
224+
const sources = resp.body.data.listIngestionSources?.ingestionSources || [];
225+
const source = sources.find((s) => s.name === sourceName);
226+
if (!source) return;
227+
const lastExec = source.executions?.executionRequests?.[0];
228+
if (!lastExec) return;
229+
const status = lastExec.result?.status;
230+
if (status && status !== "RUNNING" && status !== "PENDING") return;
231+
cy.request({
232+
method: "POST",
233+
url: "/api/v2/graphql",
234+
body: {
235+
query: `mutation cancelIngestionExecutionRequest($input: CancelIngestionExecutionRequestInput!) {
236+
cancelIngestionExecutionRequest(input: $input)
237+
}`,
238+
variables: {
239+
input: {
240+
ingestionSourceUrn: source.urn,
241+
executionRequestUrn: lastExec.urn,
242+
},
243+
},
244+
},
245+
failOnStatusCode: false,
246+
});
247+
});
248+
};
249+
181250
export const deleteIngestionSource = (sourceName) => {
251+
cancelRunningExecution(sourceName);
182252
cy.contains("td", sourceName)
183253
.siblings("td")
184254
.find('[data-testid="ingestion-more-options"]')
@@ -196,7 +266,6 @@ export const createAndRunIngestionSource = (sourceName) => {
196266

197267
// Wait for the source selection modal to appear and any loading to finish
198268
// Multi-step builder uses "Search..." while old builder uses "Search data sources..."
199-
cy.wait(1000); // Wait for modal animations to complete
200269
cy.get('[placeholder="Search..."], [placeholder="Search data sources..."]', {
201270
timeout: 10000,
202271
})

0 commit comments

Comments
 (0)