testcafe-selector-generator
Version:
93 lines (74 loc) • 3.64 kB
JavaScript
import TESTCAFE_CORE from '../../deps/testcafe-core';
import {
escapeSpecifiedSymbols, escapeValueForSelectorWithRegExp, escapeAttrValue,
} from '../utils/escape';
import { SelectorDescriptor } from '../../selector-descriptor';
import { FilterOption } from '../../selector-descriptor/filter-option';
import * as FILTER_OPTION_TYPE from '../../selector-descriptor/filter-option-type';
import { getAttributeRegExpFilter } from '../../selector-descriptor/filters';
const { domUtils, arrayUtils } = TESTCAFE_CORE;
const ASP_AUTOGENERATED_ATTR_RE = /\$ctl\d+|ctl\d+\$|_ctl\d+|ctl\d+_|^ctl\d+$/g;
const SEPARATOR_CONST = '!!!!!separator!!!!!';
const ANY_NUMBER_CONST = '!!!!!anyNumber!!!!!';
const SEPARATOR_RE = new RegExp(SEPARATOR_CONST, 'g');
const ANY_NUMBER_RE = new RegExp(ANY_NUMBER_CONST, 'g');
const SOME_SPACES_RE = /\s{2,}/g;
const MAX_TEXT_LENGTH_IN_SELECTOR = 50;
export const CLASS_ATTRIBUTE_NAME = 'class';
// eslint-disable-next-line max-params
export function getRegExpAttributesDescriptor (ruleType, el, cssSelector, filterRegExpValue, ancestorSelectorDescriptor) {
return new SelectorDescriptor({
ancestorSelectorDescriptor: ancestorSelectorDescriptor,
cssSelector: cssSelector,
element: el,
filter: getAttributeRegExpFilter(filterRegExpValue),
filterOptions: new FilterOption(FILTER_OPTION_TYPE.byAttr, filterRegExpValue),
ruleType: ruleType,
});
}
// eslint-disable-next-line max-lines-per-function, max-params
export function getAttributesDescriptor (ruleType, el, attributes, ancestorSelectorDescriptor) {
const tagName = domUtils.getTagName(el);
let cssSelector = '';
let attrRegExpObject = null;
arrayUtils.forEach(attributes, ({ name, value }) => {
let valueWasCut = false;
let shouldUseRegExp = false;
if (value.replace(SOME_SPACES_RE, ' ').length > MAX_TEXT_LENGTH_IN_SELECTOR) {
value = value.substr(0, MAX_TEXT_LENGTH_IN_SELECTOR);
valueWasCut = true;
}
if (ASP_AUTOGENERATED_ATTR_RE.test(value)) {
shouldUseRegExp = true;
value = value.replace(ASP_AUTOGENERATED_ATTR_RE, substr => substr.replace(/\d+/, ANY_NUMBER_CONST));
}
if (SOME_SPACES_RE.test(value)) {
shouldUseRegExp = true;
value = value.replace(SOME_SPACES_RE, SEPARATOR_CONST);
}
if (shouldUseRegExp) {
if (!attrRegExpObject) {
value = escapeValueForSelectorWithRegExp(value);
value = value.replace(ANY_NUMBER_RE, '\\d+').replace(SEPARATOR_RE, '\\s+');
attrRegExpObject = { attrName: name, attrValueRe: new RegExp(value) };
}
}
else {
value = name === CLASS_ATTRIBUTE_NAME ? escapeSpecifiedSymbols(value).trimEnd() : escapeAttrValue(value);
if (name === CLASS_ATTRIBUTE_NAME && !valueWasCut)
cssSelector += (' ' + value).replace(/\s+/g, '.');
else
cssSelector += `[${name}${valueWasCut ? '^' : ''}${value ? `="${value}"` : ''}]`;
}
});
if (attrRegExpObject) {
const selector = cssSelector || tagName;
return getRegExpAttributesDescriptor(ruleType, el, selector, attrRegExpObject, ancestorSelectorDescriptor);
}
return new SelectorDescriptor({
ruleType,
element: el,
ancestorSelectorDescriptor,
cssSelector,
});
}