Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});

Expand Down Expand Up @@ -97,24 +97,26 @@ 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}');

// 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');
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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;

Expand All @@ -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');
});
Expand All @@ -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 },
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});

Expand All @@ -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);

Expand All @@ -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');
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;

Expand All @@ -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);
});

Expand All @@ -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);
});

Expand Down
Loading
Loading