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

feat: embedded superset dashboards #3205

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f15afc2
feat: fetch superset base url with system settings (#3181)
HendrikThePendric Jan 14, 2025
4cfbc06
feat: create embedded superset dashboard (#3185)
HendrikThePendric Jan 20, 2025
7bce0c9
feat: show embedded superset dashboard in iframe (#3192)
HendrikThePendric Jan 21, 2025
5707556
feat: adjust dashboards bar for external dashboards (#3193)
HendrikThePendric Jan 23, 2025
1d413b2
test: add e2e tests for superset embedded dashboards feature (#3197)
HendrikThePendric Feb 3, 2025
ce6f4c4
chore: add unit tests and implement feedback (#3206)
HendrikThePendric Feb 6, 2025
5251bf3
chore: fix problem in en.pot file
HendrikThePendric Feb 10, 2025
92bbefe
chore: always use "continue" text in choose-dashboard-type-modal button
HendrikThePendric Feb 10, 2025
e9033e0
chore: update yarn.lock
HendrikThePendric Feb 11, 2025
8776e35
chore: clarify supersetGateway api with comments
HendrikThePendric Feb 11, 2025
6f1cf2f
chore: rename hasSupersetSupport to isSupersetSupported
HendrikThePendric Feb 11, 2025
66e4354
fix: correct position of conditional chain
HendrikThePendric Feb 11, 2025
bb90d1a
chore: use individual constants for field names
HendrikThePendric Feb 11, 2025
279e3e7
chore: adjust e2e test after changing button label
HendrikThePendric Feb 12, 2025
63421b2
chore: improve naming consistency for blocks
HendrikThePendric Feb 12, 2025
47bbb89
chore: solve sonarcube issue by removing commented code block
HendrikThePendric Feb 12, 2025
8ecfbea
chore: reword e2e test description to better reflect its limitations
HendrikThePendric Feb 12, 2025
b563143
chore(e2e): merge disabled buttons step into creation step
HendrikThePendric Feb 12, 2025
e9d85e8
fix: adjust external creation modal title
cooper-joe Feb 12, 2025
40e76e0
fix: adjust dashboard type radio components
cooper-joe Feb 12, 2025
ad8576b
fix: external creation code help text
cooper-joe Feb 12, 2025
a6ea0e8
chore: test showing and hiding the description in a single test step
HendrikThePendric Feb 12, 2025
f60980c
chore: show description before asserting the updated description text
HendrikThePendric Feb 12, 2025
35ae6a6
fix: restore last-updated-tag to original width
HendrikThePendric Feb 12, 2025
28c3da1
fix: labels in tests
cooper-joe Feb 12, 2025
f68dd18
Merge branch 'feat/embedded-superset-dashboards-DHIS2-17891' of https…
cooper-joe Feb 12, 2025
8d4b503
chore: regenerate pot file after textual changes
HendrikThePendric Feb 12, 2025
9ad1d5e
fix: add max height of 180px to description field
HendrikThePendric Feb 12, 2025
042ab10
fix: ensure Superset is capitalized in error message
HendrikThePendric Feb 12, 2025
a2c69b9
fix: add tooltip around external data tag as in ui specs
HendrikThePendric Feb 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ cypress.env.json
cypress/screenshots
cypress/videos
cypress/downloads
.aider*
1 change: 1 addition & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const { defineConfig } = require('cypress')
async function setupNodeEvents(on, config) {
await addCucumberPreprocessorPlugin(on, config)
chromeAllowXSiteCookies(on, config)
// excludeByVersionTags(on, config)

on(
'file:preprocessor',
Expand Down
213 changes: 213 additions & 0 deletions cypress/e2e/embedded_superset_dashboard.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { newButtonSel } from '../elements/viewDashboard.js'
import { EXTENDED_TIMEOUT } from '../support/utils.js'

const SUPERSET_BASE_URL = 'https://superset-test.dhis2.org'
const NAME = 'My new dashboard'
const NAME_UPDATED = 'My updated dashboard'
const CODE = 'MY_CODE'
const DESCRIPTION = 'My dashboard description text'
const DESCRIPTION_UPATED = 'My updated dashboard description'
const UUID = '2e5ae28f-60d1-4fb9-a609-5bd4586bf4ac'
const UUID_UPDATED = '418b4581-3c2a-43a9-8561-9725eadcaffd'
const SUPERSET_DASHBOARD_STUB = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Superset dashboard stub</title>
</head>
<body>
<h1>Superset dashboard stub</h1>
</body>
</html>
`

const getInputByLabelText = (labelText, inputTag = 'input') =>
cy.get('form').contains('label', labelText).parent().find(inputTag)

describe('Creating, viewing, editing and deleting an embedded superset dashboard', function () {
before(function () {
// Skip this test if the DHIS2 Core version is below 42
const version = parseInt(Cypress.env('dhis2InstanceVersion'))
if (version < 42) {
this.skip()
}
})

beforeEach(() => {
// Fake support for embedded dashboards by intercepting the requests below
cy.intercept('**', (req) => {
if (req.url.includes('/systemSettings?')) {
// Append system setting for embedded dashboard support
req.continue((resp) => {
resp.body.keyEmbeddedDashboardsEnabled = true
return resp
})
} else if (req.url.includes('/superset-gateway/api/info')) {
// Stub the response to the superset gateway info request
req.reply({
supersetBaseUrl: SUPERSET_BASE_URL,
apiDocsPath:
'/superset-gateway/apidocs/dhis2-superset-gateway/swagger-ui/',
})
} else if (
req.url.includes(
'/superset-gateway/api/guestTokens/dhis2/dashboards/'
)
) {
// Stub the response to the superset gatewat guest token request
req.reply({
// Note that the string below does need to have a particular pattern: it was
// copied from the network tab and cannot be replaced by a random shorter string.
// The superset embed SDK must do some sort of pattern validation on it
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJuYW1lIjoiYW5hbHl0aWNzIiwiZmlyc3RfbmFtZSI6IkRISVMgMiBTdXBlcnNldCIsImxhc3RfbmFtZSI6IkdhdGV3YXkifSwicmVzb3VyY2VzIjpbeyJ0eXBlIjoiZGFzaGJvYXJkIiwiaWQiOiI2NmEzNGMyYS1mYTA2LTRkYjUtYmQ2ZS0wOGZkMjRiZjVhY2MifV0sInJsc19ydWxlcyI6W10sImlhdCI6MTczNzk5NTE0My4yMTU5NjcsImV4cCI6MTczNzk5NTQ0My4yMTU5NjcsImF1ZCI6Imh0dHA6Ly8wLjAuMC4wOjgwODAvIiwidHlwZSI6Imd1ZXN0In0.AayCHirBjomllKxThOCQsn4RHoIfaXfULOVtcnylhj8',
})
} else if (req.url.includes(`${SUPERSET_BASE_URL}/embedded/`)) {
req.reply(SUPERSET_DASHBOARD_STUB)
} else {
// Just return the response by default
req.continue((resp) => resp)
}
})
})

it('creates an embedded superset dashboard', () => {
// Start a new dashboard from the start page
cy.visit('#/start')

cy.get(newButtonSel, EXTENDED_TIMEOUT).click()

// Choose the embedded dashboard option
cy.contains('External').should('be.visible').click()

// Click the configure source button
cy.contains('Continue').should('be.visible').click()

// A modal form to create a new embedded dashboard is showing
cy.contains('New dashboard: external').should('be.visible')

// Check all initial values and change them
getInputByLabelText('Title').should('have.value', '').type(NAME)
getInputByLabelText('Code').should('have.value', '').type(CODE)
getInputByLabelText('Description', 'textarea')
.should('have.value', '')
.type(DESCRIPTION)
getInputByLabelText('Superset Embed ID')
.should('have.value', '')
.type(UUID)
getInputByLabelText('Show chart controls on dashboard items')
.should('be.checked')
.uncheck()
getInputByLabelText('Expand filters').should('not.be.checked').check()

// Click the create button
cy.contains('Save dashboard').should('be.enabled').click()

cy.contains('h3', NAME).should('be.visible')
cy.contains('External data').should('be.visible')
// An iframe should be visible with the UUID in the src
cy.get('iframe')
.should('be.visible')
.and('have.attr', 'src')
.and('contain', UUID)

// some options are disabled
// Primary actions
cy.contains('button', 'Slideshow').should('be.disabled')
cy.contains('button', 'Filter').should('be.disabled')
// Actions in the more-menu
cy.getByDataTest('more-actions-button').should('be.enabled').click()
cy.contains('a', 'Make available offline').should(
'have.attr',
'aria-disabled',
'true'
)
cy.contains('a', 'Print')
.should('have.attr', 'aria-disabled', 'true')
.and('have.attr', 'aria-expanded', 'false')
// Close the menu by clicking the backdrop
cy.get('.backdrop').should('be.visible').click()
})

it('shows and hides the description', () => {
cy.getByDataTest('more-actions-button').should('be.enabled').click()
cy.contains('a', 'Show description').should('be.visible').click()
cy.contains(DESCRIPTION).should('be.visible')
cy.getByDataTest('more-actions-button').should('be.enabled').click()
cy.contains('a', 'Hide description').should('be.visible').click()
cy.contains(DESCRIPTION_UPATED).should('not.exist')
})

it('stars and unstars the superset embedded dashboard', () => {
// Can be starred via menu bar
cy.getByDataTest('dashboard-unstarred').click()
cy.getByDataTest('dashboard-starred').should('be.visible')
// And unstarred via ...menu
cy.getByDataTest('more-actions-button').should('be.enabled').click()
cy.contains('a', 'Unstar dashboard').should('be.visible').click()
cy.getByDataTest('dashboard-unstarred').should('be.visible')
})

it('can open the sharing dialog', () => {
cy.contains('button', 'Share').should('be.enabled').click()
cy.contains('h1', `Sharing and access: ${NAME}`).should('be.visible')
// We don't test the actual sharing, just if the sharing modal pops up
cy.contains('button', 'Close').should('be.enabled').click()
})

it('edits the superset embedded dashboard', () => {
cy.contains('button', 'Edit').should('be.enabled').click()
cy.contains('Edit external dashboard').should('be.visible')
// Check all initial values are as when created and change some of them
getInputByLabelText('Title')
.should('have.value', NAME)
.clear()
.type(NAME_UPDATED)
getInputByLabelText('Code').should('have.value', CODE)
getInputByLabelText('Description', 'textarea')
.should('have.value', DESCRIPTION)
.clear()
.type(DESCRIPTION_UPATED)
getInputByLabelText('Superset Embed ID')
.should('have.value', UUID)
.clear()
.type(UUID_UPDATED)
getInputByLabelText('Show chart controls on dashboard items').should(
'not.be.checked'
)
getInputByLabelText('Expand filters').should('be.checked')

// Click the update button
cy.contains('Save dashboard').should('be.enabled').click()

cy.contains('h3', NAME_UPDATED).should('be.visible')
cy.contains('External data').should('be.visible')

// First show the description
cy.getByDataTest('more-actions-button').should('be.enabled').click()
cy.contains('a', 'Show description').should('be.visible').click()
// Ensure it is showing the updated description
cy.contains(DESCRIPTION_UPATED).should('be.visible')

// An iframe should be visible with the UUID in the src
cy.get('iframe')
.should('be.visible')
.and('have.attr', 'src')
.and('contain', UUID_UPDATED)
})

it('hides the description', () => {
cy.getByDataTest('more-actions-button').should('be.enabled').click()
cy.contains('a', 'Hide description').should('be.visible').click()
cy.contains(DESCRIPTION_UPATED).should('not.exist')
})

it('deletes the new superset embedded dashboard', () => {
cy.contains('Edit').should('be.enabled').click()
cy.contains('Edit external dashboard').should('be.visible')
cy.contains('button', 'Delete dashboard').should('be.enabled').click()
cy.contains('h1', 'Delete dashboard').should('be.visible')
cy.contains('button', 'Delete').should('be.enabled').click()
cy.url().should('satisfy', (href) => href.endsWith('/#/'))
})
})
Loading