Skip to content

Commit

Permalink
test: add integration tests for cancel functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
hoorayimhelping committed Jun 15, 2020
1 parent c0c739c commit 649297a
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 45 deletions.
2 changes: 1 addition & 1 deletion ui/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = {
'ts-jest': {
tsConfig: 'tsconfig.test.json',
diagnostics: {
ignoreCodes: [6133, 6192], // ignore unused variable errors
ignoreCodes: [6133, 6192] // ignore unused variable errors
},
},
},
Expand Down
20 changes: 19 additions & 1 deletion ui/jestSetup.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import {cleanup} from '@testing-library/react'
import 'intersection-observer'
import MutationObserver from 'mutation-observer'
import fetchMock from 'jest-fetch-mock'

// global vars
process.env.API_PREFIX = 'http://example.com/'

declare global {
interface Window {
flushAllPromises: () => Promise<any>
MutationObserver: MutationObserver
}
}

// Adds MutationObserver as a polyfill for testing
window.MutationObserver = MutationObserver

window.flushAllPromises = async () => {
return new Promise(resolve => setImmediate(resolve))
}

// mocks and stuff
fetchMock.enableMocks()
jest.mock('honeybadger-js', () => () => null)

process.env.API_PREFIX = '/'
// cleans up state between @testing-library/react tests
afterEach(() => {
cleanup()
fetchMock.resetMocks()
})
4 changes: 1 addition & 3 deletions ui/src/shared/apis/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ export const runQuery = (
}
}

