跳到主要内容

测试面板插件

面板插件允许您以不同方式可视化数据。本指南将向您展示如何使用 @grafana/plugin-e2e 测试您的面板插件在多个 Grafana 版本中正确渲染数据。

测试数据

为了测试您的面板插件,您需要为其提供测试数据。Grafana 自带 TestData 数据源,可用于模拟格式为时间序列、日志、跟踪、注释等的数据帧

开始之前

要编写类似于本指南中的端到端测试,您需要通过预设配置 TestData 数据源。如果您还没有阅读关于如何设置资源的指南,请先阅读该指南。

测试面板选项

为了测试面板的行为,我们建议预设一个仪表盘,其中包含多个面板,展示面板的不同状态。这确保您的面板在各种配置下都能正常工作。通过避免依赖 Grafana 面板编辑 UI,这种方法减少了由 UI 更改导致的测试失败,使您的测试更加稳定和可靠。

在需要与面板编辑选项交互的情况下,我们提供了一组 API 来简化测试编写。这些 API 确保您的测试在不同版本的 Grafana 中都能一致地运行,而无需进行更改。

要与 Grafana 提供的任何选项组交互,请使用以下任一函数

函数名返回的选项组
getPanelOptions()面板选项
getStandardOptions()标准选项
getValueMappingOptions()值映射
getDataLinksOptions()数据链接
getThresholdsOptions()阈值

要与您的面板添加的自定义选项组交互,请使用 getCustomOptions('name of option group') API。

调用这些 API 中的任何一个都会返回一个选项组对象,该对象提供了用于与该组内选项交互的 API。

函数名返回选项类型
getRadioGroup(label)RadioGroup
getSwitch(label)Switch
getTextInput(label)定位器
getNumberInput(label)定位器
getSliderInput(label)定位器
getSelect(label)Select
getMultiSelect(label)MultiSelect
getColorPicker(label)ColorPicker
getUnitPicker(label)UnitPicker

示例

此测试确保当用户从标准选项中选择不同的单位时,UI 中显示的单位会正确更新。

test('should change the unit when standard option is changed', async ({ panelEditPage }) => {
const standardOptions = panelEditPage.getStandardOptions();
const unitPicker = standardOptions.getUnitPicker('Unit');
const unit = page.getByTestId('unit-container');

await unitPicker.selectOption('Misc > Pixels');

await expect(unit).toContainText('px');
});

此测试验证在面板设置中选择不同的时区后,时钟面板中显示的时区会更新。

test('should change time zone when option is selected', async ({ panelEditPage, page }) => {
const timeFormatOptions = panelEditPage.getCustomOptions('Timezone');
const timeZoneSelect = timeFormatOptions.getSelect('Timezone');
const timeZone = page.getByTestId('clock-panel').getByTestId('time-zone');

await timeZoneSelect.selectOption('Europe/Stockholm');
await expect(timeZone).toContainText('Europe/Stockholm');
});

此测试验证在时钟面板中启用等宽字体选项后,面板的字体族正确更新为 "monospace"。

test('should change the font family when enabling monospace', async ({ panelEditPage, page }) => {
const clockOptions = panelEditPage.getCustomOptions('Clock');
const monospaceFont = clockOptions.getSwitch('Font monospace');
const panel = page.getByTestId('clock-panel');

await monospaceFont.check();
await expect(panel).toHaveCSS('font-family', 'monospace');
});

此测试确保在时钟面板选项中选择“倒计时”模式时,时钟以倒计时模式运行。

test('should count down time when option is selected', async ({ panelEditPage, page }) => {
const clockOptions = panelEditPage.getCustomOptions('Clock');
const clockMode = clockOptions.getRadioGroup('Mode');
const panel = page.getByTestId('clock-panel-countdown');

await clockMode.check('Countdown');
await expect(panel).toBeVisible();
});

此测试验证当在面板选项的颜色选择器中选择新颜色时,面板的背景颜色会改变。

test('should update background color based on selected option', async ({ panelEditPage, page }) => {
const color = { hex: '#73bf69', rgb: 'rgb(115, 191, 105)' };
const clockOptions = panelEditPage.getCustomOptions('Clock');
const backgroundColor = clockOptions.getColorPicker('Background color');
const panel = page.getByTestId('clock-panel');

await backgroundColor.selectOption(color.hex);
await expect(panel).toHaveCSS('background-color', color.rgb);
});

表格面板默认定义了一个名为 Show table header 的自定义面板选项。如果此开关被禁用,表格面板应从表格中移除表头。

以下测试验证了字段名(表头)默认显示,并且在未选择 Show table header 选项时会被移除。

test('should hide headers when "Show table header" is unchecked', async ({ panelEditPage, selectors }) => {
await panelEditPage.datasource.set('gdev-testdata');
await panelEditPage.setVisualization('Table');
await expect(await panelEditPage.panel.fieldNames.count()).toBeGreaterThan(0);
const showTableHeaderSwitch = panelEditPage
.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Table Show table header'))
.getByLabel('Toggle switch');
await panelEditPage.collapseSection('Table');
await showTableHeaderSwitch.uncheck();
await expect(panelEditPage.panel.fieldNames).not.toBeVisible();
});

测试面板如何处理不同的数据类型

数据帧模型在设计上具有灵活性。其目的是允许数据源根据各种不同的数据类型返回查询响应。Grafana 框架中的数据类型定义或声明包括种类(kind)和格式(format)。

面板不必支持所有数据类型。但是,如果您的面板应该支持某种数据类型,我们建议您编写端到端测试来验证它是否按预期工作。

“无数据”场景

如果数据源返回 无数据,那么向用户指示这一点是很好的做法。在以下代码片段中,我们测试了表格面板如何处理 无数据 场景。

test('should display "No data" in case no data response was passed to the panel', async ({ panelEditPage, page }) => {
await panelEditPage.datasource.set('gdev-testdata');
await panelEditPage.setVisualization('Table');
await page.getByLabel('Scenario').last().click();
await page.getByText('No Data Points').click();
await panelEditPage.refreshPanel();
await expect(panelEditPage.panel.locator).toContainText('No data');
});

多个数据帧

表格面板一次只能显示一个数据帧。如果向面板传递了多个数据帧,无论它们源自同一查询还是不同查询,面板都只显示第一个数据帧。

此外,面板中还会有一个下拉菜单,允许用户在不同数据帧之间切换。此行为是表格面板特有的。

以下代码片段测试了在向面板传递两个数据帧的情况下,插件是否显示包含两个值的下拉菜单。

test('should display dropdown with two values when two frames are passed to the panel', async ({
panelEditPage,
page,
selectors,
}) => {
await panelEditPage.datasource.set('gdev-testdata');
await panelEditPage.setVisualization('Table');
await panelEditPage.getQueryEditorRow('A').getByLabel('Alias').fill('a');
await page.getByText('Add query').click();
await panelEditPage.getQueryEditorRow('B').getByLabel('Alias').fill('b');
await panelEditPage.refreshPanel();
await panelEditPage.panel.locator.getByRole('combobox').click();
await expect(panelEditPage.getByTestIdOrAriaLabel(selectors.components.Select.option)).toHaveText(['a', 'b']);
});