|
| 1 | +//* .spec (the expected behavior for the component) vs .test (straughtforward saying here are the tests) |
| 2 | + |
| 3 | +import React from 'react'; |
| 4 | +// render- lets us display the component |
| 5 | +// screen- allows us to query the DOM for els |
| 6 | +// waitFor- waits for async UI updates (ex- Axios completes) |
| 7 | +import { render, screen, waitFor } from '@testing-library/react'; |
| 8 | +// simulates user behavior |
| 9 | +import userEvent from '@testing-library/user-event'; |
| 10 | +import { BrowserRouter } from 'react-router-dom'; |
| 11 | +import TestNewQuery from '../../src/pages/TestNewQuery'; |
| 12 | +import axios, { Axios } from 'axios'; |
| 13 | +import { AxiosResponse } from 'axios'; |
| 14 | + |
| 15 | +// Wrap component in BrowserRouter to render Register inside router (helper function) |
| 16 | +const renderWithRouter = (component: React.ReactElement): ReturnType<typeof render> => { |
| 17 | + return render(<BrowserRouter>{component}</BrowserRouter>); |
| 18 | +}; |
| 19 | + |
| 20 | +//? TESTS |
| 21 | +describe('Test New Query Component', () => { |
| 22 | + //? Testing (1): Test New Query heading renders |
| 23 | + it('renders the Test New Query heading', () => { |
| 24 | + renderWithRouter(<TestNewQuery />); |
| 25 | + // will check if the heading 'Test Your Query!' is rendering on the screen |
| 26 | + expect(screen.getByRole('heading', { name: /Test New Query/i })).toBeInTheDocument(); |
| 27 | + }); |
| 28 | + |
| 29 | + //? Testing (2): input fields render |
| 30 | + it('renders the input fields (Query Name, DB Link, Query)', () => { |
| 31 | + renderWithRouter(<TestNewQuery />); |
| 32 | + // will check input fields are rendering on the screen |
| 33 | + expect(screen.getByPlaceholderText(/Name your query/i)).toBeInTheDocument(); |
| 34 | + expect(screen.getByPlaceholderText(/Enter DB link here/i)).toBeInTheDocument(); |
| 35 | + expect(screen.getByPlaceholderText(/Write your SQL query here/i)).toBeInTheDocument(); |
| 36 | + }); |
| 37 | + |
| 38 | + //? Testing (3): buttons render |
| 39 | + it('render the buttons (Test Query, Save Query, Improve with AI)', () => { |
| 40 | + renderWithRouter(<TestNewQuery />); |
| 41 | + // will check buttons are rendering on the screen |
| 42 | + const testQueryButton = screen.getByRole('button', { name: /Run Query/i }); |
| 43 | + const saveQueryButton = screen.getByRole('button', { name: /Save Query/i }); |
| 44 | + const improveWithAIButton = screen.getByRole('button', { name: /Improve with AI/i }); |
| 45 | + expect(testQueryButton).toBeInTheDocument(); |
| 46 | + expect(saveQueryButton).toBeInTheDocument(); |
| 47 | + expect(improveWithAIButton).toBeInTheDocument(); |
| 48 | + }); |
| 49 | + |
| 50 | + //? Testing (4): input fields are editable |
| 51 | + it('checks input fields are editable', async (): Promise<void> => { |
| 52 | + renderWithRouter(<TestNewQuery />); |
| 53 | + // will find input fields by placeholder text |
| 54 | + const queryNameInput: HTMLTextAreaElement = |
| 55 | + screen.getByPlaceholderText(/Name your query/i); |
| 56 | + const dbLinkInput: HTMLTextAreaElement = |
| 57 | + screen.getByPlaceholderText(/Enter DB link here/i); |
| 58 | + const queryInput: HTMLTextAreaElement = screen.getByPlaceholderText( |
| 59 | + /Write your SQL query here/i |
| 60 | + ); |
| 61 | + |
| 62 | + // will simulate typing into the input field |
| 63 | + await userEvent.type(queryNameInput, 'My Test Query'); |
| 64 | + await userEvent.type(dbLinkInput, 'My Test DB Link'); |
| 65 | + await userEvent.type(queryInput, 'My Test SQL Query'); |
| 66 | + |
| 67 | + // will check if the input field has the expected value |
| 68 | + expect(queryNameInput).toHaveValue('My Test Query'); |
| 69 | + expect(dbLinkInput).toHaveValue('My Test DB Link'); |
| 70 | + expect(queryInput).toHaveValue('My Test SQL Query'); |
| 71 | + }); |
| 72 | + |
| 73 | + //? Testing (5): clicking Run Query button triggers Get Req |
| 74 | + it('checks if clicking Run Query button triggers req to the BE', async () => { |
| 75 | + // mocking the axios Get Req |
| 76 | + const mockGet = jest.spyOn(axios, 'get').mockResolvedValue({ |
| 77 | + data: ['Query: SELECT * FROM users', 'Date: 2025-04-11'], |
| 78 | + }); |
| 79 | + renderWithRouter(<TestNewQuery />); |
| 80 | + // will simulate user typing into input fields |
| 81 | + const queryNameInput: HTMLTextAreaElement = |
| 82 | + screen.getByPlaceholderText(/Name your query/i); |
| 83 | + const dbLinkInput: HTMLTextAreaElement = |
| 84 | + screen.getByPlaceholderText(/Enter DB link here/i); |
| 85 | + const queryInput: HTMLTextAreaElement = screen.getByPlaceholderText( |
| 86 | + /Write your SQL query here/i |
| 87 | + ); |
| 88 | + |
| 89 | + await userEvent.type(queryNameInput, 'My Test Query'); |
| 90 | + await userEvent.type(dbLinkInput, 'postgres://user:pass@host:5432/db'); |
| 91 | + await userEvent.type(queryInput, 'SELECT * FROM users'); |
| 92 | + |
| 93 | + // Run Query button |
| 94 | + const runQueryButton = screen.getByRole('button', { name: /Run Query/i }); |
| 95 | + |
| 96 | + // will simulate user clicking the Run Query button |
| 97 | + await userEvent.click(runQueryButton); |
| 98 | + // wait / confirm for the mocked axios Get Req to be called |
| 99 | + await waitFor(() => { |
| 100 | + expect(mockGet).toHaveBeenCalledTimes(1); |
| 101 | + }); |
| 102 | + // remove mock before next test |
| 103 | + mockGet.mockRestore(); |
| 104 | + }); |
| 105 | + |
| 106 | + //? Testing (6): clicking Save Query button triggers Post Req |
| 107 | + it('checks if clicking Save Query button triggers req to the BE', async () => { |
| 108 | + // mocking running a query so we have queryResult |
| 109 | + const getMockResponse: AxiosResponse<{ metrics: string[]; otherMetrics: any[] }> = { |
| 110 | + data: { |
| 111 | + metrics: [ |
| 112 | + 'Query Name: Testing Sunday', |
| 113 | + 'Query: SELECT * FROM people', |
| 114 | + 'Date Run: 2025-04-11', |
| 115 | + 'Execution Time: 0.042', |
| 116 | + ], |
| 117 | + otherMetrics: [ |
| 118 | + { |
| 119 | + planningTime: 1.932, |
| 120 | + actualTotalTime: 0.025, |
| 121 | + totalCost: 2.87, |
| 122 | + nodeType: 'Seq Scan', |
| 123 | + relationName: 'people', |
| 124 | + planRows: 87, |
| 125 | + actualRows: 87, |
| 126 | + sharedHit: 2, |
| 127 | + sharedRead: 0, |
| 128 | + }, |
| 129 | + ], |
| 130 | + }, |
| 131 | + status: 200, |
| 132 | + statusText: 'OK', |
| 133 | + headers: {}, |
| 134 | + config: {}, |
| 135 | + }; |
| 136 | + |
| 137 | + const postMockResponse: AxiosResponse<any> = { |
| 138 | + data: 'Query saved successfully!', |
| 139 | + status: 200, |
| 140 | + statusText: 'OK', |
| 141 | + headers: {}, |
| 142 | + config: {}, |
| 143 | + }; |
| 144 | + // mocking the axios Get Req w/ a mocked response |
| 145 | + const mockGet = jest.spyOn(axios, 'get').mockResolvedValue(getMockResponse); |
| 146 | + const mockPost = jest.spyOn(axios, 'post').mockResolvedValue(postMockResponse); |
| 147 | + |
| 148 | + renderWithRouter(<TestNewQuery />); |
| 149 | + |
| 150 | + // will simulate user typing into input fields |
| 151 | + const queryNameInput: HTMLTextAreaElement = |
| 152 | + screen.getByPlaceholderText(/Name your query/i); |
| 153 | + const dbLinkInput: HTMLTextAreaElement = |
| 154 | + screen.getByPlaceholderText(/Enter DB link here/i); |
| 155 | + const queryInput: HTMLTextAreaElement = screen.getByPlaceholderText( |
| 156 | + /Write your SQL query here/i |
| 157 | + ); |
| 158 | + // will simulate user's inputed query data |
| 159 | + await userEvent.type(queryNameInput, 'My Test Query'); |
| 160 | + await userEvent.type(dbLinkInput, 'postgres://user:pass@host:5432/db'); |
| 161 | + await userEvent.type(queryInput, 'SELECT * FROM users'); |
| 162 | + |
| 163 | + // will simulate user clicking the Run Query button |
| 164 | + const runQueryButton = screen.getByRole('button', { name: /Run Query/i }); |
| 165 | + await userEvent.click(runQueryButton); |
| 166 | + |
| 167 | + // will wait for mocked axios Get Req to be called |
| 168 | + await waitFor(() => { |
| 169 | + expect(mockGet).toHaveBeenCalledTimes(1); |
| 170 | + }); |
| 171 | + |
| 172 | + // will simulate user clicking the Save Query button |
| 173 | + const saveQueryButton = screen.getByRole('button', { name: /Save Query/i }); |
| 174 | + await userEvent.click(saveQueryButton); |
| 175 | + |
| 176 | + // will wait for mocked axios Post Req to be called |
| 177 | + await waitFor(() => { |
| 178 | + expect(mockPost).toHaveBeenCalledTimes(1); |
| 179 | + }); |
| 180 | + // remove mocks before next test |
| 181 | + mockPost.mockRestore(); |
| 182 | + mockGet.mockRestore(); |
| 183 | + }); |
| 184 | + |
| 185 | + //? Testing (7): clicking Run Query button renders a table w/ results |
| 186 | + it('checks if clicking Run Query button renders a table with results', async (): Promise<void> => { |
| 187 | + // mocking the axios Get Req w/ a mocked response |
| 188 | + // using TS to define the response |
| 189 | + const mockData = { |
| 190 | + metrics: [ |
| 191 | + 'Query Name: My Test Query', |
| 192 | + 'Query: SELECT * FROM users', |
| 193 | + 'Date Run: 2025-04-11', |
| 194 | + 'Execution Time: 0.042', |
| 195 | + ], |
| 196 | + otherMetrics: [ |
| 197 | + { |
| 198 | + planningTime: 1.932, |
| 199 | + actualTotalTime: 0.025, |
| 200 | + totalCost: 2.87, |
| 201 | + nodeType: 'Seq Scan', |
| 202 | + relationName: 'people', |
| 203 | + planRows: 87, |
| 204 | + actualRows: 87, |
| 205 | + sharedHit: 2, |
| 206 | + sharedRead: 0, |
| 207 | + }, |
| 208 | + ], |
| 209 | + }; |
| 210 | + // using TS to define the response |
| 211 | + const mockResponse: AxiosResponse<{ metrics: string[]; otherMetrics: any[] }> = { |
| 212 | + data: mockData, |
| 213 | + status: 200, |
| 214 | + statusText: 'OK', |
| 215 | + headers: {}, |
| 216 | + config: {}, |
| 217 | + }; |
| 218 | + // mocking the axios Get Req w/ a mocked response |
| 219 | + const mockGet = jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); |
| 220 | + |
| 221 | + renderWithRouter(<TestNewQuery />); |
| 222 | + |
| 223 | + // input fields |
| 224 | + const queryNameInput: HTMLTextAreaElement = |
| 225 | + screen.getByPlaceholderText(/Name your query/i); |
| 226 | + const dbLinkInput: HTMLTextAreaElement = |
| 227 | + screen.getByPlaceholderText(/Enter DB link here/i); |
| 228 | + const queryInput: HTMLTextAreaElement = screen.getByPlaceholderText( |
| 229 | + /Write your SQL query here/i |
| 230 | + ); |
| 231 | + |
| 232 | + // will simulate user typing into input fields |
| 233 | + await userEvent.type(queryNameInput, 'My Test Query'); |
| 234 | + await userEvent.type(dbLinkInput, 'postgres://user:pass@host:5432/db'); |
| 235 | + await userEvent.type(queryInput, 'SELECT * FROM users'); |
| 236 | + |
| 237 | + // simulate user clicking Run Query button |
| 238 | + const runQueryButton: HTMLButtonElement = screen.getByRole('button', { |
| 239 | + name: /Run Query/i, |
| 240 | + }); |
| 241 | + |
| 242 | + await userEvent.click(runQueryButton); |
| 243 | + screen.debug(); |
| 244 | + |
| 245 | + // will wait for QUery Results heading to render |
| 246 | + await waitFor(() => { |
| 247 | + expect(screen.getByText(/Query Results/i)).toBeInTheDocument(); |
| 248 | + }); |
| 249 | + |
| 250 | + // will check that all parts of our mocked results render |
| 251 | + expect(screen.getByText(/My Test Query/i)).toBeInTheDocument(); |
| 252 | + expect(screen.getByText(/SELECT \* FROM users/i)).toBeInTheDocument(); |
| 253 | + expect(screen.getByText(/2025-04-11/i)).toBeInTheDocument(); |
| 254 | + expect(screen.getByText(/0.042/i)).toBeInTheDocument(); |
| 255 | + |
| 256 | + // remove mock before next test |
| 257 | + mockGet.mockRestore(); |
| 258 | + }); |
| 259 | + |
| 260 | + //? Testing (8): clicking Run Query button renders error message |
| 261 | + // mock the axios Get Req to fail |
| 262 | + // confirm error message renders (error running query, check db link and query) |
| 263 | + |
| 264 | + //? Testing (9): clicking Run Query button clears the input fields |
| 265 | + // mock running a query |
| 266 | + // confirm input fields are cleared |
| 267 | + |
| 268 | + //? Testing (9): clicking Save Query button renders 'Query Saved!' |
| 269 | + // mock saving a query |
| 270 | + // confirm 'Query Saved!' renders |
| 271 | + // currently THIS is failing. Even if the req to save the query fails, the 'Query Saved!' message still renders |
| 272 | +}); |
0 commit comments