Prometheus: Migrate from aria-label to data-testid for selectors (#78554)

* migrate from aria-label to data-testid for selectors

* fix e2e tests with shared components

* fix e2e tests with more shared components

* pass data-testid to SelectBase component

* pass data-testid to value container and traverse the dom to access the input in e2e
This commit is contained in:
Brendan O'Handley 2023-12-26 13:43:06 -06:00 committed by GitHub
parent f41a0559fa
commit a9f4ffab5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 52 additions and 57 deletions

View File

@ -669,9 +669,6 @@ exports[`better eslint`] = {
"packages/grafana-ui/src/components/PanelChrome/index.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"packages/grafana-ui/src/components/QueryField/QueryField.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
],
"packages/grafana-ui/src/components/Segment/SegmentSelect.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
@ -5485,13 +5482,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"]
],
"public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
],
"public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"]
[0, 0, 0, "Styles should be written using objects.", "1"]
],
"public/app/plugins/datasource/loki/configuration/ConfigEditor.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@ -5662,8 +5655,7 @@ exports[`better eslint`] = {
],
"public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"]
[0, 0, 0, "Styles should be written using objects.", "1"]
],
"public/app/plugins/datasource/prometheus/configuration/AzureCredentialsConfig.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@ -5708,12 +5700,8 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "18"],
[0, 0, 0, "Styles should be written using objects.", "19"]
],
"public/app/plugins/datasource/prometheus/configuration/ExemplarSetting.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
],
"public/app/plugins/datasource/prometheus/configuration/ExemplarsSettings.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"]
[0, 0, 0, "Styles should be written using objects.", "0"]
],
"public/app/plugins/datasource/prometheus/datasource.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@ -5774,13 +5762,10 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/datasource/prometheus/querybuilder/components/LabelFilterItem.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"],
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "4"],
[0, 0, 0, "Do not use any type assertions.", "5"],
[0, 0, 0, "Do not use any type assertions.", "6"]
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"]
],
"public/app/plugins/datasource/prometheus/querybuilder/components/LabelFilters.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"]
@ -5809,9 +5794,6 @@ exports[`better eslint`] = {
"public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryCodeEditor.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"]
],
"public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
],
"public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/AdditionalSettings.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
@ -5857,18 +5839,15 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/datasource/prometheus/querybuilder/shared/LabelFilterItem.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"],
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "3"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Do not use any type assertions.", "5"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Do not use any type assertions.", "7"],
[0, 0, 0, "Do not use any type assertions.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Do not use any type assertions.", "10"],
[0, 0, 0, "Do not use any type assertions.", "11"]
[0, 0, 0, "Do not use any type assertions.", "8"]
],
"public/app/plugins/datasource/prometheus/querybuilder/shared/LabelFilters.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]

View File

