diff --git a/frontend/src/mocks-server/__mockdata__/explorer_views.ts b/frontend/src/mocks-server/__mockdata__/explorer_views.ts index b51eb2ee9c..4719e77697 100644 --- a/frontend/src/mocks-server/__mockdata__/explorer_views.ts +++ b/frontend/src/mocks-server/__mockdata__/explorer_views.ts @@ -78,13 +78,13 @@ export const explorerView = { extraData: '{"color":"#00ffd0"}', }, { - uuid: '58b010b6-8be9-40d1-8d25-f73b5f7314ad', - name: 'success traces list view', + uuid: '8c4bf492-d54d-4ab2-a8d6-9c1563f46e1f', + name: 'R-test panel', category: '', - createdAt: '2023-08-30T13:00:40.958011925Z', - createdBy: 'test-email', - updatedAt: '2024-04-29T13:09:06.175537361Z', - updatedBy: 'test-email', + createdAt: '2024-07-01T13:45:57.924686766Z', + createdBy: 'test-user-test', + updatedAt: '2024-07-01T13:48:31.032106578Z', + updatedBy: 'test-user-test', sourcePage: 'traces', tags: [''], compositeQuery: { @@ -106,13 +106,13 @@ export const explorerView = { items: [ { key: { - key: 'responseStatusCode', + key: 'httpMethod', dataType: 'string', type: 'tag', isColumn: true, isJSON: false, }, - value: '200', + value: 'GET', op: '=', }, ], @@ -128,7 +128,7 @@ export const explorerView = { order: 'desc', }, ], - reduceTo: 'sum', + reduceTo: 'avg', timeAggregation: 'rate', spaceAggregation: 'sum', ShiftBy: 0, @@ -137,7 +137,7 @@ export const explorerView = { panelType: 'list', queryType: 'builder', }, - extraData: '{"color":"#bdff9d"}', + extraData: '{"color":"#AD7F58"}', }, ], }; diff --git a/frontend/src/pages/SaveView/__test__/SaveView.test.tsx b/frontend/src/pages/SaveView/__test__/SaveView.test.tsx new file mode 100644 index 0000000000..fee0d3d6f0 --- /dev/null +++ b/frontend/src/pages/SaveView/__test__/SaveView.test.tsx @@ -0,0 +1,172 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +/* eslint-disable sonarjs/no-duplicate-string */ +import ROUTES from 'constants/routes'; +import { explorerView } from 'mocks-server/__mockdata__/explorer_views'; +import { server } from 'mocks-server/server'; +import { rest } from 'msw'; +import { MemoryRouter, Route } from 'react-router-dom'; +import { fireEvent, render, screen, waitFor, within } from 'tests/test-utils'; + +import SaveView from '..'; + +const handleExplorerTabChangeTest = jest.fn(); +jest.mock('hooks/useHandleExplorerTabChange', () => ({ + useHandleExplorerTabChange: () => ({ + handleExplorerTabChange: handleExplorerTabChangeTest, + }), +})); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn().mockReturnValue({ + pathname: `${ROUTES.TRACES_SAVE_VIEWS}`, + }), +})); + +describe('SaveView', () => { + it('should render the SaveView component', async () => { + render(); + expect(await screen.findByText('Table View')).toBeInTheDocument(); + + const savedViews = screen.getAllByRole('row'); + expect(savedViews).toHaveLength(2); + + // assert row 1 + expect( + within(document.querySelector('.view-tag') as HTMLElement).getByText('T'), + ).toBeInTheDocument(); + expect(screen.getByText('test-user-1')).toBeInTheDocument(); + + // assert row 2 + expect(screen.getByText('R-test panel')).toBeInTheDocument(); + expect(screen.getByText('test-user-test')).toBeInTheDocument(); + }); + + it('explorer icon should take the user to the related explorer page', async () => { + render( + + + + + , + ); + + expect(await screen.findByText('Table View')).toBeInTheDocument(); + + const explorerIcon = await screen.findAllByTestId('go-to-explorer'); + expect(explorerIcon[0]).toBeInTheDocument(); + + // Simulate click on explorer icon + fireEvent.click(explorerIcon[0]); + + await waitFor(() => + expect(handleExplorerTabChangeTest).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + '/traces-explorer', // Asserts the third argument is '/traces-explorer' + ), + ); + }); + + it('should render the SaveView component with a search input', async () => { + render(); + const searchInput = screen.getByPlaceholderText('Search for views...'); + expect(await screen.findByText('Table View')).toBeInTheDocument(); + + expect(searchInput).toBeInTheDocument(); + + // search for 'R-test panel' + searchInput.focus(); + (searchInput as HTMLInputElement).setSelectionRange( + 0, + (searchInput as HTMLInputElement).value.length, + ); + + fireEvent.change(searchInput, { target: { value: 'R-test panel' } }); + expect(searchInput).toHaveValue('R-test panel'); + searchInput.blur(); + + expect(await screen.findByText('R-test panel')).toBeInTheDocument(); + + // Table View should not be present now + const savedViews = screen.getAllByRole('row'); + expect(savedViews).toHaveLength(1); + }); + + it('should be able to edit name of view', async () => { + server.use( + rest.put( + 'http://localhost/api/v1/explorer/views/test-uuid-1', + // eslint-disable-next-line no-return-assign + (_req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + ...explorerView, + data: [ + ...explorerView.data, + (explorerView.data[0].name = 'New Table View'), + ], + }), + ), + ), + ); + render(); + + const editButton = await screen.findAllByTestId('edit-view'); + fireEvent.click(editButton[0]); + + const viewName = await screen.findByTestId('view-name'); + expect(viewName).toBeInTheDocument(); + expect(viewName).toHaveValue('Table View'); + + const newViewName = 'New Table View'; + fireEvent.change(viewName, { target: { value: newViewName } }); + expect(viewName).toHaveValue(newViewName); + + const saveButton = await screen.findByTestId('save-view'); + fireEvent.click(saveButton); + + await waitFor(() => + expect(screen.getByText(newViewName)).toBeInTheDocument(), + ); + }); + + it('should be able to delete a view', async () => { + server.use( + rest.delete( + 'http://localhost/api/v1/explorer/views/test-uuid-1', + (_req, res, ctx) => res(ctx.status(200), ctx.json({ status: 'success' })), + ), + ); + + render(); + + const deleteButton = await screen.findAllByTestId('delete-view'); + fireEvent.click(deleteButton[0]); + + expect(await screen.findByText('delete_confirm_message')).toBeInTheDocument(); + + const confirmButton = await screen.findByTestId('confirm-delete'); + fireEvent.click(confirmButton); + + await waitFor(() => expect(screen.queryByText('Table View')).toBeNull()); + }); + + it('should render empty state', async () => { + server.use( + rest.get('http://localhost/api/v1/explorer/views', (_req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + status: 'success', + data: [], + }), + ), + ), + ); + render(); + + expect(screen.getByText('No data')).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/pages/SaveView/index.tsx b/frontend/src/pages/SaveView/index.tsx index 7088c4a78c..03c76151b7 100644 --- a/frontend/src/pages/SaveView/index.tsx +++ b/frontend/src/pages/SaveView/index.tsx @@ -263,13 +263,19 @@ function SaveView(): JSX.Element { handleEditModelOpen(view, bgColor)} /> - handleRedirectQuery(view)} /> + handleRedirectQuery(view)} + data-testid="go-to-explorer" + /> handleDeleteModelOpen(view.uuid, view.name)} /> @@ -347,6 +353,7 @@ function SaveView(): JSX.Element { onClick={onDeleteHandler} className="delete-btn" disabled={isDeleteLoading} + data-testid="confirm-delete" > Delete view , @@ -371,6 +378,7 @@ function SaveView(): JSX.Element { icon={} onClick={onUpdateQueryHandler} disabled={isViewUpdating} + data-testid="save-view" > Save changes , @@ -385,6 +393,7 @@ function SaveView(): JSX.Element { setNewViewName(e.target.value)} /> diff --git a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx index 0622a365c2..a28776f0d0 100644 --- a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx +++ b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx @@ -617,9 +617,7 @@ describe('TracesExplorer - ', () => { const viewListOptions = await screen.findByRole('listbox'); expect(viewListOptions).toBeInTheDocument(); - expect( - within(viewListOptions).getByText('success traces list view'), - ).toBeInTheDocument(); + expect(within(viewListOptions).getByText('R-test panel')).toBeInTheDocument(); expect(within(viewListOptions).getByText('Table View')).toBeInTheDocument();