modernize test-subj-selector (#140847)

This commit is contained in:
spalger 2022-09-15 12:38:36 -05:00
parent 997caf021c
commit 77964bc0f7
23 changed files with 217 additions and 104 deletions

View file

@ -1105,6 +1105,7 @@
"@types/kbn__telemetry-tools": "link:bazel-bin/packages/kbn-telemetry-tools/npm_module_types",
"@types/kbn__test": "link:bazel-bin/packages/kbn-test/npm_module_types",
"@types/kbn__test-jest-helpers": "link:bazel-bin/packages/kbn-test-jest-helpers/npm_module_types",
"@types/kbn__test-subj-selector": "link:bazel-bin/packages/kbn-test-subj-selector/npm_module_types",
"@types/kbn__tooling-log": "link:bazel-bin/packages/kbn-tooling-log/npm_module_types",
"@types/kbn__type-summarizer": "link:bazel-bin/packages/kbn-type-summarizer/npm_module_types",
"@types/kbn__type-summarizer-cli": "link:bazel-bin/packages/kbn-type-summarizer-cli/npm_module_types",

View file

@ -562,6 +562,7 @@ filegroup(
"//packages/kbn-telemetry-tools:build_types",
"//packages/kbn-test:build_types",
"//packages/kbn-test-jest-helpers:build_types",
"//packages/kbn-test-subj-selector:build_types",
"//packages/kbn-tooling-log:build_types",
"//packages/kbn-type-summarizer:build_types",
"//packages/kbn-type-summarizer-cli:build_types",

View file

@ -7,7 +7,7 @@
*/
import { EuiCallOut } from '@elastic/eui';
import testSubjSelector from '@kbn/test-subj-selector';
import { subj as testSubjSelector } from '@kbn/test-subj-selector';
import React from 'react';
import { of, ReplaySubject } from 'rxjs';
import { mountWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers';

View file

@ -1,13 +1,28 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "pkg_npm")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_BASE_NAME = "kbn-test-subj-selector"
PKG_DIRNAME = "kbn-test-subj-selector"
PKG_REQUIRE_NAME = "@kbn/test-subj-selector"
SOURCE_FILES = glob([
"index.d.ts",
"index.js"
])
SOURCE_FILES = glob(
[
"**/*.ts",
],
exclude = [
"**/*.config.js",
"**/*.mock.*",
"**/*.test.*",
"**/*.stories.*",
"**/__snapshots__/**",
"**/integration_tests/**",
"**/mocks/**",
"**/scripts/**",
"**/storybook/**",
"**/test_fixtures/**",
"**/test_helpers/**",
],
)
SRCS = SOURCE_FILES
@ -20,29 +35,90 @@ NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = []
# In this array place runtime dependencies, including other packages and NPM packages
# which must be available for this code to run.
#
# To reference other packages use:
# "//repo/relative/path/to/package"
# eg. "//packages/kbn-utils"
#
# To reference a NPM package use:
# "@npm//name-of-package"
# eg. "@npm//lodash"
RUNTIME_DEPS = [
]
# In this array place dependencies necessary to build the types, which will include the
# :npm_module_types target of other packages and packages from NPM, including @types/*
# packages.
#
# To reference the types for another package use:
# "//repo/relative/path/to/package:npm_module_types"
# eg. "//packages/kbn-utils:npm_module_types"
#
# References to NPM packages work the same as RUNTIME_DEPS
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
declaration_map = True,
emit_declaration_only = True,
out_dir = "target_types",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_BASE_NAME,
srcs = NPM_MODULE_EXTRA_FILES + [
":srcs",
],
deps = RUNTIME_DEPS,
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [
":%s" % PKG_BASE_NAME,
]
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [
":npm_module",
],
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -7,4 +7,20 @@ date: 2022-05-19
tags: ['kibana', 'dev', 'contributor', 'operations', 'test', 'subj', 'selector']
---
Converts a string from a test subject syntax into a css selectors composed by `data-test-subj`.
Converts a testSubject selector into a CSS selector.
testSubject selector syntax rules:
- `data-test-subj` values can include spaces
- prefixing a value with `~` will allow matching a single word in a `data-test-subj` that uses several space delimited list words
- example: `~foo`
- css equivalent: `[data-test-subj~="foo"]`
- the `>` character is used between two values to indicate that the value on the right must match an element inside an element matched by the value on the left
- example: `foo > bar`
- css equivalent: `[data-test-subj=foo] [data-test-subj=bar]`
- the `&` character is used between two values to indicate that the value on both sides must both match the element
- example: `foo & bar`
- css equivalent: `[data-test-subj=foo][data-test-subj=bar]`

View file

@ -1,38 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
function selectorToTerms(selector) {
return selector
.replace(/\s*~\s*/g, '~') // css locator with '~' operator cannot contain spaces
.replace(/\s*>\s*/g, '>') // remove all whitespace around joins >
.replace(/\s*&\s*/g, '&') // remove all whitespace around joins &
.split(/>+/);
}
function termToCssSelector(term) {
if (term) {
return term.startsWith('~')
? '[data-test-subj~="' + term.substring(1).replace(/\s/g, '') + '"]'
: '[data-test-subj="' + term + '"]';
} else {
return '';
}
}
module.exports = function testSubjSelector(selector) {
const cssSelectors = [];
const terms = selectorToTerms(selector);
while (terms.length) {
const term = terms.shift();
// split each term by joins/& and map to css selectors
cssSelectors.push(term.split('&').map(termToCssSelector).join(''));
}
return cssSelectors.join(' ');
};

View file

@ -1,24 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
const testSubjSelector = require('.');
describe('testSubjSelector()', function () {
it('converts subjectSelectors to cssSelectors', function () {
expect(testSubjSelector('foo bar')).toEqual('[data-test-subj="foo bar"]');
expect(testSubjSelector('foo > bar')).toEqual('[data-test-subj="foo"] [data-test-subj="bar"]');
expect(testSubjSelector('foo > bar baz')).toEqual(
'[data-test-subj="foo"] [data-test-subj="bar baz"]'
);
expect(testSubjSelector('foo> ~bar')).toEqual('[data-test-subj="foo"] [data-test-subj~="bar"]');
expect(testSubjSelector('~ foo')).toEqual('[data-test-subj~="foo"]');
expect(testSubjSelector('~foo & ~ bar')).toEqual(
'[data-test-subj~="foo"][data-test-subj~="bar"]'
);
});
});

View file

@ -6,5 +6,4 @@
* Side Public License, v 1.
*/
// eslint-disable-next-line import/no-default-export
export default function kbnTestSubjSelector(selector: string): string;
export { subj } from './test_subj_selector';

View file

@ -7,7 +7,7 @@
*/
module.exports = {
preset: '@kbn/test',
preset: '@kbn/test/jest_node',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-test-subj-selector'],
};

View file

@ -1,8 +1,8 @@
{
"type": "shared-common",
"id": "@kbn/test-subj-selector",
"devOnly": true,
"owner": "@elastic/kibana-operations",
"devOnly": true,
"runtimeDeps": [],
"typeDeps": []
"typeDeps": [],
}

11
packages/kbn-test-subj-selector/package.json Executable file → Normal file
View file

@ -1,10 +1,7 @@
{
"name": "@kbn/test-subj-selector",
"version": "0.2.1",
"description": "",
"main": "index.js",
"keywords": [],
"author": "Spencer Alger <email@spalger.com>",
"license": "SSPL-1.0 OR Elastic License 2.0",
"private": "true"
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { subj } from './test_subj_selector';
describe('testSubjSelector()', function () {
it('converts subjectSelectors to cssSelectors', function () {
expect(subj('foo bar')).toEqual('[data-test-subj="foo bar"]');
expect(subj('foo > bar')).toEqual('[data-test-subj="foo"] [data-test-subj="bar"]');
expect(subj('foo > bar baz')).toEqual('[data-test-subj="foo"] [data-test-subj="bar baz"]');
expect(subj('foo> ~bar')).toEqual('[data-test-subj="foo"] [data-test-subj~="bar"]');
expect(subj('~ foo')).toEqual('[data-test-subj~="foo"]');
expect(subj('~foo & ~ bar')).toEqual('[data-test-subj~="foo"][data-test-subj~="bar"]');
});
});

View file

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
function selectorToTerms(selector: string) {
return selector
.replace(/\s*~\s*/g, '~') // css locator with '~' operator cannot contain spaces
.replace(/\s*>\s*/g, '>') // remove all whitespace around joins >
.replace(/\s*&\s*/g, '&') // remove all whitespace around joins &
.split(/>+/);
}
function termToCssSelector(term: string) {
if (term) {
return term.startsWith('~')
? '[data-test-subj~="' + term.substring(1).replace(/\s/g, '') + '"]'
: '[data-test-subj="' + term + '"]';
} else {
return '';
}
}
/**
* Converts a testSubject selector into a CSS selector.
*
* testSubject selector syntax rules:
*
* - `data-test-subj` values can include spaces
*
* - prefixing a value with `~` will allow matching a single word in a `data-test-subj` that uses several space delimited list words
* - example: `~foo`
* - css equivalent: `[data-test-subj~="foo"]`
*
* - the `>` character is used between two values to indicate that the value on the right must match an element inside an element matched by the value on the left
* - example: `foo > bar`
* - css equivalent: `[data-test-subj=foo] [data-test-subj=bar]`
*
* - the `&` character is used between two values to indicate that the value on both sides must both match the element
* - example: `foo & bar`
* - css equivalent: `[data-test-subj=foo][data-test-subj=bar]`
*/
export function subj(selector: string) {
return selectorToTerms(selector)
.map((term) =>
// split each term by joins/& and map to css selectors
term.split('&').map(termToCssSelector).join('')
)
.join(' ');
}

View file

@ -1,9 +1,17 @@
{
"extends": "../../tsconfig.bazel.json",
"compilerOptions": {
"outDir": "target/types"
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"stripInternal": false,
"types": [
"jest",
"node"
]
},
"include": [
"index.d.ts"
"**/*.ts",
]
}

View file

@ -7,7 +7,7 @@
*/
import chalk from 'chalk';
import testSubjectToCss from '@kbn/test-subj-selector';
import { subj as testSubjectToCss } from '@kbn/test-subj-selector';
import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/axe-config';
import { FtrService } from '../../ftr_provider_context';

View file

@ -7,7 +7,7 @@
*/
import expect from '@kbn/expect';
import testSubjSelector from '@kbn/test-subj-selector';
import { subj as testSubjSelector } from '@kbn/test-subj-selector';
import { PluginFunctionalProviderContext } from '../../plugin_functional/services';
// eslint-disable-next-line import/no-default-export

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import testSubjSelector from '@kbn/test-subj-selector';
import { subj as testSubjSelector } from '@kbn/test-subj-selector';
import { WebElementWrapper } from '../lib/web_element_wrapper';
import { FtrService } from '../../ftr_provider_context';
import { TimeoutOpt } from './types';

View file

@ -10,7 +10,7 @@ import { setTimeout as setTimeoutAsync } from 'timers/promises';
import { WebElement, WebDriver, By, Key } from 'selenium-webdriver';
import { PNG } from 'pngjs';
import cheerio from 'cheerio';
import testSubjSelector from '@kbn/test-subj-selector';
import { subj as testSubjSelector } from '@kbn/test-subj-selector';
import { ToolingLog } from '@kbn/tooling-log';
import { CustomCheerio, CustomCheerioStatic } from './custom_cheerio_api';
// @ts-ignore not supported yet

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import testSubject from '@kbn/test-subj-selector';
import { subj as testSubject } from '@kbn/test-subj-selector';
import React from 'react';
import { act } from 'react-dom/test-utils';

View file

@ -6,7 +6,7 @@
*/
import expect from '@kbn/expect';
import testSubjSelector from '@kbn/test-subj-selector';
import { subj as testSubjSelector } from '@kbn/test-subj-selector';
import { FtrProviderContext } from '../ftr_provider_context';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import testSubjSelector from '@kbn/test-subj-selector';
import { subj as testSubjSelector } from '@kbn/test-subj-selector';
import { FtrProviderContext } from '../ftr_provider_context';
const TEST_SUBJ = Object.freeze({

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import testSubjSelector from '@kbn/test-subj-selector';
import { subj as testSubjSelector } from '@kbn/test-subj-selector';
import { FtrService } from '../../../functional/ftr_provider_context';
import { DATE_RANGE_OPTION_TO_TEST_SUBJ_MAP } from '../helpers/super_date_picker';

View file

@ -7761,6 +7761,10 @@
version "0.0.0"
uid ""
"@types/kbn__test-subj-selector@link:bazel-bin/packages/kbn-test-subj-selector/npm_module_types":
version "0.0.0"
uid ""
"@types/kbn__test@link:bazel-bin/packages/kbn-test/npm_module_types":
version "0.0.0"
uid ""