2024-10-16 00:21:45 +02:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import type { AriaTemplateNode } from './injected/ariaSnapshot';
|
|
|
|
|
import { yaml } from '../utilsBundle';
|
2024-10-19 05:18:18 +02:00
|
|
|
import type { AriaRole } from '@injected/roleUtils';
|
2024-10-16 00:21:45 +02:00
|
|
|
|
|
|
|
|
export function parseAriaSnapshot(text: string): AriaTemplateNode {
|
2024-10-19 05:18:18 +02:00
|
|
|
const fragment = yaml.parse(text) as any[];
|
|
|
|
|
const result: AriaTemplateNode = { role: 'fragment' };
|
|
|
|
|
populateNode(result, fragment);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2024-10-16 00:21:45 +02:00
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
function populateNode(node: AriaTemplateNode, container: any[]) {
|
|
|
|
|
for (const object of container) {
|
|
|
|
|
if (typeof object === 'string') {
|
|
|
|
|
const { role, name } = parseKey(object);
|
|
|
|
|
node.children = node.children || [];
|
|
|
|
|
node.children.push({ role, name });
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (const key of Object.keys(object)) {
|
|
|
|
|
if (key === 'checked') {
|
|
|
|
|
node.checked = object[key];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (key === 'disabled') {
|
|
|
|
|
node.disabled = object[key];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (key === 'expanded') {
|
|
|
|
|
node.expanded = object[key];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (key === 'level') {
|
|
|
|
|
node.level = object[key];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (key === 'pressed') {
|
|
|
|
|
node.pressed = object[key];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (key === 'selected') {
|
|
|
|
|
node.selected = object[key];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-10-16 00:21:45 +02:00
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
const { role, name } = parseKey(key);
|
|
|
|
|
const value = object[key];
|
|
|
|
|
node.children = node.children || [];
|
2024-10-16 00:21:45 +02:00
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
if (role === 'text') {
|
|
|
|
|
node.children.push(valueOrRegex(value));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-10-16 00:21:45 +02:00
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
node.children.push({ role, name, children: [valueOrRegex(value)] });
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-10-16 00:21:45 +02:00
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
const childNode = { role, name };
|
|
|
|
|
node.children.push(childNode);
|
|
|
|
|
populateNode(childNode, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-16 00:21:45 +02:00
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
function parseKey(key: string) {
|
|
|
|
|
const match = key.match(/^([a-z]+)(?:\s+(?:"([^"]*)"|\/([^\/]*)\/))?$/);
|
|
|
|
|
if (!match)
|
|
|
|
|
throw new Error(`Invalid key ${key}`);
|
2024-10-18 02:06:18 +02:00
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
const role = match[1] as AriaRole | 'text';
|
|
|
|
|
if (match[2])
|
|
|
|
|
return { role, name: match[2] };
|
|
|
|
|
if (match[3])
|
|
|
|
|
return { role, name: new RegExp(match[3]) };
|
|
|
|
|
return { role };
|
|
|
|
|
}
|
2024-10-16 00:21:45 +02:00
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
function normalizeWhitespace(text: string) {
|
|
|
|
|
return text.replace(/[\r\n\s\t]+/g, ' ').trim();
|
2024-10-16 00:21:45 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-19 05:18:18 +02:00
|
|
|
function valueOrRegex(value: string): string | RegExp {
|
|
|
|
|
return value.startsWith('/') && value.endsWith('/') ? new RegExp(value.slice(1, -1)) : normalizeWhitespace(value);
|
|
|
|
|
}
|