Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -612,13 +612,16 @@ abstract class AbstractWebDriver(
@Throws(WebDriverException::class)
override suspend fun clickTextMatches(selector: String, pattern: String, count: Int) {
val safeSelector = Strings.escapeJsString(selector)
evaluate("__pulsar_utils__.clickTextMatches('$safeSelector', '$pattern')")
val safePattern = Strings.escapeJsString(pattern)
evaluate("__pulsar_utils__.clickTextMatches('$safeSelector', '$safePattern', $count)")
}

@Throws(WebDriverException::class)
override suspend fun clickMatches(selector: String, attrName: String, pattern: String, count: Int) {
val safeSelector = Strings.escapeJsString(selector)
evaluate("__pulsar_utils__.clickMatches('$safeSelector', '$attrName', '$pattern')")
val safeAttrName = Strings.escapeJsString(attrName)
val safePattern = Strings.escapeJsString(pattern)
evaluate("__pulsar_utils__.clickMatches('$safeSelector', '$safeAttrName', '$safePattern', $count)")
}

@Throws(WebDriverException::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,27 +615,83 @@ __pulsar_utils__.click = function(selector) {
* @param {String} pattern
* @return
*/
__pulsar_utils__.clickTextMatches = function(selector, pattern) {
/**
* Click elements matching selector whose text content matches pattern.
*
* @param {string} selector CSS selector
* @param {string} pattern Pattern to match text content (regex)
* @param {number} count Maximum number of elements to click (default: 1)
* @return {number} Number of elements clicked
*/
__pulsar_utils__.clickTextMatches = function(selector, pattern, count = 1) {
// TODO: handle selector `*`

let elements = document.querySelectorAll(selector)
for (let ele of elements) {
if (ele instanceof HTMLElement) {
let text = ele.textContent
if (text.match(pattern)) {
ele.scrollIntoView()
ele.click()
try {
let elements = document.querySelectorAll(selector)
let clicked = 0
let regex = new RegExp(pattern)

for (let ele of elements) {
if (clicked >= count) {
break
}
if (ele instanceof HTMLElement) {
let text = ele.textContent
if (regex.test(text)) {
ele.scrollIntoView()
ele.click()
clicked++
}
}
}
return clicked
} catch (e) {
console.error('Error in clickTextMatches with selector=' + selector + ', pattern=' + pattern + ':', e)
return 0
}
}

/**
* Select the first element and click it.
* Click elements matching selector whose attribute matches pattern.
*
* @param {number} n The n-th anchor.
* @param {string|null} rootSelector The n-th anchor.
* @return {string|null}
* @param {string} selector CSS selector
* @param {string} attrName Attribute name to match
* @param {string} pattern Pattern to match attribute value (regex)
* @param {number} count Maximum number of elements to click (default: 1)
* @return {number} Number of elements clicked
*/
__pulsar_utils__.clickMatches = function(selector, attrName, pattern, count = 1) {
try {
let elements = document.querySelectorAll(selector)
let clicked = 0
let regex = new RegExp(pattern)

for (let ele of elements) {
if (clicked >= count) {
break
}
if (ele instanceof HTMLElement) {
let attrValue = ele.getAttribute(attrName)
if (attrValue && regex.test(attrValue)) {
ele.scrollIntoView()
ele.click()
clicked++
}
}
}
return clicked
} catch (e) {
console.error('Error in clickMatches with selector=' + selector + ', attrName=' + attrName + ', pattern=' + pattern + ':', e)
return 0
}
}

/**
* Click the n-th anchor element within the specified root.
*
* @param {number} n The index of anchor to click (1-based).
* @param {string|null} rootSelector CSS selector of the root element.
* @return {string|null} The href of the clicked anchor, or null if not found.
*/
__pulsar_utils__.clickNthAnchor = function(n, rootSelector) {
let rootNode
Expand Down
118 changes: 118 additions & 0 deletions pulsar-tests-common/src/main/resources/static/assets/click-test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Click Functions Test Page</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.test-section {
margin: 20px 0;
padding: 15px;
border: 1px solid #ccc;
border-radius: 5px;
}
.test-button {
margin: 5px;
padding: 10px 15px;
cursor: pointer;
}
.result {
margin-top: 10px;
padding: 10px;
background-color: #f0f0f0;
border-radius: 3px;
min-height: 30px;
}
.clicked {
background-color: #90EE90 !important;
}
</style>
</head>
<body>
<h1>Click Functions Test Page</h1>

<div class="test-section">
<h2>Test clickTextMatches</h2>
<button class="test-button" onclick="handleClick(this, 'text-1')">Submit Form</button>
<button class="test-button" onclick="handleClick(this, 'text-2')">Submit Data</button>
<button class="test-button" onclick="handleClick(this, 'text-3')">Cancel</button>
<button class="test-button" onclick="handleClick(this, 'text-4')">Reset Form</button>
<div id="text-result" class="result">No clicks yet</div>
</div>

<div class="test-section">
<h2>Test clickMatches (attribute matching)</h2>
<button class="test-button" data-action="save" onclick="handleClick(this, 'attr-1')">Save</button>
<button class="test-button" data-action="save-draft" onclick="handleClick(this, 'attr-2')">Save Draft</button>
<button class="test-button" data-action="delete" onclick="handleClick(this, 'attr-3')">Delete</button>
<button class="test-button" data-action="archive" onclick="handleClick(this, 'attr-4')">Archive</button>
<div id="attr-result" class="result">No clicks yet</div>
</div>

<div class="test-section">
<h2>Test clickNthAnchor</h2>
<a href="#link1" onclick="handleClick(this, 'anchor-1'); return false;">First Link</a><br>
<a href="#link2" onclick="handleClick(this, 'anchor-2'); return false;">Second Link</a><br>
<a href="#link3" onclick="handleClick(this, 'anchor-3'); return false;">Third Link</a><br>
<a href="#link4" onclick="handleClick(this, 'anchor-4'); return false;">Fourth Link</a><br>
<a href="#link5" onclick="handleClick(this, 'anchor-5'); return false;">Fifth Link</a><br>
<div id="anchor-result" class="result">No clicks yet</div>
</div>

<script>
let clickLog = [];

function handleClick(element, id) {
element.classList.add('clicked');
const timestamp = new Date().toISOString();
const entry = {
id: id,
timestamp: timestamp,
text: element.textContent || element.innerText,
tag: element.tagName.toLowerCase()
};
clickLog.push(entry);

// Update the appropriate result div
let resultDiv;
if (id.startsWith('text-')) {
resultDiv = document.getElementById('text-result');
} else if (id.startsWith('attr-')) {
resultDiv = document.getElementById('attr-result');
} else if (id.startsWith('anchor-')) {
resultDiv = document.getElementById('anchor-result');
}

if (resultDiv) {
resultDiv.textContent = `Clicked: ${entry.text} (${entry.id}) at ${timestamp}`;
}

console.log('Click logged:', entry);
console.log('Total clicks:', clickLog.length);
}

// Expose clickLog for testing
window.getClickLog = function() {
return clickLog;
};

window.getLastClick = function() {
return clickLog[clickLog.length - 1];
};

window.clearClickLog = function() {
clickLog = [];
document.querySelectorAll('.result').forEach(div => {
div.textContent = 'No clicks yet';
});
document.querySelectorAll('.clicked').forEach(el => {
el.classList.remove('clicked');
});
};
</script>
</body>
</html>
Loading