-
Notifications
You must be signed in to change notification settings - Fork 17.7k
fix(SQLLab): most recent queries at the top in Query History without refreshing the page #36359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -133,3 +133,112 @@ test('fetches the query history by the tabViewId', async () => { | |||||
| expect(queryResultText).toBeInTheDocument(); | ||||||
| isFeatureEnabledMock.mockClear(); | ||||||
| }); | ||||||
|
|
||||||
| test('displays multiple queries with newest query first', async () => { | ||||||
| const isFeatureEnabledMock = mockedIsFeatureEnabled.mockImplementation( | ||||||
| featureFlag => featureFlag === FeatureFlag.SqllabBackendPersistence, | ||||||
| ); | ||||||
|
|
||||||
| const multipleQueriesApiResult = { | ||||||
| count: 2, | ||||||
| ids: [694, 693], | ||||||
| result: [ | ||||||
| { | ||||||
| changed_on: '2024-03-12T20:10:02.497775', | ||||||
| client_id: 'd2ZDzRYzn', | ||||||
| database: { | ||||||
| database_name: 'examples', | ||||||
| id: 1, | ||||||
| }, | ||||||
| end_time: '1710274202496.047852', | ||||||
| error_message: null, | ||||||
| executed_sql: 'SELECT COUNT(*) from "FCC 2018 Survey"\nLIMIT 1001', | ||||||
| id: 694, | ||||||
| limit: 1000, | ||||||
| limiting_factor: 'DROPDOWN', | ||||||
| progress: 100, | ||||||
| results_key: null, | ||||||
| rows: 1, | ||||||
| schema: 'main', | ||||||
| select_as_cta: false, | ||||||
| sql: 'SELECT COUNT(*) from "FCC 2018 Survey"', | ||||||
| sql_editor_id: '22', | ||||||
| start_time: '1710274202445.992920', | ||||||
| status: QueryState.Success, | ||||||
| tab_name: 'Untitled Query 2', | ||||||
| tmp_table_name: null, | ||||||
| tracking_url: null, | ||||||
| user: { | ||||||
| first_name: 'admin', | ||||||
| id: 1, | ||||||
| last_name: 'user', | ||||||
| }, | ||||||
| }, | ||||||
| { | ||||||
| changed_on: '2024-03-12T20:01:02.497775', | ||||||
| client_id: 'b0ZDzRYzn', | ||||||
| database: { | ||||||
| database_name: 'examples', | ||||||
| id: 1, | ||||||
| }, | ||||||
| end_time: '1710273662496.047852', | ||||||
| error_message: null, | ||||||
| executed_sql: 'SELECT * from "FCC 2018 Survey"\nLIMIT 1001', | ||||||
| id: 693, | ||||||
| limit: 1000, | ||||||
| limiting_factor: 'DROPDOWN', | ||||||
| progress: 100, | ||||||
| results_key: null, | ||||||
| rows: 443, | ||||||
| schema: 'main', | ||||||
| select_as_cta: false, | ||||||
| sql: 'SELECT * from "FCC 2018 Survey"', | ||||||
| sql_editor_id: '22', | ||||||
| start_time: '1710273662445.992920', | ||||||
| status: QueryState.Success, | ||||||
| tab_name: 'Untitled Query 1', | ||||||
| tmp_table_name: null, | ||||||
| tracking_url: null, | ||||||
| user: { | ||||||
| first_name: 'admin', | ||||||
| id: 1, | ||||||
| last_name: 'user', | ||||||
| }, | ||||||
| }, | ||||||
| ], | ||||||
| }; | ||||||
|
|
||||||
| const editorQueryApiRoute = `glob:*/api/v1/query/?q=*`; | ||||||
| fetchMock.get(editorQueryApiRoute, multipleQueriesApiResult); | ||||||
| const { container } = render(setup(), { useRedux: true, initialState }); | ||||||
|
|
||||||
| await waitFor(() => | ||||||
| expect(fetchMock.calls(editorQueryApiRoute).length).toBe(1), | ||||||
| ); | ||||||
|
|
||||||
| expect(screen.getByTestId('listview-table')).toBeVisible(); | ||||||
| expect(screen.getByRole('table')).toBeVisible(); | ||||||
|
|
||||||
| const tableRows = container.querySelectorAll( | ||||||
| 'table > tbody > tr:not(.ant-table-measure-row)', | ||||||
| ); | ||||||
| expect(tableRows).toHaveLength(2); | ||||||
|
|
||||||
| // Check that both queries are present | ||||||
| const olderQueryRow = screen.getByText('443'); | ||||||
| const newerQueryElements = screen.getAllByText('1'); | ||||||
| expect(olderQueryRow).toBeInTheDocument(); | ||||||
| expect(newerQueryElements.length).toBeGreaterThan(0); | ||||||
|
|
||||||
| // Verify ordering: newer query (1 row) should appear before older query (443 rows) | ||||||
| // Find the actual row elements to check their order | ||||||
| const firstDataRow = tableRows[0]; | ||||||
| const secondDataRow = tableRows[1]; | ||||||
|
|
||||||
| // The newer query should be in the first row (has 1 result row) | ||||||
| expect(firstDataRow).toHaveTextContent('1'); | ||||||
| // The older query should be in the second row (has 443 result rows) | ||||||
| expect(secondDataRow).toHaveTextContent('443'); | ||||||
|
|
||||||
| isFeatureEnabledMock.mockClear(); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Using Severity Level: Critical 🚨
Suggested change
Why it matters? ⭐This suggestion is valid: mockClear() only clears call history and keeps the mock implementation, which can cause test leakage if any test relies on the default/unmocked behavior. Using mockReset() (or mockRestore()/jest.resetAllMocks() in a shared afterEach) will clear both history and implementation making tests more isolated. The suggested improved_code (calling mockReset on the underlying jest mock) is appropriate. Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** superset-frontend/src/SqlLab/components/QueryHistory/QueryHistory.test.tsx
**Line:** 243:243
**Comment:**
*Possible Bug: Using `mockClear()` only clears call history but does not remove the mock implementation; this can leave `isFeatureEnabled` returning the mocked value in subsequent tests and cause test leakage. Replace the single-line cleanup with a call that resets the mock implementation (for example `mockReset()` on the original mock) so the mock state is fully cleared between tests.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. |
||||||
| }); | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -91,7 +91,11 @@ const QueryHistory = ({ | |||||
| editorId, | ||||||
| ) | ||||||
| .concat(data.result) | ||||||
| .reverse() | ||||||
| .sort((a, b) => { | ||||||
| const aTime = a.startDttm || 0; | ||||||
| const bTime = b.startDttm || 0; | ||||||
| return aTime - bTime; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Sorting direction is reversed: the comparator returns aTime - bTime which orders oldest-first; to show most recent queries at the top the comparator must return bTime - aTime (descending order). [logic error] Severity Level: Minor
Suggested change
Why it matters? ⭐The current comparator orders oldest-first (ascending). For a query history UI you almost always want most-recent-first. Reversing the subtraction to Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
**Line:** 97:97
**Comment:**
*Logic Error: Sorting direction is reversed: the comparator returns aTime - bTime which orders oldest-first; to show most recent queries at the top the comparator must return bTime - aTime (descending order).
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. |
||||||
| }) | ||||||
| : getEditorQueries(queries, editorId), | ||||||
| [queries, data, editorId], | ||||||
| ); | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Using
screen.getAllByText('1')is fragile and can match other '1' occurrences in the DOM (headers, timestamps, ids), causing flakiness; instead, explicitly search the table rows (which you already query) to assert that at least one data row contains the expected '1' value. [possible bug]Severity Level: Critical 🚨
Why it matters? ⭐
This is a useful and practical improvement. Using getAllByText('1') is brittle because small numeric strings often appear elsewhere (headers, timestamps). Scoping the assertion to the queried table rows (which the test already selects) makes the check more robust and directly verifies the rendered data. The improved_code correctly uses the rendered container to assert presence within the table rows.
Prompt for AI Agent 🤖