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 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1f9a133
feat: fetch superset base url with system settings (#3181)
HendrikThePendric Jan 14, 2025
b159ad9
feat: create embedded superset dashboard (#3185)
HendrikThePendric Jan 20, 2025
628942e
feat: show embedded superset dashboard in iframe (#3192)
HendrikThePendric Jan 21, 2025
a067127
feat: adjust dashboards bar for external dashboards (#3193)
HendrikThePendric Jan 23, 2025
e05d616
test: add e2e tests for superset embedded dashboards feature (#3197)
HendrikThePendric Feb 3, 2025
f7a1c57
chore: add unit tests and implement feedback (#3206)
HendrikThePendric Feb 6, 2025
f9ddd9e
chore: fix problem in en.pot file
HendrikThePendric Feb 10, 2025
5ae8d17
chore: always use "continue" text in choose-dashboard-type-modal button
HendrikThePendric Feb 10, 2025
3767128
fix: adjust external creation modal title
cooper-joe Feb 12, 2025
2b10be5
fix: adjust dashboard type radio components
cooper-joe Feb 12, 2025
87cd240
fix: external creation code help text
cooper-joe Feb 12, 2025
ffd3956
fix: labels in tests
cooper-joe Feb 12, 2025
5ffc150
chore: update yarn.lock
HendrikThePendric Feb 11, 2025
ed85aac
chore: clarify supersetGateway api with comments
HendrikThePendric Feb 11, 2025
c3d959f
chore: rename hasSupersetSupport to isSupersetSupported
HendrikThePendric Feb 11, 2025
8857f20
fix: correct position of conditional chain
HendrikThePendric Feb 11, 2025
d20a1a7
chore: use individual constants for field names
HendrikThePendric Feb 11, 2025
7f170a2
chore: improve naming consistency for blocks
HendrikThePendric Feb 12, 2025
5270706
chore: solve sonarcube issue by removing commented code block
HendrikThePendric Feb 12, 2025
29b8fe5
chore: reword e2e test description to better reflect its limitations
HendrikThePendric Feb 12, 2025
1164220
chore(e2e): merge disabled buttons step into creation step
HendrikThePendric Feb 12, 2025
a96ca85
chore: test showing and hiding the description in a single test step
HendrikThePendric Feb 12, 2025
1a437bc
chore: show description before asserting the updated description text
HendrikThePendric Feb 12, 2025
379682d
fix: restore last-updated-tag to original width
HendrikThePendric Feb 12, 2025
35d0163
chore: regenerate pot file after textual changes
HendrikThePendric Feb 12, 2025
f61a0fb
fix: add max height of 180px to description field
HendrikThePendric Feb 12, 2025
3c5fc26
fix: ensure Superset is capitalized in error message
HendrikThePendric Feb 12, 2025
6e1a489
fix: add tooltip around external data tag as in ui specs
HendrikThePendric Feb 12, 2025
e4f2cc1
chore: remove redundant nsSeparators in i18n.t calls
HendrikThePendric Feb 12, 2025
db04f8c
chore: rename files and and components according to PR feedback
HendrikThePendric Feb 12, 2025
7ac2f18
fix: use logical css property
HendrikThePendric Feb 12, 2025
843c8ec
chore: fix formatting issue
HendrikThePendric Feb 13, 2025
2136513
fix: prevent flashing iframe error ui and show better error message
HendrikThePendric Feb 25, 2025
17847c7
fix: adjust "External data" to "External source"
HendrikThePendric Feb 26, 2025
d1424a0
chore: update en.pot and yarn.lock
HendrikThePendric Feb 26, 2025
9169cca
chore: adjust choose embedded dashboard type text
HendrikThePendric Feb 26, 2025
5467afa
feat: omit tabbar for cascading sharing for embedded dashboards
HendrikThePendric Feb 27, 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/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 source').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 source').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
Loading