Dashboard edit panel e2e (#104775)

setup suite for edit panel;  start adding test for custom variable edit
This commit is contained in:
Scott Lepper
2025-05-01 15:14:36 -04:00
committed by GitHub
parent 1114d33936
commit d30b39f350
9 changed files with 148 additions and 7 deletions

View File

@ -0,0 +1,60 @@
import { e2e } from '../utils';
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
const DASHBOARD_NAME = 'Test variable output';
describe('Dashboard edit variables', () => {
beforeEach(() => {
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
});
it('can add a new custom variable', () => {
e2e.pages.Dashboards.visit();
e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1` });
cy.contains(DASHBOARD_NAME).should('be.visible');
const variable: Variable = {
type: 'custom',
name: 'foo',
label: 'Foo',
value: 'one,two,three',
};
// common steps to add a new variable
flows.newEditPaneVariableClick();
flows.newEditPanelCommonVariableInputs(variable);
// set the custom variable value
e2e.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput().clear().type(variable.value).blur();
// assert the dropdown for the variable is visible and has the correct values
e2e.pages.Dashboard.SubMenu.submenuItemLabels(variable.label).should('be.visible').contains(variable.label);
const values = variable.value.split(',');
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(values[0]).should('be.visible');
});
});
// Common flows for adding/editing variables
// TODO: maybe move to e2e flows
const flows = {
newEditPaneVariableClick() {
e2e.components.NavToolbar.editDashboard.editButton().should('be.visible').click();
e2e.components.PanelEditor.Outline.section().should('be.visible').click();
e2e.components.PanelEditor.Outline.item('Variables').should('be.visible').click();
e2e.components.PanelEditor.ElementEditPane.addVariableButton().should('be.visible').click();
},
newEditPanelCommonVariableInputs(variable: Variable) {
e2e.components.PanelEditor.ElementEditPane.variableType(variable.type).should('be.visible').click();
e2e.components.PanelEditor.ElementEditPane.variableNameInput().clear().type(variable.name).blur();
e2e.components.PanelEditor.ElementEditPane.variableLabelInput().clear().type(variable.label).blur();
},
};
type Variable = {
type: string;
name: string;
label: string;
description?: string;
value: string;
};

View File

@ -30,6 +30,7 @@ rootForEnterpriseSuite="./e2e/extensions-suite"
rootForOldArch="./e2e/old-arch"
rootForKubernetesDashboards="./e2e/dashboards-suite"
rootForSearchDashboards="./e2e/dashboards-search-suite"
rootForEditDashboards="./e2e/dashboards-edit-v2-suite"
declare -A cypressConfig=(
[screenshotsFolder]=./e2e/"${args[0]}"/screenshots
@ -149,6 +150,25 @@ case "$1" in
;;
esac
;;
"dashboards-edit-v2")
env[kubernetesDashboards]=true
env[dashboardNewLayouts]=true
cypressConfig[specPattern]=$rootForEditDashboards/$testFilesForSingleSuite
cypressConfig[video]=false
case "$2" in
"debug")
echo -e "Debug mode"
env[SLOWMO]=1
PARAMS="--no-exit"
enterpriseSuite=$(basename "${args[2]}")
;;
"dev")
echo "Dev mode"
CMD="cypress open"
enterpriseSuite=$(basename "${args[2]}")
;;
esac
;;
"enterprise-smtp")
env[SMTP_PLUGIN_ENABLED]=true
cypressConfig[specPattern]=./e2e/extensions/enterprise/smtp-suite/$testFilesForSingleSuite

View File

@ -14,6 +14,8 @@
"e2e:old-arch": "./e2e/start-and-run-suite old-arch",
"e2e:schema-v2": "./e2e/start-and-run-suite dashboards-schema-v2",
"e2e:dashboards-search": "./e2e/start-and-run-suite dashboards-search",
"e2e:dashboards-edit-v2": "./e2e/start-and-run-suite dashboards-edit-v2",
"e2e:dashboards-edit-v2:dev": "./e2e/start-and-run-suite dashboards-edit-v2 dev",
"e2e:debug": "./e2e/start-and-run-suite debug",
"e2e:dev": "./e2e/start-and-run-suite dev",
"e2e:benchmark:live": "./e2e/start-and-run-suite benchmark live",

View File

@ -532,6 +532,33 @@ export const versionedComponents = {
measureButton: {
'9.2.0': 'show measure tools',
},
Outline: {
section: {
'12.0.0': 'data-testid Outline section',
},
node: {
'12.0.0': (type: string) => `data-testid outline node ${type}`,
},
item: {
'12.0.0': (type: string) => `data-testid outline item ${type}`,
},
},
ElementEditPane: {
variableType: {
'12.0.0': (type?: string) => `data-testid variable type ${type}`,
},
addVariableButton: {
'12.0.0': 'data-testid add variable button',
},
variableNameInput: {
'12.0.0': 'data-testid variable name input',
},
variableLabelInput: {
'12.0.0': 'data-testid variable label input',
},
},
},
PanelInspector: {
Data: {

View File

@ -3,6 +3,7 @@ import { Resizable } from 're-resizable';
import { useLocalStorage } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { SceneObjectState, SceneObjectBase, SceneObject, sceneGraph, useSceneObjectState } from '@grafana/scenes';
import {
ElementSelectionContextItem,
@ -272,11 +273,12 @@ export function DashboardEditPaneRenderer({ editPane, isCollapsed, onToggleColla
role="button"
onClick={() => setOutlineCollapsed(!outlineCollapsed)}
className={styles.outlineCollapseButton}
data-testid={selectors.components.PanelEditor.Outline.section}
>
<Text weight="medium">
<Trans i18nKey="dashboard-scene.dashboard-edit-pane-renderer.outline">Outline</Trans>
</Text>
<Icon name="angle-up" />
<Icon name={outlineCollapsed ? 'angle-up' : 'angle-down'} />
</div>
{!outlineCollapsed && (
<div className={styles.outlineContainer}>

View File

@ -3,6 +3,7 @@ import { sortBy } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { SceneObject } from '@grafana/scenes';
import { Box, Icon, Text, useElementSelection, useStyles2, useTheme2 } from '@grafana/ui';
import { t, Trans } from 'app/core/internationalization';
@ -88,7 +89,12 @@ function DashboardOutlineNode({
onPointerDown={onNodeClicked}
>
{elementInfo.isContainer && (
<button role="treeitem" className={styles.angleButton} onPointerDown={onToggleCollapse}>
<button
role="treeitem"
className={styles.angleButton}
onPointerDown={onToggleCollapse}
data-testid={selectors.components.PanelEditor.Outline.node(instanceName)}
>
<Icon name={!isCollapsed ? 'angle-down' : 'angle-right'} />
</button>
)}
@ -96,6 +102,7 @@ function DashboardOutlineNode({
role="button"
className={cx(styles.nodeName, isCloned && styles.nodeNameClone)}
onDoubleClick={outlineRename.onNameDoubleClicked}
data-testid={selectors.components.PanelEditor.Outline.item(instanceName)}
>
<Icon size="sm" name={elementInfo.icon} />
{outlineRename.isRenaming ? (

View File

@ -11,7 +11,7 @@ interface Props {
checkedIcon?: IconName;
checkedLabel?: string;
disabled?: boolean;
'data-testId'?: string;
'data-testid'?: string;
onClick: (evt: MouseEvent<HTMLDivElement>) => void;
}
@ -23,7 +23,7 @@ export const ToolbarSwitch = ({
checkedLabel,
disabled,
onClick,
'data-testId': dataTestId,
'data-testid': dataTestId,
}: Props) => {
const styles = useStyles2(getStyles);

View File

@ -1,6 +1,7 @@
import { FormEvent, useMemo, useState } from 'react';
import { VariableHide } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { locationService } from '@grafana/runtime';
import { LocalValueVariable, MultiValueVariable, SceneVariable, SceneVariableSet } from '@grafana/scenes';
import { Input, TextArea, Button, Field, Box, Stack } from '@grafana/ui';
@ -143,14 +144,27 @@ function VariableNameInput({ variable, isNewElement }: { variable: SceneVariable
return (
<Field label={t('dashboard.edit-pane.variable.name', 'Name')} invalid={!!nameError} error={nameError}>
<Input ref={ref} value={name} onChange={onChange} required onBlur={onBlur} />
<Input
ref={ref}
value={name}
onChange={onChange}
required
onBlur={onBlur}
data-testid={selectors.components.PanelEditor.ElementEditPane.variableNameInput}
/>
</Field>
);
}
function VariableLabelInput({ variable }: VariableInputProps) {
const { label } = variable.useState();
return <Input value={label} onChange={(e) => variable.setState({ label: e.currentTarget.value })} />;
return (
<Input
value={label}
onChange={(e) => variable.setState({ label: e.currentTarget.value })}
data-testid={selectors.components.PanelEditor.ElementEditPane.variableLabelInput}
/>
);
}
function VariableDescriptionTextArea({ variable }: VariableInputProps) {

View File

@ -3,6 +3,7 @@ import { useMemo } from 'react';
import { useToggle } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { SceneVariable, SceneVariableSet } from '@grafana/scenes';
import { Stack, Button, useStyles2, Text, Box, Card } from '@grafana/ui';
import { t, Trans } from 'app/core/internationalization';
@ -87,7 +88,14 @@ function VariableList({ set }: { set: SceneVariableSet }) {
))}
{canAdd && (
<Box paddingBottom={1} display={'flex'}>
<Button fullWidth icon="plus" size="sm" variant="secondary" onClick={setIsAdding}>
<Button
fullWidth
icon="plus"
size="sm"
variant="secondary"
onClick={setIsAdding}
data-testid={selectors.components.PanelEditor.ElementEditPane.addVariableButton}
>
<Trans i18nKey="dashboard.edit-pane.variables.add-variable">Add variable</Trans>
</Button>
</Box>
@ -115,6 +123,7 @@ function VariableTypeSelection({ onAddVariable }: VariableTypeSelectionProps) {
onClick={() => onAddVariable(option.value!)}
key={option.value}
title={t('dashboard.edit-pane.variables.select-type-card-tooltip', 'Click to select type')}
data-testid={selectors.components.PanelEditor.ElementEditPane.variableType(option.value!)}
>
<Card.Heading>{option.label}</Card.Heading>
<Card.Description className={styles.cardDescription}>{option.description}</Card.Description>