/** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // This file can't have dependencies, it is a part of the utility script. export type ParsedSelector = { parts: { name: string, body: string, }[], capture?: number, }; export function parseSelector(selector: string): ParsedSelector { let index = 0; let quote: string | undefined; let start = 0; const result: ParsedSelector = { parts: [] }; const append = () => { const part = selector.substring(start, index).trim(); const eqIndex = part.indexOf('='); let name: string; let body: string; if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[a-zA-Z_0-9-+:*]+$/)) { name = part.substring(0, eqIndex).trim(); body = part.substring(eqIndex + 1); } else if (part.length > 1 && part[0] === '"' && part[part.length - 1] === '"') { name = 'text'; body = part; } else if (part.length > 1 && part[0] === "'" && part[part.length - 1] === "'") { name = 'text'; body = part; } else if (/^\(*\/\//.test(part)) { // If selector starts with '//' or '//' prefixed with multiple opening // parenthesis, consider xpath. @see https://github.com/microsoft/playwright/issues/817 name = 'xpath'; body = part; } else { name = 'css'; body = part; } let capture = false; if (name[0] === '*') { capture = true; name = name.substring(1); } result.parts.push({ name, body }); if (capture) { if (result.capture !== undefined) throw new Error(`Only one of the selectors can capture using * modifier`); result.capture = result.parts.length - 1; } }; while (index < selector.length) { const c = selector[index]; if (c === '\\' && index + 1 < selector.length) { index += 2; } else if (c === quote) { quote = undefined; index++; } else if (!quote && (c === '"' || c === '\'' || c === '`')) { quote = c; index++; } else if (!quote && c === '>' && selector[index + 1] === '>') { append(); index += 2; start = index; } else { index++; } } append(); return result; }