Skip to content

Commit 1e3b086

Browse files
author
DavertMik
committed
added aria elements
1 parent 799c9f0 commit 1e3b086

File tree

2 files changed

+252
-0
lines changed

2 files changed

+252
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import Locator from '../../locator.js'
2+
3+
function buildLocatorString(locator) {
4+
if (locator.isCustom()) {
5+
return `${locator.type}=${locator.value}`
6+
}
7+
if (locator.isXPath()) {
8+
return `xpath=${locator.value}`
9+
}
10+
return locator.simplify()
11+
}
12+
13+
async function findElements(matcher, locator) {
14+
if (locator.react) return findReact(matcher, locator)
15+
if (locator.vue) return findVue(matcher, locator)
16+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
17+
if (locator.role) return findByRole(matcher, locator)
18+
locator = new Locator(locator, 'css')
19+
20+
return matcher.locator(buildLocatorString(locator)).all()
21+
}
22+
23+
async function findElement(matcher, locator) {
24+
if (locator.react) return findReact(matcher, locator)
25+
if (locator.vue) return findVue(matcher, locator)
26+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
27+
if (locator.role) return findByRole(matcher, locator)
28+
locator = new Locator(locator, 'css')
29+
30+
return matcher.locator(buildLocatorString(locator)).first()
31+
}
32+
33+
async function getVisibleElements(elements) {
34+
const visibleElements = []
35+
for (const element of elements) {
36+
if (await element.isVisible()) {
37+
visibleElements.push(element)
38+
}
39+
}
40+
if (visibleElements.length === 0) {
41+
return elements
42+
}
43+
return visibleElements
44+
}
45+
46+
async function findReact(matcher, locator) {
47+
let _locator = `_react=${locator.react}`
48+
let props = ''
49+
50+
if (locator.props) {
51+
props += propBuilder(locator.props)
52+
_locator += props
53+
}
54+
return matcher.locator(_locator).all()
55+
}
56+
57+
async function findVue(matcher, locator) {
58+
let _locator = `_vue=${locator.vue}`
59+
let props = ''
60+
61+
if (locator.props) {
62+
props += propBuilder(locator.props)
63+
_locator += props
64+
}
65+
return matcher.locator(_locator).all()
66+
}
67+
68+
async function findByPlaywrightLocator(matcher, locator) {
69+
if (locator && locator.toString().includes(process.env.testIdAttribute)) return matcher.getByTestId(locator.pw.value.split('=')[1])
70+
return matcher.locator(locator.pw).all()
71+
}
72+
73+
async function findByRole(matcher, locator) {
74+
const role = locator.role
75+
76+
if (!locator.text) {
77+
const roleOptions = {}
78+
if (locator.includeHidden !== undefined) roleOptions.includeHidden = locator.includeHidden
79+
return matcher.getByRole(role, roleOptions).all()
80+
}
81+
82+
const allElements = await matcher.getByRole(role, locator.includeHidden !== undefined ? { includeHidden: locator.includeHidden } : {}).all()
83+
84+
if (locator.exact === true) {
85+
const filtered = []
86+
for (const el of allElements) {
87+
const [accessibleName, placeholder, innerText] = await el.evaluate(element => {
88+
const getAccessibleName = () => {
89+
if (element.hasAttribute('aria-label')) {
90+
return element.getAttribute('aria-label')
91+
}
92+
if (element.id) {
93+
const label = document.querySelector(`label[for="${element.id}"]`)
94+
if (label) return label.textContent.trim()
95+
}
96+
return ''
97+
}
98+
return [getAccessibleName(), element.getAttribute('placeholder') || '', element.innerText ? element.innerText.trim() : '']
99+
})
100+
101+
if (accessibleName === locator.text || placeholder === locator.text || innerText === locator.text) {
102+
filtered.push(el)
103+
}
104+
}
105+
return filtered
106+
}
107+
108+
const filtered = []
109+
for (const el of allElements) {
110+
const [accessibleName, placeholder, innerText] = await el.evaluate(element => {
111+
const getAccessibleName = () => {
112+
if (element.hasAttribute('aria-label')) {
113+
return element.getAttribute('aria-label')
114+
}
115+
if (element.id) {
116+
const label = document.querySelector(`label[for="${element.id}"]`)
117+
if (label) return label.textContent.trim()
118+
}
119+
return ''
120+
}
121+
return [getAccessibleName(), element.getAttribute('placeholder') || '', element.innerText ? element.innerText.trim() : '']
122+
})
123+
124+
if ((accessibleName && accessibleName.includes(locator.text)) || (placeholder && placeholder.includes(locator.text)) || (innerText && innerText.includes(locator.text))) {
125+
filtered.push(el)
126+
}
127+
}
128+
return filtered
129+
}
130+
131+
function propBuilder(props) {
132+
let _props = ''
133+
134+
for (const [key, value] of Object.entries(props)) {
135+
if (typeof value === 'object') {
136+
for (const [k, v] of Object.entries(value)) {
137+
_props += `[${key}.${k} = "${v}"]`
138+
}
139+
} else {
140+
_props += `[${key} = "${value}"]`
141+
}
142+
}
143+
return _props
144+
}
145+
146+
export { buildLocatorString, findElements, findElement, getVisibleElements, findReact, findVue, findByPlaywrightLocator, findByRole }
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Role Elements Test</title>
5+
<style>
6+
body { font-family: Arial, sans-serif; padding: 20px; }
7+
.form-group { margin: 15px 0; }
8+
label { display: block; margin-bottom: 5px; font-weight: bold; }
9+
input, button, select { padding: 8px; margin: 5px 0; }
10+
.result { margin-top: 20px; padding: 10px; background: #f0f0f0; }
11+
</style>
12+
</head>
13+
<body>
14+
<h1>Role Elements Test Page</h1>
15+
16+
<form action="/form/role_elements" method="POST">
17+
<div class="form-group">
18+
<label for="title-input">Title Search:</label>
19+
<input id="title-input" name="title" role="combobox" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" placeholder="Title" type="search">
20+
</div>
21+
22+
<div class="form-group">
23+
<label for="name-input">Name Search:</label>
24+
<input id="name-input" name="name" role="combobox" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" placeholder="Name" type="search">
25+
</div>
26+
27+
<div class="form-group">
28+
<label for="category-input">Category Search:</label>
29+
<input id="category-input" name="category" role="combobox" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" placeholder="Category" type="search">
30+
</div>
31+
32+
<div class="form-group">
33+
<label for="search-input">General Search:</label>
34+
<input id="search-input" name="search" role="searchbox" placeholder="Search..." type="search">
35+
</div>
36+
37+
<div class="form-group">
38+
<label for="email-input">Email:</label>
39+
<input id="email-input" name="email" role="textbox" type="email" placeholder="your@email.com">
40+
</div>
41+
42+
<div class="form-group">
43+
<label for="message-text">Message:</label>
44+
<textarea id="message-text" name="message" role="textbox" placeholder="Enter your message"></textarea>
45+
</div>
46+
47+
<div class="form-group">
48+
<button name="submit" role="button" type="submit">Submit Form</button>
49+
<button name="cancel" role="button" type="button">Cancel</button>
50+
<button name="reset" role="button" type="reset">Reset</button>
51+
</div>
52+
53+
<div class="form-group">
54+
<label for="country-select">Country:</label>
55+
<select id="country-select" name="country" role="combobox">
56+
<option value="">Select a country</option>
57+
<option value="us">United States</option>
58+
<option value="uk">United Kingdom</option>
59+
<option value="ca">Canada</option>
60+
<option value="au">Australia</option>
61+
</select>
62+
</div>
63+
64+
<div class="form-group">
65+
<input name="newsletter" role="checkbox" type="checkbox" id="newsletter-checkbox">
66+
<label for="newsletter-checkbox">Subscribe to newsletter</label>
67+
</div>
68+
69+
<div class="form-group">
70+
<input name="terms" role="checkbox" type="checkbox" id="terms-checkbox">
71+
<label for="terms-checkbox">I agree to the terms and conditions</label>
72+
</div>
73+
</form>
74+
75+
<div class="result" id="result" style="display: none;">
76+
<h3>Form Submitted!</h3>
77+
<p id="result-text"></p>
78+
</div>
79+
80+
<script>
81+
document.querySelector('form').addEventListener('submit', function(e) {
82+
e.preventDefault();
83+
const resultDiv = document.getElementById('result');
84+
const resultText = document.getElementById('result-text');
85+
86+
const formData = new FormData(this);
87+
let result = 'Form data submitted:<br>';
88+
for (let [key, value] of formData.entries()) {
89+
if (value) result += `${key}: ${value}<br>`;
90+
}
91+
92+
resultText.innerHTML = result;
93+
resultDiv.style.display = 'block';
94+
});
95+
96+
document.querySelector('button[name="cancel"]').addEventListener('click', function() {
97+
document.querySelector('form').reset();
98+
document.getElementById('result').style.display = 'none';
99+
});
100+
101+
document.querySelector('button[name="reset"]').addEventListener('click', function() {
102+
document.getElementById('result').style.display = 'none';
103+
});
104+
</script>
105+
</body>
106+
</html>

0 commit comments

Comments
 (0)