export const processResponse = async (
response: Response
): Promise<RunQueryResult> => {
const processResponse = async (response: Response): Promise<RunQueryResult> => {
switch (response.status) {
case 200:
return processSuccessResponse(response)
Expand Down
3 changes: 2 additions & 1 deletion ui/src/timeMachine/actions/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,9 @@ export const executeQueries = (abortController?: AbortController) => async (
dispatch(
setQueryResults(RemoteDataState.Done, files, duration, null, statuses)
)
return results
} catch (error) {
if (error.name === 'CancellationError') {
if (error.name === 'CancellationError' || error.name === 'AbortError') {
dispatch(setQueryResults(RemoteDataState.Done, null, null))
return
}
Expand Down
203 changes: 172 additions & 31 deletions ui/src/timeMachine/components/SubmitQueryButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,71 @@
// Libraries
import React from 'react'
import {mocked} from 'ts-jest/utils'
import {fireEvent} from '@testing-library/react'

// Components
import SubmitQueryButton from 'src/timeMachine/components/SubmitQueryButton'

// Utils
import {renderWithRedux} from 'src/mockState'
import {fireEvent, waitFor} from '@testing-library/react'

// Types
import {RemoteDataState} from 'src/types'

declare global {
interface Window {
TextDecoder: any
}
}

class FakeTextDecoder {
decode() {
return ''
}
}

window.TextDecoder = FakeTextDecoder

jest.mock('src/external/parser', () => {
return {
parse: jest.fn(() => {
return {
type: 'File',
package: {
name: {
name: 'fake',
type: 'Identifier',
},
type: 'PackageClause',
},
imports: [],
body: [],
}
}),
}
})

jest.mock('src/variables/actions/thunks', () => {
return {
hydrateVariables: jest.fn(() => {
return (_dispatch, _getState) => {
return Promise.resolve()
}
}),
}
})

import SubmitQueryButton from 'src/timeMachine/components/SubmitQueryButton'

const stateOverride = {
timeMachines: {
activeTimeMachineID: 'veo',
timeMachines: {
veo: {
draftQueries: [{text: 'this is a draft query'}],
draftQueries: [
{
text: `from(bucket: "apps")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "rum")
|> filter(fn: (r) => r["_field"] == "domInteractive")
|> map(fn: (r) => ({r with _value: r._value / 1000.0}))
|> group()`,
},
],
activeQueryIndex: 0,
queryResults: {
status: RemoteDataState.NotStarted,
Expand All @@ -32,30 +81,122 @@ const stateOverride = {
}

describe('TimeMachine.Components.SubmitQueryButton', () => {
describe('if button is clicked', () => {
it('disables the submit button when no query is present', () => {
const {getByTitle} = renderWithRedux(<SubmitQueryButton />)

const SubmitBtn = getByTitle('Submit')
fireEvent.click(SubmitBtn)
// expect the button to still be on submit
expect(getByTitle('Submit')).toBeTruthy()
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.useRealTimers()
})

it('it changes the Submit button to Cancel when the request is in flight, then back to Submit after the request has resolved', async () => {
const fakeReader = {
cancel: jest.fn(),
read: jest.fn(() => {
return Promise.resolve({
done: true,
})
}),
}

const fakeResponse = {
status: 200,
body: {
getReader: () => fakeReader,
},
}

const expectedMockedFetchCall = {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Accept-Encoding': 'gzip'},
body: JSON.stringify({
query: stateOverride.timeMachines.timeMachines.veo.draftQueries[0].text,
extern: {
type: 'File',
package: null,
imports: null,
body: [
{
type: 'OptionStatement',
assignment: {
type: 'VariableAssignment',
id: {type: 'Identifier', name: 'v'},
init: {
type: 'ObjectExpression',
properties: [
{
type: 'Property',
key: {type: 'Identifier', name: 'timeRangeStart'},
value: {
type: 'UnaryExpression',
operator: '-',
argument: {
type: 'DurationLiteral',
values: [{magnitude: 1, unit: 'h'}],
},
},
},
{
type: 'Property',
key: {type: 'Identifier', name: 'timeRangeStop'},
value: {
type: 'CallExpression',
callee: {type: 'Identifier', name: 'now'},
},
},
],
},
},
},
],
},
dialect: {annotations: ['group', 'datatype', 'default']},
}),
signal: new AbortController().signal,
}

mocked(fetch).mockImplementation(() => {
return Promise.resolve(fakeResponse)
})
it('allows the query to be cancelled after submission', async () => {
const {getByTitle} = renderWithRedux(<SubmitQueryButton />, s => ({
...s,
...stateOverride,
}))

const SubmitBtn = getByTitle('Submit')
fireEvent.click(SubmitBtn)

const CancelBtn = getByTitle('Cancel')
// expect the button to toggle to Cancel
expect(CancelBtn).toBeTruthy()
fireEvent.click(CancelBtn)
// aborts the query and returns the query to submit mode
expect(await waitFor(() => getByTitle('Submit'))).toBeTruthy()
const {getByTitle} = renderWithRedux(<SubmitQueryButton />, s => ({
...s,
...stateOverride,
}))

fireEvent.click(getByTitle('Submit'))
expect(getByTitle('Cancel')).toBeTruthy()
await window.flushAllPromises()

expect(mocked(fetch)).toHaveBeenCalledWith(
'http://example.com/api/v2/query?orgID=orgid',
expectedMockedFetchCall
)
expect(getByTitle('Submit')).toBeTruthy()
})

it("cancels the query after submission if the query hasn't finished and resolved", async () => {
mocked(fetchMock).mockResponse(() => {
return new Promise((resolve, _reject) => {
setTimeout(() => {
resolve('')
}, 3000)
})
})

const {getByTitle} = renderWithRedux(<SubmitQueryButton />, s => ({
...s,
...stateOverride,
}))
const SubmitBtn = getByTitle('Submit')
fireEvent.click(SubmitBtn)

const CancelBtn = getByTitle('Cancel')
fireEvent.click(CancelBtn)
await window.flushAllPromises()

const {type, value: error} = mocked(fetch).mock.results[0] as any
expect(type).toBe('throw')
expect(error.name).toBe('AbortError')

expect(getByTitle('Submit')).toBeTruthy()
})
})
16 changes: 8 additions & 8 deletions ui/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9358,14 +9358,14 @@ react-dnd@^2.6.0:
prop-types "^15.5.10"

react-dom@^16.8.2:
version "16.8.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.2.tgz#7c8a69545dd554d45d66442230ba04a6a0a3c3d3"
integrity sha512-cPGfgFfwi+VCZjk73buu14pYkYBR1b/SRMSYqkLDdhSEHnSwcuYTPu6/Bh6ZphJFIk80XLvbSe2azfcRzNF+Xg==
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.13.2"
scheduler "^0.19.1"

react-draggable@3.x, "react-draggable@^2.2.6 || ^3.0.3":
version "3.0.5"
Expand Down Expand Up @@ -10123,10 +10123,10 @@ schedule@^0.5.0:
dependencies:
object-assign "^4.1.1"

scheduler@^0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.2.tgz#969eaee2764a51d2e97b20a60963b2546beff8fa"
integrity sha512-qK5P8tHS7vdEMCW5IPyt8v9MJOHqTrOUgPXib7tqm9vh834ibBX5BNhwkplX/0iOzHW5sXyluehYfS9yrkz9+w==
scheduler@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
Expand Down

0 comments on commit 649297a

Please sign in to comment.