This is an automated email from the ASF dual-hosted git repository.

msyavuz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 3a565a6c16e fix(tests): update DatasetList tests to new fetch-mock API 
(#37623)
3a565a6c16e is described below

commit 3a565a6c16ec23251bf009b1ae64a56e1c2ca13a
Author: Joe Li <[email protected]>
AuthorDate: Tue Feb 3 03:15:58 2026 -0800

    fix(tests): update DatasetList tests to new fetch-mock API (#37623)
    
    Co-authored-by: Claude Opus 4.5 <[email protected]>
---
 .../DatasetList/DatasetList.behavior.test.tsx      |  78 +++--
 .../DatasetList/DatasetList.integration.test.tsx   |  52 +--
 .../DatasetList/DatasetList.listview.test.tsx      | 378 +++++++++++++--------
 .../DatasetList/DatasetList.permissions.test.tsx   |  82 +++--
 .../src/pages/DatasetList/DatasetList.test.tsx     |  84 +++--
 .../pages/DatasetList/DatasetList.testHelpers.tsx  | 183 +++++-----
 6 files changed, 538 insertions(+), 319 deletions(-)

diff --git 
a/superset-frontend/src/pages/DatasetList/DatasetList.behavior.test.tsx 
b/superset-frontend/src/pages/DatasetList/DatasetList.behavior.test.tsx
index af73876de11..4aa30c34600 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.behavior.test.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.behavior.test.tsx
@@ -62,8 +62,8 @@ afterEach(async () => {
   // Reset browser history state to prevent query params leaking between tests
   window.history.replaceState({}, '', '/');
 
-  fetchMock.resetHistory();
-  fetchMock.restore();
+  fetchMock.clearHistory();
+  fetchMock.removeRoutes();
   jest.restoreAllMocks();
 });
 
@@ -97,7 +97,9 @@ test('typing in search triggers debounced API call with 
search filter', async ()
   const searchInput = within(searchContainer).getByRole('textbox');
 
   // Record initial API calls
-  const initialCallCount = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const initialCallCount = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Type search query and submit with Enter to trigger the debounced fetch
   await userEvent.type(searchInput, 'sales{enter}');
@@ -105,16 +107,16 @@ test('typing in search triggers debounced API call with 
search filter', async ()
   // Wait for debounced API call
   await waitFor(
     () => {
-      const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+      const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
       expect(calls.length).toBeGreaterThan(initialCallCount);
     },
     { timeout: 5000 },
   );
 
   // Verify the latest API call includes search filter in URL
-  const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+  const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
   const latestCall = calls[calls.length - 1];
-  const url = latestCall[0] as string;
+  const url = latestCall.url as string;
 
   // URL should contain filters parameter with search term
   expect(url).toContain('filters');
@@ -134,13 +136,14 @@ test('typing in search triggers debounced API call with 
search filter', async ()
 test('500 error triggers danger toast with error message', async () => {
   const addDangerToast = jest.fn();
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     {
       status: 500,
       body: { message: 'Internal Server Error' },
     },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   // Pass toast spy directly via props to bypass withToasts HOC
@@ -174,10 +177,11 @@ test('500 error triggers danger toast with error 
message', async () => {
 test('network timeout triggers danger toast', async () => {
   const addDangerToast = jest.fn();
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { throws: new Error('Network timeout') },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   // Pass toast spy directly via props to bypass withToasts HOC
@@ -215,10 +219,14 @@ test('clicking delete opens modal with related objects 
count', async () => {
   // Set up delete mocks
   setupDeleteMocks(datasetToDelete.id);
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetToDelete], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetToDelete],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -256,10 +264,14 @@ test('clicking delete opens modal with related objects 
count', async () => {
 test('clicking export calls handleResourceExport with dataset ID', async () => 
{
   const datasetToExport = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetToExport], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetToExport],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -290,16 +302,23 @@ test('clicking duplicate opens modal and submits 
duplicate request', async () =>
     kind: 'virtual',
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetToDuplicate], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetToDuplicate],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   fetchMock.post(
     API_ENDPOINTS.DATASET_DUPLICATE,
-    { id: 999, table_name: 'Copy of Dataset' },
-    { overwriteRoutes: true },
+    {
+      id: 999,
+      table_name: 'Copy of Dataset',
+    },
+    { name: API_ENDPOINTS.DATASET_DUPLICATE },
   );
 
   const addSuccessToast = jest.fn();
@@ -314,7 +333,7 @@ test('clicking duplicate opens modal and submits duplicate 
request', async () =>
   });
 
   // Track initial dataset list API calls BEFORE duplicate action
-  const initialDatasetCallCount = fetchMock.calls(
+  const initialDatasetCallCount = fetchMock.callHistory.calls(
     API_ENDPOINTS.DATASETS,
   ).length;
 
@@ -341,11 +360,11 @@ test('clicking duplicate opens modal and submits 
duplicate request', async () =>
 
   // Verify duplicate API was called with correct payload
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASET_DUPLICATE);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASET_DUPLICATE);
     expect(calls.length).toBeGreaterThan(0);
 
     // Verify POST body contains correct dataset info
-    const requestBody = JSON.parse(calls[0][1]?.body as string);
+    const requestBody = JSON.parse(calls[0].options?.body as string);
     expect(requestBody.base_model_id).toBe(datasetToDuplicate.id);
     expect(requestBody.table_name).toBe('Copy of Dataset');
   });
@@ -358,7 +377,7 @@ test('clicking duplicate opens modal and submits duplicate 
request', async () =>
   // Verify refreshData() is called (observable via new dataset list API call)
   await waitFor(
     () => {
-      const datasetCalls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+      const datasetCalls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
       expect(datasetCalls.length).toBeGreaterThan(initialDatasetCallCount);
     },
     { timeout: 3000 },
@@ -379,10 +398,14 @@ test('certified dataset shows badge and tooltip with 
certification details', asy
     }),
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [certifiedDataset], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [certifiedDataset],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -420,10 +443,14 @@ test('dataset with warning shows icon and tooltip with 
markdown content', async
     }),
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetWithWarning], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetWithWarning],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -455,10 +482,11 @@ test('dataset with warning shows icon and tooltip with 
markdown content', async
 test('dataset name links to Explore with correct URL and accessible label', 
async () => {
   const dataset = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
diff --git 
a/superset-frontend/src/pages/DatasetList/DatasetList.integration.test.tsx 
b/superset-frontend/src/pages/DatasetList/DatasetList.integration.test.tsx
index 0b228509b2a..80490826077 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.integration.test.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.integration.test.tsx
@@ -62,8 +62,8 @@ afterEach(async () => {
   // Reset browser history state to prevent query params leaking between tests
   window.history.replaceState({}, '', '/');
 
-  fetchMock.resetHistory();
-  fetchMock.restore();
+  fetchMock.clearHistory();
+  fetchMock.removeRoutes();
   jest.restoreAllMocks();
 });
 
@@ -72,11 +72,10 @@ test('ListView provider correctly merges filter + sort + 
pagination state on ref
   // the ListView provider correctly merges them for the API call.
   // Component tests verify individual pieces persist; this verifies they 
COMBINE correctly.
 
-  fetchMock.get(
-    API_ENDPOINTS.DATASETS,
-    { result: mockDatasets, count: mockDatasets.length },
-    { overwriteRoutes: true },
-  );
+  fetchMock.get(API_ENDPOINTS.DATASETS, {
+    result: mockDatasets,
+    count: mockDatasets.length,
+  });
 
   renderDatasetList(mockAdminUser);
 
@@ -90,30 +89,34 @@ test('ListView provider correctly merges filter + sort + 
pagination state on ref
     name: /Name/i,
   });
 
-  const callsBeforeSort = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const callsBeforeSort = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
   await userEvent.click(nameHeader);
 
   // Wait for sort-triggered refetch to complete before applying filter
   await waitFor(() => {
-    expect(fetchMock.calls(API_ENDPOINTS.DATASETS).length).toBeGreaterThan(
-      callsBeforeSort,
-    );
+    expect(
+      fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS).length,
+    ).toBeGreaterThan(callsBeforeSort);
   });
 
   // 2. Apply a filter using selectOption helper
-  const beforeFilterCallCount = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const beforeFilterCallCount = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
   await selectOption('Virtual', 'Type');
 
   // Wait for filter API call to complete
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(beforeFilterCallCount);
   });
 
   // 3. Verify the final API call contains ALL three state pieces merged 
correctly
-  const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+  const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
   const latestCall = calls[calls.length - 1];
-  const url = latestCall[0] as string;
+  const url = latestCall.url as string;
 
   // Decode the rison payload using URL parser
   const risonPayload = new URL(url, 'http://localhost').searchParams.get('q');
@@ -147,11 +150,10 @@ test('bulk action orchestration: selection → action → 
cleanup cycle works co
 
   setupBulkDeleteMocks();
 
-  fetchMock.get(
-    API_ENDPOINTS.DATASETS,
-    { result: mockDatasets, count: mockDatasets.length },
-    { overwriteRoutes: true },
-  );
+  fetchMock.get(API_ENDPOINTS.DATASETS, {
+    result: mockDatasets,
+    count: mockDatasets.length,
+  });
 
   renderDatasetList(mockAdminUser);
 
@@ -213,7 +215,7 @@ test('bulk action orchestration: selection → action → 
cleanup cycle works co
   await userEvent.type(confirmInput, 'DELETE');
 
   // Capture datasets call count before confirming
-  const datasetsCallCountBeforeDelete = fetchMock.calls(
+  const datasetsCallCountBeforeDelete = fetchMock.callHistory.calls(
     API_ENDPOINTS.DATASETS,
   ).length;
 
@@ -224,7 +226,9 @@ test('bulk action orchestration: selection → action → 
cleanup cycle works co
 
   // 3. Wait for bulk delete API call to be made
   await waitFor(() => {
-    const deleteCalls = fetchMock.calls(API_ENDPOINTS.DATASET_BULK_DELETE);
+    const deleteCalls = fetchMock.callHistory.calls(
+      API_ENDPOINTS.DATASET_BULK_DELETE,
+    );
     expect(deleteCalls.length).toBeGreaterThan(0);
   });
 
@@ -235,7 +239,9 @@ test('bulk action orchestration: selection → action → 
cleanup cycle works co
 
   // Wait for datasets refetch after delete
   await waitFor(() => {
-    const datasetsCallCount = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+    const datasetsCallCount = fetchMock.callHistory.calls(
+      API_ENDPOINTS.DATASETS,
+    ).length;
     expect(datasetsCallCount).toBeGreaterThan(datasetsCallCountBeforeDelete);
   });
 
diff --git 
a/superset-frontend/src/pages/DatasetList/DatasetList.listview.test.tsx 
b/superset-frontend/src/pages/DatasetList/DatasetList.listview.test.tsx
index b43f78dc9c4..449d85a7ba5 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.listview.test.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.listview.test.tsx
@@ -33,6 +33,7 @@ import {
   mockHandleResourceExport,
   assertOnlyExpectedCalls,
   API_ENDPOINTS,
+  DELETE_ROUTE_NAME,
 } from './DatasetList.testHelpers';
 
 const mockAddDangerToast = jest.fn();
@@ -112,10 +113,11 @@ const setupErrorTestScenario = ({
   });
 
   // Configure fetchMock to return single dataset
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   // Render component with toast mocks
@@ -143,8 +145,8 @@ afterEach(async () => {
   // QueryParamProvider reads from window.history, which persists across 
renders
   window.history.replaceState({}, '', '/');
 
-  fetchMock.resetHistory();
-  fetchMock.restore();
+  fetchMock.clearHistory();
+  fetchMock.removeRoutes();
   jest.restoreAllMocks();
 });
 
@@ -199,10 +201,11 @@ test('renders all required column headers', async () => {
 test('displays dataset name in Name column', async () => {
   const dataset = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -216,10 +219,14 @@ test('displays dataset type as Physical or Virtual', 
async () => {
   const physicalDataset = mockDatasets[0]; // kind: 'physical'
   const virtualDataset = mockDatasets[1]; // kind: 'virtual'
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [physicalDataset, virtualDataset], count: 2 },
-    { overwriteRoutes: true },
+    {
+      result: [physicalDataset, virtualDataset],
+      count: 2,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -234,10 +241,11 @@ test('displays dataset type as Physical or Virtual', 
async () => {
 test('displays database name in Database column', async () => {
   const dataset = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -252,10 +260,11 @@ test('displays database name in Database column', async 
() => {
 test('displays schema name in Schema column', async () => {
   const dataset = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -268,10 +277,11 @@ test('displays schema name in Schema column', async () => 
{
 test('displays last modified date in humanized format', async () => {
   const dataset = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -296,21 +306,23 @@ test('sorting by Name column updates API call with sort 
parameter', async () =>
   });
 
   // Record initial calls
-  const initialCalls = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const initialCalls = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Click Name header to sort
   await userEvent.click(nameHeader);
 
   // Wait for new API call
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(initialCalls);
   });
 
   // Verify latest call includes sort parameter
-  const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+  const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
   const latestCall = calls[calls.length - 1];
-  const url = latestCall[0] as string;
+  const url = latestCall.url as string;
 
   // URL should contain order_column for sorting
   expect(url).toMatch(/order_column|sort/);
@@ -328,17 +340,19 @@ test('sorting by Database column updates sort parameter', 
async () => {
     name: /Database/i,
   });
 
-  const initialCalls = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const initialCalls = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   await userEvent.click(databaseHeader);
 
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(initialCalls);
   });
 
-  const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
-  const url = calls[calls.length - 1][0] as string;
+  const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
+  const url = calls[calls.length - 1].url as string;
   expect(url).toMatch(/order_column|sort/);
 });
 
@@ -354,27 +368,30 @@ test('sorting by Last modified column updates sort 
parameter', async () => {
     name: /Last modified/i,
   });
 
-  const initialCalls = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const initialCalls = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   await userEvent.click(modifiedHeader);
 
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(initialCalls);
   });
 
-  const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
-  const url = calls[calls.length - 1][0] as string;
+  const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
+  const url = calls[calls.length - 1].url as string;
   expect(url).toMatch(/order_column|sort/);
 });
 
 test('export button triggers handleResourceExport with dataset ID', async () 
=> {
   const dataset = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -403,10 +420,11 @@ test('delete button opens modal with dataset details', 
async () => {
 
   setupDeleteMocks(dataset.id);
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -429,10 +447,14 @@ test('delete action successfully deletes dataset and 
refreshes list', async () =
   const datasetToDelete = mockDatasets[0];
   setupDeleteMocks(datasetToDelete.id);
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetToDelete], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetToDelete],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser, {
@@ -455,7 +477,9 @@ test('delete action successfully deletes dataset and 
refreshes list', async () =
   await userEvent.type(confirmInput, 'DELETE');
 
   // Track API calls before confirm
-  const callsBefore = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const callsBefore = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Click confirm - find the danger button (last delete button in modal)
   const confirmButton = within(modal)
@@ -465,13 +489,8 @@ test('delete action successfully deletes dataset and 
refreshes list', async () =
 
   // Wait for delete API call
   await waitFor(() => {
-    const deleteCalls = fetchMock.calls(
-      `glob:*/api/v1/dataset/${datasetToDelete.id}`,
-    );
-    const hasDelete = deleteCalls.some(
-      call => (call[1] as RequestInit)?.method === 'DELETE',
-    );
-    expect(hasDelete).toBe(true);
+    const deleteCalls = fetchMock.callHistory.calls(DELETE_ROUTE_NAME);
+    expect(deleteCalls.length).toBeGreaterThan(0);
   });
 
   // Success toast shown and modal closes
@@ -482,9 +501,9 @@ test('delete action successfully deletes dataset and 
refreshes list', async () =
 
   // List refreshes
   await waitFor(() => {
-    expect(fetchMock.calls(API_ENDPOINTS.DATASETS).length).toBeGreaterThan(
-      callsBefore,
-    );
+    expect(
+      fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS).length,
+    ).toBeGreaterThan(callsBefore);
   });
 });
 
@@ -492,10 +511,11 @@ test('delete action cancel closes modal without 
deleting', async () => {
   const dataset = mockDatasets[0];
   setupDeleteMocks(dataset.id);
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -520,9 +540,11 @@ test('delete action cancel closes modal without deleting', 
async () => {
   });
 
   // No delete API call made (only related_objects GET was called)
-  const deleteCalls = fetchMock.calls(`glob:*/api/v1/dataset/${dataset.id}`);
+  const deleteCalls = fetchMock.callHistory.calls(
+    `glob:*/api/v1/dataset/${dataset.id}`,
+  );
   const hasDeleteMethod = deleteCalls.some(
-    call => (call[1] as RequestInit)?.method === 'DELETE',
+    call => (call.options as RequestInit)?.method === 'DELETE',
   );
   expect(hasDeleteMethod).toBe(false);
 
@@ -534,10 +556,11 @@ test('duplicate action successfully duplicates virtual 
dataset', async () => {
   const virtualDataset = mockDatasets[1]; // Virtual dataset (kind: 'virtual')
   setupDuplicateMocks();
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [virtualDataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser, {
@@ -560,7 +583,9 @@ test('duplicate action successfully duplicates virtual 
dataset', async () => {
   await userEvent.type(input, 'Copy of Analytics');
 
   // Track API calls before submit
-  const callsBefore = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const callsBefore = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Submit
   const submitButton = within(modal).getByRole('button', {
@@ -570,7 +595,9 @@ test('duplicate action successfully duplicates virtual 
dataset', async () => {
 
   // Wait for duplicate API call and modal closes
   await waitFor(() => {
-    const dupCalls = fetchMock.calls(API_ENDPOINTS.DATASET_DUPLICATE);
+    const dupCalls = fetchMock.callHistory.calls(
+      API_ENDPOINTS.DATASET_DUPLICATE,
+    );
     expect(dupCalls.length).toBeGreaterThan(0);
     // Modal closes (duplicate success doesn't show toast, just closes modal)
     expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
@@ -578,9 +605,9 @@ test('duplicate action successfully duplicates virtual 
dataset', async () => {
 
   // List refreshes
   await waitFor(() => {
-    expect(fetchMock.calls(API_ENDPOINTS.DATASETS).length).toBeGreaterThan(
-      callsBefore,
-    );
+    expect(
+      fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS).length,
+    ).toBeGreaterThan(callsBefore);
   });
 });
 
@@ -588,10 +615,14 @@ test('duplicate button visible only for virtual 
datasets', async () => {
   const physicalDataset = mockDatasets[0]; // kind: 'physical'
   const virtualDataset = mockDatasets[1]; // kind: 'virtual'
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [physicalDataset, virtualDataset], count: 2 },
-    { overwriteRoutes: true },
+    {
+      result: [physicalDataset, virtualDataset],
+      count: 2,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -648,10 +679,14 @@ test('bulk select enables checkboxes', async () => {
 }, 30000);
 
 test('selecting all datasets shows correct count in toolbar', async () => {
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: mockDatasets, count: mockDatasets.length },
-    { overwriteRoutes: true },
+    {
+      result: mockDatasets,
+      count: mockDatasets.length,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -688,10 +723,14 @@ test('selecting all datasets shows correct count in 
toolbar', async () => {
 }, 30000);
 
 test('bulk export triggers export with selected IDs', async () => {
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [mockDatasets[0]], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [mockDatasets[0]],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -731,10 +770,14 @@ test('bulk export triggers export with selected IDs', 
async () => {
 test('bulk delete opens confirmation modal', async () => {
   setupBulkDeleteMocks();
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [mockDatasets[0]], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [mockDatasets[0]],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -838,10 +881,14 @@ test('certified badge appears for certified datasets', 
async () => {
     }),
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [certifiedDataset], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [certifiedDataset],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -869,10 +916,14 @@ test('warning icon appears for datasets with warnings', 
async () => {
     }),
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetWithWarning], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetWithWarning],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -898,10 +949,14 @@ test('info tooltip appears for datasets with 
descriptions', async () => {
     description: 'Sales data from Q4 2024',
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetWithDescription], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetWithDescription],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -924,10 +979,11 @@ test('info tooltip appears for datasets with 
descriptions', async () => {
 test('dataset name links to Explore page', async () => {
   const dataset = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -948,10 +1004,14 @@ test('dataset name links to Explore page', async () => {
 test('physical dataset shows delete, export, and edit actions (no duplicate)', 
async () => {
   const physicalDataset = mockDatasets[0]; // kind: 'physical'
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [physicalDataset], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [physicalDataset],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -980,10 +1040,11 @@ test('physical dataset shows delete, export, and edit 
actions (no duplicate)', a
 test('virtual dataset shows delete, export, edit, and duplicate actions', 
async () => {
   const virtualDataset = mockDatasets[1]; // kind: 'virtual'
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [virtualDataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -1013,10 +1074,11 @@ test('edit action is enabled for dataset owner', async 
() => {
     owners: [{ id: mockAdminUser.userId, username: 'admin' }],
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -1040,10 +1102,11 @@ test('edit action is disabled for non-owner', async () 
=> {
     owners: [{ id: 999, username: 'other_user' }], // Different user
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   // Use a non-admin user to test ownership check
@@ -1073,10 +1136,11 @@ test('all action buttons are clickable and enabled for 
admin user', async () =>
     owners: [{ id: mockAdminUser.userId, username: 'admin' }],
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [virtualDataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -1112,10 +1176,14 @@ test('all action buttons are clickable and enabled for 
admin user', async () =>
 });
 
 test('displays error when initial dataset fetch fails with 500', async () => {
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { status: 500, body: { message: 'Internal Server Error' } },
-    { overwriteRoutes: true },
+    {
+      status: 500,
+      body: { message: 'Internal Server Error' },
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser, {
@@ -1134,10 +1202,14 @@ test('displays error when initial dataset fetch fails 
with 500', async () => {
 });
 
 test('displays error when initial dataset fetch fails with 403 permission 
denied', async () => {
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { status: 403, body: { message: 'Access Denied' } },
-    { overwriteRoutes: true },
+    {
+      status: 403,
+      body: { message: 'Access Denied' },
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser, {
@@ -1402,20 +1474,22 @@ test('sort order persists after deleting a dataset', 
async () => {
   });
 
   // Record initial API calls count
-  const initialCalls = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const initialCalls = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Click Name header to sort
   await userEvent.click(nameHeader);
 
   // Wait for new API call with sort parameter
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(initialCalls);
   });
 
   // Record the sort parameter from the API call after sorting
-  const callsAfterSort = fetchMock.calls(API_ENDPOINTS.DATASETS);
-  const sortedUrl = callsAfterSort[callsAfterSort.length - 1][0] as string;
+  const callsAfterSort = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
+  const sortedUrl = callsAfterSort[callsAfterSort.length - 1].url as string;
   expect(sortedUrl).toMatch(/order_column|sort/);
 
   // Delete a dataset - get delete button from first row only
@@ -1433,7 +1507,9 @@ test('sort order persists after deleting a dataset', 
async () => {
   await userEvent.type(confirmInput, 'DELETE');
 
   // Record call count before delete to track refetch
-  const callsBeforeDelete = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const callsBeforeDelete = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   const confirmButton = within(modal)
     .getAllByRole('button', { name: /^delete$/i })
@@ -1452,7 +1528,9 @@ test('sort order persists after deleting a dataset', 
async () => {
 
   // Wait for list refetch to complete (prevents async cleanup error)
   await waitFor(() => {
-    const currentCalls = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+    const currentCalls = fetchMock.callHistory.calls(
+      API_ENDPOINTS.DATASETS,
+    ).length;
     expect(currentCalls).toBeGreaterThan(callsBeforeDelete);
   });
 
@@ -1476,10 +1554,14 @@ test('sort order persists after deleting a dataset', 
async () => {
 // test. Component tests here focus on individual behaviors.
 
 test('bulk selection clears when filter changes', async () => {
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: mockDatasets, count: mockDatasets.length },
-    { overwriteRoutes: true },
+    {
+      result: mockDatasets,
+      count: mockDatasets.length,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -1528,7 +1610,9 @@ test('bulk selection clears when filter changes', async 
() => {
   });
 
   // Record API call count before filter
-  const beforeFilterCallCount = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const beforeFilterCallCount = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Wait for filter combobox to be ready before applying filter
   await screen.findByRole('combobox', { name: 'Type' });
@@ -1538,14 +1622,14 @@ test('bulk selection clears when filter changes', async 
() => {
 
   // Wait for filter API call to complete
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(beforeFilterCallCount);
   });
 
   // Verify filter was applied by decoding URL payload
-  const urlAfterFilter = fetchMock
+  const urlAfterFilter = fetchMock.callHistory
     .calls(API_ENDPOINTS.DATASETS)
-    .at(-1)?.[0] as string;
+    .at(-1)?.url as string;
   const risonAfterFilter = new URL(
     urlAfterFilter,
     'http://localhost',
@@ -1578,19 +1662,22 @@ test('type filter API call includes correct filter 
parameter', async () => {
   await screen.findByRole('combobox', { name: 'Type' });
 
   // Snapshot call count before filter
-  const callsBeforeFilter = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const callsBeforeFilter = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Apply Type filter
   await selectOption('Virtual', 'Type');
 
   // Wait for filter API call to complete
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(callsBeforeFilter);
   });
 
   // Verify the latest API call includes the Type filter
-  const url = fetchMock.calls(API_ENDPOINTS.DATASETS).at(-1)?.[0] as string;
+  const url = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS).at(-1)
+    ?.url as string;
   expect(url).toContain('filters');
 
   // searchParams.get() already URL-decodes, so pass directly to rison.decode
@@ -1622,21 +1709,23 @@ test('type filter persists after duplicating a 
dataset', async () => {
   await screen.findByRole('combobox', { name: 'Type' });
 
   // Snapshot call count before filter
-  const callsBeforeFilter = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const callsBeforeFilter = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Apply Type filter
   await selectOption('Virtual', 'Type');
 
   // Wait for filter API call to complete
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(callsBeforeFilter);
   });
 
   // Verify filter is present by checking the latest API call
-  const urlAfterFilter = fetchMock
+  const urlAfterFilter = fetchMock.callHistory
     .calls(API_ENDPOINTS.DATASETS)
-    .at(-1)?.[0] as string;
+    .at(-1)?.url as string;
   const risonAfterFilter = new URL(
     urlAfterFilter,
     'http://localhost',
@@ -1654,7 +1743,7 @@ test('type filter persists after duplicating a dataset', 
async () => {
   );
 
   // Capture datasets API call count BEFORE any duplicate operations
-  const datasetsCallCountBeforeDuplicate = fetchMock.calls(
+  const datasetsCallCountBeforeDuplicate = fetchMock.callHistory.calls(
     API_ENDPOINTS.DATASETS,
   ).length;
 
@@ -1682,20 +1771,24 @@ test('type filter persists after duplicating a 
dataset', async () => {
 
   // Wait for duplicate API call to be made
   await waitFor(() => {
-    const duplicateCalls = fetchMock.calls(API_ENDPOINTS.DATASET_DUPLICATE);
+    const duplicateCalls = fetchMock.callHistory.calls(
+      API_ENDPOINTS.DATASET_DUPLICATE,
+    );
     expect(duplicateCalls.length).toBeGreaterThan(0);
   });
 
   // Wait for datasets refetch to occur (proves duplicate triggered a refresh)
   await waitFor(() => {
-    const datasetsCallCount = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+    const datasetsCallCount = fetchMock.callHistory.calls(
+      API_ENDPOINTS.DATASETS,
+    ).length;
     
expect(datasetsCallCount).toBeGreaterThan(datasetsCallCountBeforeDuplicate);
   });
 
   // Verify Type filter persisted in the NEW datasets API call after 
duplication
-  const urlAfterDuplicate = fetchMock
+  const urlAfterDuplicate = fetchMock.callHistory
     .calls(API_ENDPOINTS.DATASETS)
-    .at(-1)?.[0] as string;
+    .at(-1)?.url as string;
   const risonAfterDuplicate = new URL(
     urlAfterDuplicate,
     'http://localhost',
@@ -1729,10 +1822,11 @@ test('edit action shows error toast when dataset fetch 
fails', async () => {
     ],
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [ownedDataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   // Mock SupersetClient.get to fail for the specific dataset endpoint
@@ -1776,10 +1870,14 @@ test('bulk export error shows toast and clears loading 
state', async () => {
   // Mock handleResourceExport to throw an error
   mockHandleResourceExport.mockRejectedValueOnce(new Error('Export failed'));
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [mockDatasets[0]], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [mockDatasets[0]],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser, {
@@ -1836,16 +1934,19 @@ test('bulk export error shows toast and clears loading 
state', async () => {
 
 test('bulk delete error shows toast without refreshing list', async () => {
   // Mock bulk delete to fail
-  fetchMock.delete(
-    API_ENDPOINTS.DATASET_BULK_DELETE,
-    { status: 500, body: { message: 'Bulk delete failed' } },
-    { overwriteRoutes: true },
-  );
+  fetchMock.delete(API_ENDPOINTS.DATASET_BULK_DELETE, {
+    status: 500,
+    body: { message: 'Bulk delete failed' },
+  });
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [mockDatasets[0]], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [mockDatasets[0]],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser, {
@@ -1919,10 +2020,14 @@ test('bulk select shows "N Selected (Virtual)" for 
virtual-only selection', asyn
   // Use only virtual datasets
   const virtualDatasets = mockDatasets.filter(d => d.kind === 'virtual');
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: virtualDatasets, count: virtualDatasets.length },
-    { overwriteRoutes: true },
+    {
+      result: virtualDatasets,
+      count: virtualDatasets.length,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -1966,10 +2071,14 @@ test('bulk select shows "N Selected (Physical)" for 
physical-only selection', as
   // Use only physical datasets
   const physicalDatasets = mockDatasets.filter(d => d.kind === 'physical');
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: physicalDatasets, count: physicalDatasets.length },
-    { overwriteRoutes: true },
+    {
+      result: physicalDatasets,
+      count: physicalDatasets.length,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -2017,10 +2126,14 @@ test('bulk select shows mixed count for virtual and 
physical selection', async (
     mockDatasets.find(d => d.kind === 'virtual')!,
   ];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: mixedDatasets, count: mixedDatasets.length },
-    { overwriteRoutes: true },
+    {
+      result: mixedDatasets,
+      count: mixedDatasets.length,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -2081,20 +2194,17 @@ test('delete modal shows affected dashboards with 
overflow for >10 items', async
     title: `Dashboard ${i + 1}`,
   }));
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
-  fetchMock.get(
-    `glob:*/api/v1/dataset/${dataset.id}/related_objects*`,
-    {
-      charts: { count: 0, result: [] },
-      dashboards: { count: 15, result: manyDashboards },
-    },
-    { overwriteRoutes: true },
-  );
+  fetchMock.get(`glob:*/api/v1/dataset/${dataset.id}/related_objects*`, {
+    charts: { count: 0, result: [] },
+    dashboards: { count: 15, result: manyDashboards },
+  });
 
   renderDatasetList(mockAdminUser);
 
@@ -2126,20 +2236,17 @@ test('delete modal shows affected dashboards with 
overflow for >10 items', async
 test('delete modal hides affected dashboards section when count is zero', 
async () => {
   const dataset = mockDatasets[0];
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
-  fetchMock.get(
-    `glob:*/api/v1/dataset/${dataset.id}/related_objects*`,
-    {
-      charts: { count: 2, result: [{ id: 1, slice_name: 'Chart 1' }] },
-      dashboards: { count: 0, result: [] },
-    },
-    { overwriteRoutes: true },
-  );
+  fetchMock.get(`glob:*/api/v1/dataset/${dataset.id}/related_objects*`, {
+    charts: { count: 2, result: [{ id: 1, slice_name: 'Chart 1' }] },
+    dashboards: { count: 0, result: [] },
+  });
 
   renderDatasetList(mockAdminUser);
 
@@ -2172,20 +2279,17 @@ test('delete modal shows affected charts with overflow 
for >10 items', async ()
     slice_name: `Chart ${i + 1}`,
   }));
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
-  fetchMock.get(
-    `glob:*/api/v1/dataset/${dataset.id}/related_objects*`,
-    {
-      charts: { count: 12, result: manyCharts },
-      dashboards: { count: 0, result: [] },
-    },
-    { overwriteRoutes: true },
-  );
+  fetchMock.get(`glob:*/api/v1/dataset/${dataset.id}/related_objects*`, {
+    charts: { count: 12, result: manyCharts },
+    dashboards: { count: 0, result: [] },
+  });
 
   renderDatasetList(mockAdminUser);
 
diff --git 
a/superset-frontend/src/pages/DatasetList/DatasetList.permissions.test.tsx 
b/superset-frontend/src/pages/DatasetList/DatasetList.permissions.test.tsx
index a997ff9a551..5a93913551b 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.permissions.test.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.permissions.test.tsx
@@ -20,7 +20,6 @@ import { act, screen, waitFor, within } from 
'@testing-library/react';
 import fetchMock from 'fetch-mock';
 import {
   setupMocks,
-  setupApiPermissions,
   renderDatasetList,
   mockAdminUser,
   mockReadOnlyUser,
@@ -35,7 +34,7 @@ import {
 jest.setTimeout(30000);
 
 beforeEach(() => {
-  setupMocks();
+  // Default setup - tests that need different permissions will call 
setupMocks() again
   jest.clearAllMocks();
 });
 
@@ -51,14 +50,21 @@ afterEach(async () => {
   // Reset browser history to prevent query param leakage
   window.history.replaceState({}, '', '/');
 
-  fetchMock.resetHistory();
-  fetchMock.restore();
+  fetchMock.clearHistory();
+  fetchMock.removeRoutes();
   jest.restoreAllMocks();
 });
 
 test('admin users see all UI elements', async () => {
   // Setup API with full admin permissions
-  setupApiPermissions(['can_read', 'can_write', 'can_export', 
'can_duplicate']);
+  setupMocks({
+    [API_ENDPOINTS.DATASETS_INFO]: [
+      'can_read',
+      'can_write',
+      'can_export',
+      'can_duplicate',
+    ],
+  });
 
   renderDatasetList(mockAdminUser);
 
@@ -90,7 +96,7 @@ test('admin users see all UI elements', async () => {
 
 test('read-only users cannot see Actions column', async () => {
   // Setup API with read-only permissions
-  setupApiPermissions(['can_read']);
+  setupMocks({ [API_ENDPOINTS.DATASETS_INFO]: ['can_read'] });
 
   renderDatasetList(mockReadOnlyUser);
 
@@ -107,7 +113,7 @@ test('read-only users cannot see Actions column', async () 
=> {
 
 test('read-only users cannot see bulk select button', async () => {
   // Setup API with read-only permissions
-  setupApiPermissions(['can_read']);
+  setupMocks({ [API_ENDPOINTS.DATASETS_INFO]: ['can_read'] });
 
   renderDatasetList(mockReadOnlyUser);
 
@@ -123,7 +129,7 @@ test('read-only users cannot see bulk select button', async 
() => {
 
 test('read-only users cannot see Create/Import buttons', async () => {
   // Setup API with read-only permissions
-  setupApiPermissions(['can_read']);
+  setupMocks({ [API_ENDPOINTS.DATASETS_INFO]: ['can_read'] });
 
   renderDatasetList(mockReadOnlyUser);
 
@@ -144,7 +150,9 @@ test('read-only users cannot see Create/Import buttons', 
async () => {
 
 test('write users see Actions column', async () => {
   // Setup API with write permissions
-  setupApiPermissions(['can_read', 'can_write', 'can_export']);
+  setupMocks({
+    [API_ENDPOINTS.DATASETS_INFO]: ['can_read', 'can_write', 'can_export'],
+  });
 
   renderDatasetList(mockWriteUser);
 
@@ -162,7 +170,9 @@ test('write users see Actions column', async () => {
 
 test('write users see bulk select button', async () => {
   // Setup API with write permissions
-  setupApiPermissions(['can_read', 'can_write', 'can_export']);
+  setupMocks({
+    [API_ENDPOINTS.DATASETS_INFO]: ['can_read', 'can_write', 'can_export'],
+  });
 
   renderDatasetList(mockWriteUser);
 
@@ -177,7 +187,9 @@ test('write users see bulk select button', async () => {
 
 test('write users see Create/Import buttons', async () => {
   // Setup API with write permissions
-  setupApiPermissions(['can_read', 'can_write', 'can_export']);
+  setupMocks({
+    [API_ENDPOINTS.DATASETS_INFO]: ['can_read', 'can_write', 'can_export'],
+  });
 
   renderDatasetList(mockWriteUser);
 
@@ -198,7 +210,7 @@ test('write users see Create/Import buttons', async () => {
 
 test('export-only users see bulk select (for export only)', async () => {
   // Setup API with export-only permissions
-  setupApiPermissions(['can_read', 'can_export']);
+  setupMocks({ [API_ENDPOINTS.DATASETS_INFO]: ['can_read', 'can_export'] });
 
   renderDatasetList(mockExportOnlyUser);
 
@@ -214,7 +226,7 @@ test('export-only users see bulk select (for export only)', 
async () => {
 
 test('export-only users cannot see Create/Import buttons', async () => {
   // Setup API with export-only permissions
-  setupApiPermissions(['can_read', 'can_export']);
+  setupMocks({ [API_ENDPOINTS.DATASETS_INFO]: ['can_read', 'can_export'] });
 
   renderDatasetList(mockExportOnlyUser);
 
@@ -233,14 +245,23 @@ test('export-only users cannot see Create/Import 
buttons', async () => {
 
 test('action buttons respect user permissions', async () => {
   // Setup API with full admin permissions
-  setupApiPermissions(['can_read', 'can_write', 'can_export', 
'can_duplicate']);
+  setupMocks({
+    [API_ENDPOINTS.DATASETS_INFO]: [
+      'can_read',
+      'can_write',
+      'can_export',
+      'can_duplicate',
+    ],
+  });
 
   const dataset = mockDatasets[0];
 
+  // Override the default DATASETS route with test-specific data
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -263,14 +284,16 @@ test('action buttons respect user permissions', async () 
=> {
 
 test('read-only user sees no delete or duplicate buttons in row', async () => {
   // Setup API with read-only permissions
-  setupApiPermissions(['can_read']);
+  setupMocks({ [API_ENDPOINTS.DATASETS_INFO]: ['can_read'] });
 
   const dataset = mockDatasets[0];
 
+  // Override the default DATASETS route with test-specific data
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockReadOnlyUser);
@@ -299,17 +322,21 @@ test('read-only user sees no delete or duplicate buttons 
in row', async () => {
 test('write user sees edit, delete, and export actions', async () => {
   // Setup API with write permissions (includes delete)
   // Note: can_write grants both edit and delete permissions in DatasetList
-  setupApiPermissions(['can_read', 'can_write', 'can_export']);
+  setupMocks({
+    [API_ENDPOINTS.DATASETS_INFO]: ['can_read', 'can_write', 'can_export'],
+  });
 
   const dataset = {
     ...mockDatasets[0],
     owners: [{ id: mockWriteUser.userId, username: 'writeuser' }],
   };
 
+  // Override the default DATASETS route with test-specific data
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockWriteUser);
@@ -341,14 +368,16 @@ test('write user sees edit, delete, and export actions', 
async () => {
 test('export-only user has no Actions column (no write/duplicate 
permissions)', async () => {
   // Setup API with export-only permissions
   // Note: Export action alone doesn't render Actions column - it's in 
toolbar/bulk select
-  setupApiPermissions(['can_read', 'can_export']);
+  setupMocks({ [API_ENDPOINTS.DATASETS_INFO]: ['can_read', 'can_export'] });
 
   const dataset = mockDatasets[0];
 
+  // Override the default DATASETS route with test-specific data
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockExportOnlyUser);
@@ -377,15 +406,20 @@ test('export-only user has no Actions column (no 
write/duplicate permissions)',
 
 test('user with can_duplicate sees duplicate button only for virtual 
datasets', async () => {
   // Setup API with duplicate permission
-  setupApiPermissions(['can_read', 'can_duplicate']);
+  setupMocks({ [API_ENDPOINTS.DATASETS_INFO]: ['can_read', 'can_duplicate'] });
 
   const physicalDataset = mockDatasets[0]; // kind: 'physical'
   const virtualDataset = mockDatasets[1]; // kind: 'virtual'
 
+  // Override the default DATASETS route with test-specific data
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [physicalDataset, virtualDataset], count: 2 },
-    { overwriteRoutes: true },
+    {
+      result: [physicalDataset, virtualDataset],
+      count: 2,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
diff --git a/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx 
b/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx
index 51ad4cbab7a..d77d27aae1f 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx
@@ -53,8 +53,8 @@ afterEach(async () => {
   // Reset browser history state to prevent query params leaking between tests
   window.history.replaceState({}, '', '/');
 
-  fetchMock.resetHistory();
-  fetchMock.restore();
+  fetchMock.clearHistory();
+  fetchMock.removeRoutes();
   jest.restoreAllMocks();
 });
 
@@ -74,7 +74,6 @@ test('shows loading state during initial data fetch', () => {
     new Promise(resolve =>
       setTimeout(() => resolve({ result: [], count: 0 }), 10000),
     ),
-    { overwriteRoutes: true },
   );
 
   renderDatasetList(mockAdminUser);
@@ -93,7 +92,6 @@ test('maintains component structure during loading', () => {
     new Promise(resolve =>
       setTimeout(() => resolve({ result: [], count: 0 }), 10000),
     ),
-    { overwriteRoutes: true },
   );
 
   renderDatasetList(mockAdminUser);
@@ -215,10 +213,14 @@ test('handles datasets with missing fields and renders 
gracefully', async () =>
     sql: null,
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetWithMissingFields], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetWithMissingFields],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -242,10 +244,11 @@ test('handles datasets with missing fields and renders 
gracefully', async () =>
 });
 
 test('handles empty results (shows empty state)', async () => {
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [], count: 0 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -258,7 +261,7 @@ test('makes correct initial API call on load', async () => {
   renderDatasetList(mockAdminUser);
 
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(0);
   });
 });
@@ -267,9 +270,9 @@ test('API call includes correct page size', async () => {
   renderDatasetList(mockAdminUser);
 
   await waitFor(() => {
-    const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+    const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
     expect(calls.length).toBeGreaterThan(0);
-    const url = calls[0][0] as string;
+    const url = calls[0].url as string;
     expect(url).toContain('page_size');
   });
 });
@@ -281,7 +284,9 @@ test('typing in name filter updates input value and 
triggers API with decoded se
   const searchInput = within(searchContainer).getByRole('textbox');
 
   // Record initial API calls
-  const initialCallCount = fetchMock.calls(API_ENDPOINTS.DATASETS).length;
+  const initialCallCount = fetchMock.callHistory.calls(
+    API_ENDPOINTS.DATASETS,
+  ).length;
 
   // Type in search box and press Enter to trigger search
   await userEvent.type(searchInput, 'sales{enter}');
@@ -294,11 +299,11 @@ test('typing in name filter updates input value and 
triggers API with decoded se
   // Wait for API call after Enter key press
   await waitFor(
     () => {
-      const calls = fetchMock.calls(API_ENDPOINTS.DATASETS);
+      const calls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
       expect(calls.length).toBeGreaterThan(initialCallCount);
 
       // Get latest API call
-      const url = calls[calls.length - 1][0] as string;
+      const url = calls[calls.length - 1].url as string;
 
       // Verify URL contains search filter
       expect(url).toContain('filters');
@@ -351,9 +356,7 @@ test('handles 500 error on initial load without crashing', 
async () => {
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { throws: new Error('Internal Server Error') },
-    {
-      overwriteRoutes: true,
-    },
+    {},
   );
 
   renderDatasetList(mockAdminUser, {
@@ -369,8 +372,9 @@ test('handles 500 error on initial load without crashing', 
async () => {
 test('handles 403 error on _info endpoint and disables create actions', async 
() => {
   const addDangerToast = jest.fn();
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS_INFO);
   fetchMock.get(API_ENDPOINTS.DATASETS_INFO, mockApiError403, {
-    overwriteRoutes: true,
+    name: API_ENDPOINTS.DATASETS_INFO,
   });
 
   renderDatasetList(mockAdminUser, {
@@ -391,10 +395,11 @@ test('handles 403 error on _info endpoint and disables 
create actions', async ()
 });
 
 test('handles network timeout without crashing', async () => {
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { throws: new Error('Network timeout') },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser, {
@@ -412,7 +417,7 @@ test('component requires explicit mocks for all API 
endpoints', async () => {
   setupMocks();
 
   // Clear call history to start fresh
-  fetchMock.resetHistory();
+  fetchMock.clearHistory();
 
   // Render component with standard setup
   renderDatasetList(mockAdminUser);
@@ -421,15 +426,15 @@ test('component requires explicit mocks for all API 
endpoints', async () => {
   await waitForDatasetsPageReady();
 
   // Verify that critical endpoints were called and had mocks available
-  const newDatasetsCalls = fetchMock.calls(API_ENDPOINTS.DATASETS);
-  const newInfoCalls = fetchMock.calls(API_ENDPOINTS.DATASETS_INFO);
+  const newDatasetsCalls = fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS);
+  const newInfoCalls = 
fetchMock.callHistory.calls(API_ENDPOINTS.DATASETS_INFO);
 
   // These should have been called during render
   expect(newDatasetsCalls.length).toBeGreaterThan(0);
   expect(newInfoCalls.length).toBeGreaterThan(0);
 
   // Verify no unmatched calls (all endpoints were mocked)
-  const unmatchedCalls = fetchMock.calls(false); // false = unmatched only
+  const unmatchedCalls = fetchMock.callHistory.calls('unmatched');
   expect(unmatchedCalls.length).toBe(0);
 });
 
@@ -453,10 +458,14 @@ test('renders datasets with certification data', async () 
=> {
     }),
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [certifiedDataset], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [certifiedDataset],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -481,10 +490,14 @@ test('displays datasets with warning_markdown', async () 
=> {
     }),
   };
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetWithWarning], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetWithWarning],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -503,10 +516,14 @@ test('displays datasets with warning_markdown', async () 
=> {
 test('displays dataset with multiple owners', async () => {
   const datasetWithOwners = mockDatasets[1]; // Has 2 owners: Jane Smith, Bob 
Jones
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetWithOwners], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetWithOwners],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -525,10 +542,14 @@ test('displays dataset with multiple owners', async () => 
{
 test('displays ModifiedInfo with humanized date', async () => {
   const datasetWithModified = mockDatasets[0]; // changed_by_name: 'John Doe', 
changed_on: '1 day ago'
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
-    { result: [datasetWithModified], count: 1 },
-    { overwriteRoutes: true },
+    {
+      result: [datasetWithModified],
+      count: 1,
+    },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
@@ -548,10 +569,11 @@ test('displays ModifiedInfo with humanized date', async 
() => {
 test('dataset name links to Explore with correct explore_url', async () => {
   const dataset = mockDatasets[0]; // explore_url: 
'/explore/?datasource=1__table'
 
+  fetchMock.removeRoute(API_ENDPOINTS.DATASETS);
   fetchMock.get(
     API_ENDPOINTS.DATASETS,
     { result: [dataset], count: 1 },
-    { overwriteRoutes: true },
+    { name: API_ENDPOINTS.DATASETS },
   );
 
   renderDatasetList(mockAdminUser);
diff --git 
a/superset-frontend/src/pages/DatasetList/DatasetList.testHelpers.tsx 
b/superset-frontend/src/pages/DatasetList/DatasetList.testHelpers.tsx
index 844ad8f9163..4c1324edcd4 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.testHelpers.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.testHelpers.tsx
@@ -23,6 +23,7 @@ import { Provider } from 'react-redux';
 import { MemoryRouter } from 'react-router-dom';
 import { configureStore } from '@reduxjs/toolkit';
 import { QueryParamProvider } from 'use-query-params';
+import { ReactRouter5Adapter } from 'use-query-params/adapters/react-router-5';
 import DatasetList from 'src/pages/DatasetList';
 import handleResourceExport from 'src/utils/export';
 
@@ -329,14 +330,17 @@ export const API_ENDPOINTS = {
   DATASET_RELATED_CHANGED_BY: 'glob:*/api/v1/dataset/related/changed_by*',
 };
 
-// Setup API permissions mock (for permission-based testing)
-export const setupApiPermissions = (permissions: string[]) => {
-  fetchMock.get(
-    API_ENDPOINTS.DATASETS_INFO,
-    { permissions },
-    { overwriteRoutes: true },
-  );
-};
+// Default permissions for admin users
+const DEFAULT_PERMISSIONS = [
+  'can_read',
+  'can_write',
+  'can_export',
+  'can_duplicate',
+];
+
+// Type for payload map used in setupMocks
+// Uses Record type to allow API_ENDPOINTS values as keys
+type PayloadMap = Partial<Record<string, string[]>>;
 
 // Store utilities
 export const createMockStore = (initialState: Partial<StoreState> = {}) =>
@@ -384,7 +388,7 @@ export const renderDatasetList = (
   return render(
     <Provider store={store}>
       <MemoryRouter>
-        <QueryParamProvider>
+        <QueryParamProvider adapter={ReactRouter5Adapter}>
           <DatasetList user={user} {...props} />
         </QueryParamProvider>
       </MemoryRouter>
@@ -400,6 +404,10 @@ export const waitForDatasetsPageReady = async () => {
   await screen.findByText('Datasets');
 };
 
+// Route name constants for delete operations (used for call history queries)
+export const DELETE_ROUTE_NAME = 'dataset-delete';
+export const RELATED_OBJECTS_ROUTE_NAME = 'dataset-related-objects';
+
 // Helper functions for specific operations
 export const setupDeleteMocks = (datasetId: number) => {
   fetchMock.get(
@@ -408,30 +416,29 @@ export const setupDeleteMocks = (datasetId: number) => {
       charts: mockRelatedCharts,
       dashboards: mockRelatedDashboards,
     },
-    { overwriteRoutes: true },
+    { name: RELATED_OBJECTS_ROUTE_NAME },
   );
 
   fetchMock.delete(
     `glob:*/api/v1/dataset/${datasetId}`,
-    { message: 'Dataset deleted successfully' },
-    { overwriteRoutes: true },
+    {
+      message: 'Dataset deleted successfully',
+    },
+    { name: DELETE_ROUTE_NAME },
   );
 };
 
 export const setupDuplicateMocks = () => {
-  fetchMock.post(
-    API_ENDPOINTS.DATASET_DUPLICATE,
-    { id: 999, table_name: 'Copy of Dataset' },
-    { overwriteRoutes: true },
-  );
+  fetchMock.post(API_ENDPOINTS.DATASET_DUPLICATE, {
+    id: 999,
+    table_name: 'Copy of Dataset',
+  });
 };
 
 export const setupBulkDeleteMocks = () => {
-  fetchMock.delete(
-    API_ENDPOINTS.DATASET_BULK_DELETE,
-    { message: '3 datasets deleted successfully' },
-    { overwriteRoutes: true },
-  );
+  fetchMock.delete(API_ENDPOINTS.DATASET_BULK_DELETE, {
+    message: '3 datasets deleted successfully',
+  });
 };
 
 // Setup error mocks for negative flow testing
@@ -439,25 +446,17 @@ export const setupDeleteErrorMocks = (
   datasetId: number,
   statusCode: number,
 ) => {
-  fetchMock.get(
-    `glob:*/api/v1/dataset/${datasetId}/related_objects*`,
-    {
-      status: statusCode,
-      body: { message: 'Failed to fetch related objects' },
-    },
-    { overwriteRoutes: true },
-  );
+  fetchMock.get(`glob:*/api/v1/dataset/${datasetId}/related_objects*`, {
+    status: statusCode,
+    body: { message: 'Failed to fetch related objects' },
+  });
 };
 
 export const setupDuplicateErrorMocks = (statusCode: number) => {
-  fetchMock.post(
-    API_ENDPOINTS.DATASET_DUPLICATE,
-    {
-      status: statusCode,
-      body: { message: 'Failed to duplicate dataset' },
-    },
-    { overwriteRoutes: true },
-  );
+  fetchMock.post(API_ENDPOINTS.DATASET_DUPLICATE, {
+    status: statusCode,
+    body: { message: 'Failed to duplicate dataset' },
+  });
 };
 
 /**
@@ -468,11 +467,10 @@ export const setupDuplicateErrorMocks = (statusCode: 
number) => {
  * @throws If any unmocked endpoints were called or expected endpoints weren't 
called
  */
 export const assertOnlyExpectedCalls = (expectedEndpoints: string[]) => {
-  const allCalls = fetchMock.calls(true); // Get all calls including unmatched
-  const unmatchedCalls = allCalls.filter(call => call.isUnmatched);
+  const unmatchedCalls = fetchMock.callHistory.calls('unmatched');
 
   if (unmatchedCalls.length > 0) {
-    const unmatchedUrls = unmatchedCalls.map(call => call[0]);
+    const unmatchedUrls = unmatchedCalls.map(call => call.url);
     throw new Error(
       `Unmocked endpoints called: ${unmatchedUrls.join(', ')}. ` +
         'Add explicit mocks in setupMocks() or test setup.',
@@ -481,7 +479,7 @@ export const assertOnlyExpectedCalls = (expectedEndpoints: 
string[]) => {
 
   // Verify expected endpoints were called
   expectedEndpoints.forEach(endpoint => {
-    const calls = fetchMock.calls(endpoint);
+    const calls = fetchMock.callHistory.calls(endpoint);
     if (calls.length === 0) {
       throw new Error(
         `Expected endpoint not called: ${endpoint}. ` +
@@ -492,48 +490,75 @@ export const assertOnlyExpectedCalls = 
(expectedEndpoints: string[]) => {
 };
 
 // MSW setup using fetch-mock (following ChartList pattern)
-export const setupMocks = () => {
-  fetchMock.reset();
+// payloadMap allows customizing permissions for the _info endpoint
+export const setupMocks = (
+  payloadMap: PayloadMap = {
+    [API_ENDPOINTS.DATASETS_INFO]: DEFAULT_PERMISSIONS,
+  },
+) => {
+  fetchMock.removeRoutes();
 
-  fetchMock.get(API_ENDPOINTS.DATASETS_INFO, {
-    permissions: ['can_read', 'can_write', 'can_export', 'can_duplicate'],
-  });
+  // Get permissions from payloadMap or use defaults
+  const permissions =
+    payloadMap[API_ENDPOINTS.DATASETS_INFO] || DEFAULT_PERMISSIONS;
 
-  fetchMock.get(API_ENDPOINTS.DATASETS, {
-    result: mockDatasets,
-    count: mockDatasets.length,
-  });
+  fetchMock.get(
+    API_ENDPOINTS.DATASETS_INFO,
+    { permissions },
+    { name: API_ENDPOINTS.DATASETS_INFO },
+  );
 
-  fetchMock.get(API_ENDPOINTS.DATASET_FAVORITE_STATUS, {
-    result: [],
-  });
+  fetchMock.get(
+    API_ENDPOINTS.DATASETS,
+    {
+      result: mockDatasets,
+      count: mockDatasets.length,
+    },
+    { name: API_ENDPOINTS.DATASETS },
+  );
 
-  fetchMock.get(API_ENDPOINTS.DATASET_RELATED_DATABASE, {
-    result: [
-      { value: 1, text: 'PostgreSQL' },
-      { value: 2, text: 'MySQL' },
-      { value: 3, text: 'Redshift' },
-    ],
-    count: 3,
-  });
+  fetchMock.get(
+    API_ENDPOINTS.DATASET_FAVORITE_STATUS,
+    { result: [] },
+    { name: API_ENDPOINTS.DATASET_FAVORITE_STATUS },
+  );
 
-  fetchMock.get(API_ENDPOINTS.DATASET_RELATED_SCHEMA, {
-    result: [
-      { value: 'public', text: 'public' },
-      { value: 'analytics', text: 'analytics' },
-      { value: 'metrics', text: 'metrics' },
-      { value: 'reports', text: 'reports' },
-    ],
-    count: 4,
-  });
+  fetchMock.get(
+    API_ENDPOINTS.DATASET_RELATED_DATABASE,
+    {
+      result: [
+        { value: 1, text: 'PostgreSQL' },
+        { value: 2, text: 'MySQL' },
+        { value: 3, text: 'Redshift' },
+      ],
+      count: 3,
+    },
+    { name: API_ENDPOINTS.DATASET_RELATED_DATABASE },
+  );
 
-  fetchMock.get(API_ENDPOINTS.DATASET_RELATED_OWNERS, {
-    result: [],
-    count: 0,
-  });
+  fetchMock.get(
+    API_ENDPOINTS.DATASET_RELATED_SCHEMA,
+    {
+      result: [
+        { value: 'public', text: 'public' },
+        { value: 'analytics', text: 'analytics' },
+        { value: 'metrics', text: 'metrics' },
+        { value: 'reports', text: 'reports' },
+      ],
+      count: 4,
+    },
+    { name: API_ENDPOINTS.DATASET_RELATED_SCHEMA },
+  );
 
-  fetchMock.get(API_ENDPOINTS.DATASET_RELATED_CHANGED_BY, {
-    result: [],
-    count: 0,
-  });
+  fetchMock.get(
+    API_ENDPOINTS.DATASET_RELATED_OWNERS,
+    { result: [], count: 0 },
+    { name: API_ENDPOINTS.DATASET_RELATED_OWNERS },
+  );
+
+  fetchMock.get(
+    API_ENDPOINTS.DATASET_RELATED_CHANGED_BY,
+    { result: [], count: 0 },
+    { name: API_ENDPOINTS.DATASET_RELATED_CHANGED_BY },
+  );
 };

Reply via email to