@ -69,11 +69,20 @@ describe('Loki query builder', () => {
e2e.components.QueryBuilder.labelSelect().should('be.visible').click();
// wait until labels are loaded and set on the component before starting to type
cy.wait('@labelsRequest');
e2e.components.QueryBuilder.labelSelect().type('instance{enter}');
e2e.components.QueryBuilder.matchOperatorSelect().should('be.visible').click().type('=~{enter}');
e2e.components.QueryBuilder.labelSelect().children('div').children('input').type('instance{enter}');
e2e.components.QueryBuilder.matchOperatorSelect()
.should('be.visible')
.click()
.children('div')
.children('input')
.type('=~{enter}', { force: true });
e2e.components.QueryBuilder.valueSelect().should('be.visible').click();
cy.wait('@valuesRequest');
e2e.components.QueryBuilder.valueSelect().type('instance1{enter}').type('instance2{enter}');
e2e.components.QueryBuilder.valueSelect()
.children('div')
.children('input')
.type('instance1{enter}')
.type('instance2{enter}');
cy.contains(MISSING_LABEL_FILTER_ERROR_MESSAGE).should('not.exist');
cy.contains(finalQuery).should('be.visible');

View File

@ -61,8 +61,8 @@ export const Components = {
Prometheus: {
configPage: {
connectionSettings: 'Data source connection URL',
exemplarsAddButton: 'Add exemplar config button',
internalLinkSwitch: 'Internal link switch',
exemplarsAddButton: 'data-testid Add exemplar config button',
internalLinkSwitch: 'data-testid Internal link switch',
},
exemplarMarker: 'data-testid Exemplar marker',
},
@ -345,12 +345,12 @@ export const Components = {
TraceViewer: {
spanBar: 'data-testid SpanBar--wrapper',
},
QueryField: { container: 'Query field' },
QueryField: { container: 'data-testid Query field' },
QueryBuilder: {
queryPatterns: 'Query patterns',
labelSelect: 'Select label',
valueSelect: 'Select value',
matchOperatorSelect: 'Select match operator',
queryPatterns: 'data-testid Query patterns',
labelSelect: 'data-testid Select label',
valueSelect: 'data-testid Select value',
matchOperatorSelect: 'data-testid Select match operator',
},
ValuePicker: {
button: (name: string) => `data-testid Value picker button ${name}`,

View File

@ -210,7 +210,7 @@ export class UnThemedQueryField extends PureComponent<QueryFieldProps, QueryFiel
return (
<div className={cx(wrapperClassName, styles.wrapper)}>
<div className="slate-query-field" aria-label={selectors.components.QueryField.container}>
<div className="slate-query-field" data-testid={selectors.components.QueryField.container}>
<Editor
ref={(editor) => (this.editor = editor!)}
schema={SCHEMA}

View File

@ -73,6 +73,7 @@ export function SelectBase<T, Rest = {}>({
allowCustomValue = false,
allowCreateWhileLoading = false,
'aria-label': ariaLabel,
'data-testid': dataTestid,
autoFocus = false,
backspaceRemovesValue = true,
blurInputOnSelect,
@ -199,6 +200,7 @@ export function SelectBase<T, Rest = {}>({
const commonSelectProps = {
'aria-label': ariaLabel,
'data-testid': dataTestid,
autoFocus,
backspaceRemovesValue,
blurInputOnSelect,

View File

@ -29,17 +29,21 @@ class UnthemedValueContainer extends Component<any & { theme: GrafanaTheme2 }> {
}
renderContainer(children?: ReactNode) {
const { isMulti, theme } = this.props;
const { isMulti, theme, selectProps } = this.props;
const noWrap = this.props.selectProps?.noMultiValueWrap && !this.props.selectProps?.menuIsOpen;
const styles = getSelectStyles(theme);
const dataTestid = selectProps['data-testid'];
const className = cx(
styles.valueContainer,
isMulti && styles.valueContainerMulti,
noWrap && styles.valueContainerMultiNoWrap
);
return <div className={className}>{children}</div>;
return (
<div data-testid={dataTestid} className={className}>
{children}
</div>
);
}
}

View File

@ -18,6 +18,7 @@ export type LoadOptionsCallback<T> = (options: Array<SelectableValue<T>>) => voi
export interface SelectCommonProps<T> {
/** Aria label applied to the input field */
['aria-label']?: string;
['data-testid']?: string;
allowCreateWhileLoading?: boolean;
allowCustomValue?: boolean;
/** Focus is set to the Select when rendered*/

View File

@ -150,7 +150,7 @@ export const LokiQueryEditor = React.memo<LokiQueryEditorProps>((props) => {
<EditorHeader>
<Stack gap={1}>
<Button
aria-label={selectors.components.QueryBuilder.queryPatterns}
data-testid={selectors.components.QueryBuilder.queryPatterns}
variant="secondary"
size="sm"
onClick={() => {

View File

@ -166,7 +166,7 @@ const MonacoQueryField = ({
return (
<div
aria-label={selectors.components.QueryField.container}
data-testid={selectors.components.QueryField.container}
className={styles.container}
// NOTE: we will be setting inline-style-width/height on this element
ref={containerRef}

View File

@ -122,7 +122,7 @@ const MonacoQueryField = (props: Props) => {
return (
<div
aria-label={selectors.components.QueryField.container}
data-testid={selectors.components.QueryField.container}
className={styles.container}
// NOTE: we will be setting inline-style-width/height on this element
ref={containerRef}

View File

@ -40,7 +40,7 @@ export default function ExemplarSetting({ value, onChange, onDelete, disabled }:
<>
<Switch
value={isInternalLink}
aria-label={selectors.components.DataSource.Prometheus.configPage.internalLinkSwitch}
data-testid={selectors.components.DataSource.Prometheus.configPage.internalLinkSwitch}
onChange={(ev) => setIsInternalLink(ev.currentTarget.checked)}
/>
</>

View File

@ -46,7 +46,7 @@ export function ExemplarsSettings({ options, onChange, disabled }: Props) {
{!disabled && (
<Button
variant="secondary"
aria-label={selectors.components.DataSource.Prometheus.configPage.exemplarsAddButton}
data-testid={selectors.components.DataSource.Prometheus.configPage.exemplarsAddButton}
className={css`
margin-bottom: 10px;
`}

View File

@ -81,7 +81,7 @@ export function LabelFilterItem({
{/* Label name select, loads all values at once */}
<Select
placeholder="Select label"
aria-label={selectors.components.QueryBuilder.labelSelect}
data-testid={selectors.components.QueryBuilder.labelSelect}
inputId="prometheus-dimensions-filter-item-key"
width="auto"
value={item.label ? toOption(item.label) : null}
@ -113,7 +113,7 @@ export function LabelFilterItem({
{/* Operator select i.e. = =~ != !~ */}
<Select
aria-label={selectors.components.QueryBuilder.matchOperatorSelect}
data-testid={selectors.components.QueryBuilder.matchOperatorSelect}
className="query-segment-operator"
value={toOption(item.op ?? defaultOp)}
options={operators}
@ -133,7 +133,7 @@ export function LabelFilterItem({
{/* Label value async select: autocomplete calls prometheus API */}
<AsyncSelect
placeholder="Select value"
aria-label={selectors.components.QueryBuilder.valueSelect}
data-testid={selectors.components.QueryBuilder.valueSelect}
inputId="prometheus-dimensions-filter-item-value"
width="auto"
value={

View File

@ -116,7 +116,7 @@ export const PromQueryEditorSelector = React.memo<Props>((props) => {
/>
<EditorHeader>
<Button
aria-label={selectors.components.QueryBuilder.queryPatterns}
data-testid={selectors.components.QueryBuilder.queryPatterns}
variant="secondary"
size="sm"
onClick={() => setQueryPatternsModalOpen((prevValue) => !prevValue)}

View File

@ -77,7 +77,7 @@ export function LabelFilterItem({
<InputGroup>
<Select
placeholder="Select label"
aria-label={selectors.components.QueryBuilder.labelSelect}
data-testid={selectors.components.QueryBuilder.labelSelect}
inputId="prometheus-dimensions-filter-item-key"
width="auto"
value={item.label ? toOption(item.label) : null}
@ -107,7 +107,7 @@ export function LabelFilterItem({
/>
<Select
aria-label={selectors.components.QueryBuilder.matchOperatorSelect}
data-testid={selectors.components.QueryBuilder.matchOperatorSelect}
value={toOption(item.op ?? defaultOp)}
options={operators}
width="auto"
@ -125,7 +125,7 @@ export function LabelFilterItem({
<Select
placeholder="Select value"
aria-label={selectors.components.QueryBuilder.valueSelect}
data-testid={selectors.components.QueryBuilder.valueSelect}
inputId="prometheus-dimensions-filter-item-value"
width="auto"
value={