diff --git a/ui/cypress.json b/ui/cypress.json index 0e7bfd79e..6dcce38de 100644 --- a/ui/cypress.json +++ b/ui/cypress.json @@ -4,10 +4,11 @@ "video": false, "env": { "ALLOW_SCREENSHOT": true, - "url": "https://localhost:8086", + "influxDBURL": "https://localhost:8086", "username": "admin", "password": "admin", "connectionName": "E1M1", - "insecureSkipVerify": true + "insecureSkipVerify": true, + "oauth2ServerURL": "http://localhost:8087" } } diff --git a/ui/cypress/index.d.ts b/ui/cypress/index.d.ts index ca8f582a8..421d74b8c 100644 --- a/ui/cypress/index.d.ts +++ b/ui/cypress/index.d.ts @@ -1,22 +1,35 @@ -/* eslint @typescript-eslint/no-unused-vars: "off" */ import 'jest' import { getByTestID, - cutConnections, + removeConnections, createConnection, createDashboard, deleteDashboards, + createDashboardWithCell, + OAuthLogin, + OAuthLogout, + createUser, + deleteUser, + createOrg, + deleteOrg } from './support/commands' declare global { namespace Cypress { interface Chainable { getByTestID: typeof getByTestID - cutConnections: typeof cutConnections + removeConnections: typeof removeConnections createConnection: typeof createConnection createDashboard: typeof createDashboard deleteDashboards: typeof deleteDashboards + createDashboardWithCell: typeof createDashboardWithCell + OAuthLogin: typeof OAuthLogin + OAuthLogout: typeof OAuthLogout + createUser: typeof createUser + deleteUser: typeof deleteUser + createOrg: typeof createOrg + deleteOrg: typeof deleteOrg } } } diff --git a/ui/cypress/integration/dashboard.test.ts b/ui/cypress/integration/dashboard.test.ts index 301dbd5a6..a6f5c0762 100644 --- a/ui/cypress/integration/dashboard.test.ts +++ b/ui/cypress/integration/dashboard.test.ts @@ -1,7 +1,8 @@ -describe('dashboards', () => { +describe('Use Dashboards', () => { beforeEach(() => { + cy.OAuthLogin('test') cy.deleteDashboards() - cy.cutConnections() + cy.removeConnections() cy.createConnection() cy.get('@connections').then(connections => { cy.fixture('routes').then(({dashboards}) => { @@ -38,4 +39,41 @@ describe('dashboards', () => { }) .should('not.exist') }) + + describe('Use Dashboards as reader', () => { + let query = `SELECT mean("pointsWritten") AS "mean_pointsWritten" + FROM "_internal"."monitor"."localStore" + WHERE time > :dashboardTime: AND time < :upperDashboardTime: + GROUP BY time(:interval:) FILL(null)` + + beforeEach(() => { + cy.OAuthLogin('test') + cy.deleteUser('Reader') + cy.deleteDashboards() + cy.removeConnections() + cy.createConnection() + cy.createDashboardWithCell(query) + cy.createUser('Reader', 'oauth-mock', 'oauth2') + cy.OAuthLogout() + cy.OAuthLogin('Reader') + cy.visit('/') + }) + + it('use dashboards as user with reader role', () => { + cy.getByTestID('sidebar').should('not.exist') + cy.getByTestID('import-dashboard--button').should('not.exist') + cy.getByTestID('create-dashboard-button').should('not.exist') + cy.getByTestID('dashboard-filter--input').type('Empty') + cy.getByTestID('dashboard-panel').should('have.text', `Looks like you don’t have any dashboards`) + cy.getByTestID('dashboard-filter--input').clear().type('Dashboard') + cy.getByTestID('Unnamed Dashboard').click() + cy.get('.dashboard-empty--menu').should('not.exist') + cy.getByTestID('add-cell').should('not.exist') + cy.getByTestID('show-variables--button').click() + cy.getByTestID('add-template-variable').should('not.exist') + cy.getByTestID('show-annotations--button').click() + cy.getByTestID('add-annotation--button').should('not.exist') + cy.getByTestID('add-annotation-filter--button').should('not.exist') + }) + }) }) \ No newline at end of file diff --git a/ui/cypress/integration/queryBuilder.test.ts b/ui/cypress/integration/queryBuilder.test.ts index 3754c0891..8219b03ff 100644 --- a/ui/cypress/integration/queryBuilder.test.ts +++ b/ui/cypress/integration/queryBuilder.test.ts @@ -1,10 +1,10 @@ describe('query builder', () => { beforeEach(() => { + cy.OAuthLogin('test') cy.deleteDashboards() - cy.cutConnections() + cy.removeConnections() cy.createConnection() cy.createDashboard() - cy.get('@connections').then(connections => { cy.get('@dashboards').then(dashboards => { cy.fixture('routes').then(routes => { diff --git a/ui/cypress/integration/welcome.test.ts b/ui/cypress/integration/welcome.test.ts index b2f76cdfa..96e84650c 100644 --- a/ui/cypress/integration/welcome.test.ts +++ b/ui/cypress/integration/welcome.test.ts @@ -1,6 +1,7 @@ describe('Welcome Page', () => { beforeEach(() => { - cy.cutConnections() + cy.OAuthLogin('test') + cy.removeConnections() cy.visit('/') }) diff --git a/ui/cypress/support/commands.ts b/ui/cypress/support/commands.ts index 042504fc2..2bbe82817 100644 --- a/ui/cypress/support/commands.ts +++ b/ui/cypress/support/commands.ts @@ -1,4 +1,4 @@ -import {addMatchImageSnapshotCommand} from 'cypress-image-snapshot/command' +import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command' declare namespace Cypress { interface Chainable { @@ -15,7 +15,7 @@ addMatchImageSnapshotCommand({ customSnapshotsDir: '../ui/cypress/snapshots', failureThreshold: 0.75, // threshold for entire image failureThresholdType: 'percent', // percent of image or number of pixels - customDiffConfig: {threshold: 0.75}, // threshold for each pixel + customDiffConfig: { threshold: 0.75 }, // threshold for each pixel capture: 'viewport', // capture viewport in screenshot }) @@ -35,18 +35,43 @@ export const getByTestID = ( options?: Partial< Cypress.Loggable & Cypress.Timeoutable & Cypress.Withinable & Cypress.Shadow > -): Cypress.Chainable => { +): globalThis.Cypress.Chainable> => { return cy.get(`[data-test="${dataTest}"]`, options) } +// Function sends HTTP POST request to OAuth2 Mock server in order to change user information +const changeUserInfo: Function = (name: string): void => { + const xhttp: XMLHttpRequest = new XMLHttpRequest() + const url: string = Cypress.env('oauth2ServerURL') + '/config' + const body = { + "userinfo": { + "name": name, + "email": name + "@oauth2.mock" + } + } + + xhttp.open('POST', url) + xhttp.setRequestHeader('Content-Type', 'application/json') + xhttp.send(JSON.stringify(body)) +} + +export const OAuthLogin = (name: string) => { + changeUserInfo(name) + return cy.visit('/oauth/oauth-mock/login') +} + +export const OAuthLogout = () => { + return cy.visit('/oauth/oauth-mock/logout') +} + // Change enviromental values in cypress.json -export const createConnection = (url: string = Cypress.env('url')) => { +export const createConnection = (url?: string) => { return cy .request({ method: 'POST', url: '/chronograf/v1/sources', body: { - url: url, + url: url ?? Cypress.env('influxDBURL'), username: Cypress.env('username'), password: Cypress.env('password'), name: Cypress.env('connectionName'), @@ -58,7 +83,7 @@ export const createConnection = (url: string = Cypress.env('url')) => { }) } -export const cutConnections = () => { +export const removeConnections = () => { return cy.request('GET', '/chronograf/v1/sources').then(response => { response.body.sources.forEach(connection => { cy.request('DELETE', `${connection.links.self}`) @@ -66,14 +91,15 @@ export const cutConnections = () => { }) } -export const createDashboard = (name: string = 'Default Dashboard') => { - cy.fixture('routes').then(({dashboards}) => { +export const createDashboard = (name?: string) => { + return cy + .fixture('routes').then(({ dashboards }) => { return cy .request({ method: 'POST', url: `/chronograf/v1${dashboards}`, body: { - name: name, + name: name ?? 'Default Dashboard', }, }) .then(() => { @@ -85,34 +111,137 @@ export const createDashboard = (name: string = 'Default Dashboard') => { export const deleteDashboards = () => { return cy .request('GET', '/chronograf/v1/dashboards') - .then(({body: responseBody}) => { - responseBody.dashboards.forEach(dashboard => { + .then(({ body: response }) => { + response.dashboards.forEach(dashboard => { cy.request('DELETE', `${dashboard.links.self}`) }) }) + .then(() => { + wrapDashboards() + }) +} + +export const createDashboardWithCell = ( + query?: string, + dashboardName?: string, + cellName?: string, + xPosition?: number, + yPosition?: number, + cellWidth?: number, + cellHeight?: number, +) => { + return cy + .request({ + method: 'POST', + url: `/chronograf/v1/dashboards/`, + body: { + "cells": [ + { + "x": xPosition ?? 0, + "y": yPosition ?? 0, + "w": cellWidth ?? 8, + "h": cellHeight ?? 4, + "name": cellName ?? "Unnamed Cell", + "queries": [ + { + "query": query, + "db": Cypress.env('connectionName'), + "label": "%", + } + ] + } + ], + name: dashboardName ?? "Unnamed Dashboard" + } + }) +} + +export const createUser = ( + userName: string, + provider: string, + scheme: string, + organization?: string, + role?: string +) => { + return cy.request({ + method: 'POST', + url: '/chronograf/v1/users', + body: { + "name": userName + "@oauth2.mock", + "provider": provider, + "roles": [{ + "name": role ?? "reader", + "organization": organization ?? "default" + }], + "scheme": scheme, + } + }) +} + +export const deleteUser = (name: string) => { + const userName = name + '@oauth2.mock' + return cy + .request('GET', '/chronograf/v1/users') + .then(({ body: response }) => { + response.users.forEach(user => { + if (userName == user.name) { + cy.request('DELETE', user.links.self) + } + }) + }) } -function wrapConnections(): Cypress.Chainable { +export const createOrg = (orgName: string, defaultRole: string) => { + return cy.request({ + method: 'POST', + url: '/chronograf/v1/organizations', + body: { + "defaultRole": defaultRole, + "name": orgName + } + }) + .then(() => { + wrapOrgs() + }) +} + +export const deleteOrg = (id: string) => { + return cy.request('DELETE', `/chronograf/v1/organizations/${id}`) +} + +function wrapConnections() { return cy .request({ method: 'GET', url: '/chronograf/v1/sources', }) - .then(response => { - const connections = response.body.sources + .then(({ body: response }) => { + const connections = response.sources cy.wrap(connections).as('connections') }) } -function wrapDashboards(): Cypress.Chainable { - return cy.request('GET', '/chronograf/v1/dashboards').then(response => { - const dashboards = response.body.dashboards - cy.wrap(dashboards).as('dashboards') +function wrapDashboards() { + return cy.request('GET', '/chronograf/v1/dashboards').then(({ body: response }) => { + cy.wrap(response.dashboards).as('dashboards') + }) +} + +function wrapOrgs() { + return cy.request('GET', '/chronograf/v1/organizations').then(({ body: response }) => { + cy.wrap(response.organizations).as('orgs') }) } Cypress.Commands.add('getByTestID', getByTestID) Cypress.Commands.add('createConnection', createConnection) -Cypress.Commands.add('cutConnections', cutConnections) +Cypress.Commands.add('removeConnections', removeConnections) Cypress.Commands.add('createDashboard', createDashboard) Cypress.Commands.add('deleteDashboards', deleteDashboards) +Cypress.Commands.add('createDashboardWithCell', createDashboardWithCell) +Cypress.Commands.add('OAuthLogin', OAuthLogin) +Cypress.Commands.add('OAuthLogout', OAuthLogout) +Cypress.Commands.add('createUser', createUser) +Cypress.Commands.add('deleteUser', deleteUser) +Cypress.Commands.add('createOrg', createOrg) +Cypress.Commands.add('deleteOrg', deleteOrg) \ No newline at end of file