Skip to content
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

chore(influxql): add e2e tests #6739

Merged
merged 12 commits into from
Jun 27, 2023
321 changes: 321 additions & 0 deletions cypress/e2e/cloud/scriptQueryBuilder.influxql.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
import {Organization} from '../../../src/types'

const DEFAULT_INFLUXQL_EDITOR_TEXT = '/* Start by typing InfluxQL here */'

const DELAY_FOR_LAZY_LOAD_EDITOR = 30000
const DELAY_FOR_FILE_DOWNLOAD = 5000
const NUMBER_OF_ROWS = 5 // see `generateWriteData` for why this number

describe('Script Builder', () => {
const bucketName = 'bucket-influxql'
const databaseName = 'database-name'
const retentionPolicyName = 'retention-policy-name'
const measurement = 'ndbc'
const fieldName = 'air_degrees'
const fieldName2 = 'humidity'
const tagKey = 'air_station_id'
const tagValue = 'ST01'
const tagValue2 = 'ST02'
let route: string

const selectScriptDBRP = (dbName: string, rpName: string) => {
const dbrpName: string = `${dbName}/${rpName}`
cy.getByTestID('dbrp-selector--dropdown-button').click()
cy.getByTestID(`dbrp-selector--dropdown--${dbrpName}`).click()
cy.getByTestID('dbrp-selector--dropdown-button').should(
'contain',
`${dbrpName}`
)
}

const selectSchema = () => {
cy.log('select database/retention policy')
selectScriptDBRP(databaseName, retentionPolicyName)
cy.confirmSyncIsOn() // influxql composition is dumb. On bucket selection, it will occasionally drop the sync.
cy.log('writes empty query statement with only the timerange')
cy.getByTestID('influxql-editor', {
timeout: DELAY_FOR_LAZY_LOAD_EDITOR,
}).contains(`SELECT *`)
cy.getByTestID('influxql-editor').contains(`WHERE`)
cy.getByTestID('influxql-editor').contains(`time >= now() - 1h`)
cy.confirmSyncIsOn() // influxql sync sometimes toggles off

cy.log('select measurement')
cy.selectScriptMeasurement(measurement)
}

const confirmSchemaComposition = () => {
cy.log('has basic query')
cy.getByTestID('influxql-editor', {
timeout: DELAY_FOR_LAZY_LOAD_EDITOR,
}).contains(`SELECT *`)
cy.getByTestID('influxql-editor').contains(`WHERE`)
cy.getByTestID('influxql-editor').contains(`time >= now() - 1h`)

cy.log('has measurement chosen as a table')
cy.getByTestID('influxql-editor').contains(
`FROM "${databaseName}"."${retentionPolicyName}"."${measurement}"`
)
cy.getByTestID('influxql-editor').within(() => {
cy.log('have four lines of query')
cy.get('.composition-sync--on').should('have.length', 4)
})

cy.log('does not have other fields or tag filters')
cy.getByTestID('influxql-editor').should('not.contain', 'AND')
}

const typeInQuery = () => {
cy.log('type in a query')
cy.getByTestID('influxql-editor').monacoType(
`{selectall}{del}SELECT * FROM "${databaseName}"."${retentionPolicyName}"."${measurement}"`
)
cy.getByTestID('influxql-editor').contains(
`SELECT * FROM "${databaseName}"."${retentionPolicyName}"."${measurement}"`
)
}

before(() => {
const generateWriteData = (value: number) => {
// this will generate a table of 5 rows in csv format
// 1 row of table header + 4 rows of data
return [
`${measurement},${tagKey}=${tagValue} ${fieldName}=${value}`,
`${measurement},${tagKey}=${tagValue} ${fieldName2}=${value}`,
`${measurement},${tagKey}=${tagValue2} ${fieldName}=${value}`,
`${measurement},${tagKey}=${tagValue2} ${fieldName2}=${value}`,
]
}

cy.flush().then(() => {
return cy.signin().then(() => {
return cy.get('@org').then(({id, name}: Organization) => {
route = `/orgs/${id}/data-explorer`

cy.log('add mock data')
cy.createBucket(id, name, bucketName).should(response => {
expect(response.body).to.have.property('id')
const bucketID: string = response.body['id']
cy.createDBRP(
bucketID,
databaseName,
retentionPolicyName,
id
).should(response => {
expect(response.status).to.eq(201)
cy.log('a DBRP mapping is created')
})
})

cy.log('create time series, with change of value over time')
cy.writeData(generateWriteData(100), bucketName)
cy.wait(2000)
cy.writeData(generateWriteData(20), bucketName)
})
})
})
})

beforeEach(() => {
cy.scriptsLoginWithFlags({
influxqlUI: true,
}).then(() => {
cy.clearInfluxQLScriptSession()
cy.getByTestID('editor-sync--toggle')
cy.getByTestID('influxql-editor', {timeout: DELAY_FOR_LAZY_LOAD_EDITOR})
})
})

describe('Schema Composition', () => {
it('can construct a composition with fields and tagValues', () => {
cy.log('start with default text')
cy.getByTestID('influxql-editor').within(() => {
cy.get('textarea.inputarea').should(
'have.value',
DEFAULT_INFLUXQL_EDITOR_TEXT
)
})

cy.log(
'disable run button before selecting a database/retention policy mapping'
)
cy.getByTestID('time-machine-submit-button').should('be.disabled')

cy.log('select database/retention policy')
selectScriptDBRP(databaseName, retentionPolicyName)

cy.log('the default text should be gone')
cy.getByTestID('influxql-editor').should(
'not.contain',
DEFAULT_INFLUXQL_EDITOR_TEXT
)

cy.log(
'enable run button after selecting a database and retention policy mapping'
)
cy.getByTestID('time-machine-submit-button').should('not.be.disabled')

cy.log('select measurement')
cy.selectScriptMeasurement(measurement)

confirmSchemaComposition()

cy.log('select field --> add to composition')
cy.selectScriptFieldOrTag(fieldName, true)
cy.getByTestID('influxql-editor').contains(`SELECT "${fieldName}"`)
cy.selectScriptFieldOrTag(fieldName2, true)
cy.getByTestID('influxql-editor').contains(
`SELECT "${fieldName}", "${fieldName2}"`
)

cy.log('select field --> remove from composition')
cy.selectScriptFieldOrTag(fieldName2, false)
cy.getByTestID('influxql-editor').contains(`SELECT "${fieldName}"`)
cy.getByTestID('influxql-editor').should('not.contain', fieldName2)

cy.log('select tagValue --> add to composition')
cy.getByTestID('container-side-bar--tag-keys').within(() => {
cy.getByTestID('accordion-header').should('be.visible').click()
})
cy.selectScriptFieldOrTag(tagValue, true)
cy.getByTestID('influxql-editor').contains(
`("${tagKey}" = '${tagValue}')`
)

cy.log('select tagValue --> remove from composition')
cy.selectScriptFieldOrTag(tagValue, false)
cy.getByTestID('influxql-editor').should('not.contain', tagKey)
})

it('composition sync functionality', () => {
cy.log('default to be on')
cy.getByTestID('editor-sync--toggle').should('have.class', 'active')

cy.log('make a composition')
selectSchema()
confirmSchemaComposition()

cy.log('sync toggles on, with matching styles')
cy.get('.composition-sync--on').should('have.length', 4)
cy.get('.composition-sync--off').should('have.length', 0)

cy.log('sync toggles off, with matching styles')
cy.getByTestID('editor-sync--toggle')
.should('have.class', 'active')
.click()
.should('not.have.class', 'active')
cy.get('.composition-sync--on').should('have.length', 0)
cy.get('.composition-sync--off').should('have.length', 4)

cy.log('can still browse schema while not synced, with matching styles')
selectScriptDBRP(databaseName, retentionPolicyName)
cy.selectScriptMeasurement(measurement)
cy.get('.composition-sync--on').should('have.length', 0)
cy.get('.composition-sync--off').should('have.length', 4)

cy.log('sync toggles on')
cy.getByTestID('editor-sync--toggle')
.click()
.should('have.class', 'active')
cy.get('.composition-sync--on').should('have.length', 4)
cy.get('.composition-sync--off').should('have.length', 0)
})
})

describe('Other Core Features', () => {
const CSV_PARSING: number = 2000

it('Run query', () => {
cy.getByTestID('time-machine-submit-button')
.should('be.visible')
.should('be.disabled')

selectScriptDBRP(databaseName, retentionPolicyName)
typeInQuery()

cy.log('can execute the query')
cy.getByTestID('time-machine-submit-button')
.should('be.visible')
.should('not.have.class', 'cf-button--disabled')
cy.getByTestID('time-machine-submit-button').click()

cy.log('result view shows table')
cy.getByTestID('data-explorer-results--view').should('be.visible')
cy.getByTestID('data-explorer-results--view', {
timeout: CSV_PARSING,
}).contains(tagKey)

cy.log('should not have graph tab')
cy.getByTestID('data-explorer-results--graph-view').should('not.exist')
})

it('Save/Load as an InfluxQL Script', () => {
// The save/load functionality works the same for all the languages
// (i.e. Flux, SQL, InfluxQL) at the backend, and the file
// `scriptQueryBuilder.scriptsCrud.test.ts` has already include a
// full coverage in general, so we are just doing a simple test
// for InfluxQL save/load support here
const scriptName: string = 'InfluxQL script'
cy.intercept('POST', '/api/v2/scripts*').as('scripts')

cy.log('save an InfluxQL query')
typeInQuery()
cy.getByTestID('script-query-builder--save-script')
.should('be.visible')
.click()
cy.getByTestID('overlay--container').within(() => {
cy.getByTestID('save-script-name__input')
.should('be.visible')
.type(scriptName)
cy.getByTestID('script-query-builder--save')
.should('be.visible')
.click()
})

cy.log('check the script is saved successfully')
cy.wait('@scripts')
cy.getByTestID('notification-success')
.should('be.visible')
.contains(scriptName)
})

it('Download CSV', () => {
// The csv download functionality works the same for all the languages
// (i.e. Flux, SQL, InfluxQL), and the file `scriptQueryBuilder.result.test.ts`
// has already include a full coverage in general, so we are just doing a
// simple test for InfluxQL csv download here
cy.intercept('POST', '/query?*', req => {
req.redirect(route)
}).as('queryDownloadCSV')

cy.getByTestID('csv-download-button')
.should('be.visible')
.should('be.disabled')

selectScriptDBRP(databaseName, retentionPolicyName)
typeInQuery()

cy.log('will download complete csv data')
cy.getByTestID('csv-download-button').should('not.be.disabled').click()
cy.wait('@queryDownloadCSV', {timeout: DELAY_FOR_FILE_DOWNLOAD})
.its('request', {timeout: DELAY_FOR_FILE_DOWNLOAD})
.then(req => {
cy.request(req)
.then(({body, headers}) => {
expect(headers).to.have.property(
'content-type',
'text/csv; charset=utf-8'
)
return Promise.resolve(body)
})
.then((csv: string) => {
cy.wrap(csv)
.then(doc => doc.trim().split('\n'))
.then((list: string[]) => {
expect(list.length).eq(NUMBER_OF_ROWS)
})
})
})
})
})
})
6 changes: 6 additions & 0 deletions cypress/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
createAndAddLabel,
createLabel,
createBucket,
createDBRP,
createScraper,
createView,
createNotebook,
Expand All @@ -56,9 +57,11 @@ import {
createTaskFromEmpty,
createAlertGroup,
switchToDataExplorer,
setScriptToInfluxQL,
setScriptToFlux,
setScriptToSql,
confirmSyncIsOn,
clearInfluxQLScriptSession,
clearFluxScriptSession,
clearSqlScriptSession,
selectScriptBucket,
Expand Down Expand Up @@ -109,6 +112,7 @@ declare global {
createAndAddLabel: typeof createAndAddLabel
createLabel: typeof createLabel
createBucket: typeof createBucket
createDBRP: typeof createDBRP
createScraper: typeof createScraper
fluxEqual: typeof fluxEqual
createTelegraf: typeof createTelegraf
Expand All @@ -129,9 +133,11 @@ declare global {
quartzProvision: typeof quartzProvision
createTaskFromEmpty: typeof createTaskFromEmpty
switchToDataExplorer: typeof switchToDataExplorer
setScriptToInfluxQL: typeof setScriptToInfluxQL
setScriptToFlux: typeof setScriptToFlux
setScriptToSql: typeof setScriptToSql
confirmSyncIsOn: typeof confirmSyncIsOn
clearInfluxQLScriptSession: typeof clearInfluxQLScriptSession
clearFluxScriptSession: typeof clearFluxScriptSession
clearSqlScriptSession: typeof clearSqlScriptSession
selectScriptBucket: typeof selectScriptBucket
Expand Down
Loading