Skip to content

Commit

Permalink
AG-29014 Fix 'trusted-click-element' — element was removed and added …
Browse files Browse the repository at this point in the history
…again before it was clicked. #391

Squashed commit of the following:

commit bc53c30
Merge: 6c2150f 39e9fbf
Author: Adam Wróblewski <[email protected]>
Date:   Thu Nov 28 12:27:35 2024 +0100

    Merge branch 'master' into fix/AG-29014

commit 6c2150f
Author: Adam Wróblewski <[email protected]>
Date:   Tue Nov 26 12:20:03 2024 +0100

    Do not use destructuring assignment
    Change selectorText value to null

commit c273a04
Author: Adam Wróblewski <[email protected]>
Date:   Mon Nov 25 14:52:00 2024 +0100

    Update JSDoc for interface

commit 0cb9c15
Author: Adam Wróblewski <[email protected]>
Date:   Mon Nov 25 14:15:21 2024 +0100

    Add JSDoc for interface
    Remove properties from findAndClickElement JSDoc

commit 1432c5a
Author: Adam Wróblewski <[email protected]>
Date:   Mon Nov 25 10:26:48 2024 +0100

    Update changelog
    and comment

commit 7c65269
Author: Adam Wróblewski <[email protected]>
Date:   Fri Nov 22 18:59:46 2024 +0100

    Fix trusted-click-element
    Check if element is connected to Document
  • Loading branch information
AdamWr committed Nov 28, 2024
1 parent 39e9fbf commit eb2f95b
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 6 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
<!-- TODO: change `@added unknown` tag due to the actual version -->
<!-- during new scriptlets or redirects releasing -->

## [Unreleased]

### Fixed

- issue with `trusted-click-element` scriptlet when `delay` was used and the element was removed
and added again before it was clicked [#391]

[Unreleased]: https://github.com/AdguardTeam/Scriptlets/compare/v2.0.1...HEAD
[#391]: https://github.com/AdguardTeam/Scriptlets/issues/391

## [v2.0.1] - 2024-11-13

### Added
Expand Down
64 changes: 58 additions & 6 deletions src/scriptlets/trusted-click-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,27 @@ import { type Source } from './scriptlets';
* @added v1.7.3.
*/
/* eslint-enable max-len */

/**
* Object that contains information about element that can be clicked
*/
interface ElementObject {
/**
* HTML element to be clicked, or null if not found
*/
element: HTMLElement | null;

/**
* Indicates whether the element has been clicked
*/
clicked: boolean;

/**
* CSS selector text used to find the element
*/
selectorText: string | null;
}

export function trustedClickElement(
source: Source,
selectors: string,
Expand Down Expand Up @@ -313,14 +334,38 @@ export function trustedClickElement(
.split(SELECTORS_DELIMITER)
.map((selector) => selector.trim());

const createElementObj = (element: any): Object => {
const createElementObj = (element: any, selector?: string | null): Object => {
return {
element: element || null,
clicked: false,
selectorText: selector || null,
};
};
const elementsSequence = Array(selectorsSequence.length).fill(createElementObj(null));

/**
* Attempts to find and click an element based on the provided selector data.
*
* @param elementObj - Object containing element selector information
*
*/
const findAndClickElement = (elementObj: ElementObject): void => {
try {
if (!elementObj.selectorText) {
return;
}
const element = queryShadowSelector(elementObj.selectorText) as HTMLElement;
if (!element) {
logMessage(source, `Could not find element: '${elementObj.selectorText}'`);
return;
}
element.click();
elementObj.clicked = true;
} catch (error) {
logMessage(source, `Could not click element: '${elementObj.selectorText}'`);
}
};

// Flag indicating if the reload is set
let shouldReloadAfterClick: boolean = false;
// Value used for reload timing
Expand Down Expand Up @@ -388,8 +433,15 @@ export function trustedClickElement(
if (textMatchRegexp && !doesElementContainText(elementObj.element, textMatchRegexp)) {
continue;
}
elementObj.element.click();
elementObj.clicked = true;
// Checks if node is connected to a Document object,
// if not, try to find the element again
// https://github.com/AdguardTeam/Scriptlets/issues/391
if (elementObj.element.isConnected) {
elementObj.element.click();
elementObj.clicked = true;
} else {
findAndClickElement(elementObj);
}
}
}

Expand All @@ -407,8 +459,8 @@ export function trustedClickElement(
}
};

const handleElement = (element: Element, i: number) => {
const elementObj = createElementObj(element);
const handleElement = (element: Element, i: number, selector: string) => {
const elementObj = createElementObj(element, selector);
elementsSequence[i] = elementObj;

if (canClick) {
Expand All @@ -433,7 +485,7 @@ export function trustedClickElement(
return;
}

handleElement(element, i);
handleElement(element, i, selector);
fulfilledSelectors.push(selector);
});

Expand Down
30 changes: 30 additions & 0 deletions tests/scriptlets/trusted-click-element.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,36 @@ test('Element added to DOM is clicked', (assert) => {
}, 150);
});

test('Element added to DOM, removed and then added again - should be clicked', (assert) => {
const DELAY = 100;
const ELEM_COUNT = 1;
const ASSERTIONS = ELEM_COUNT + 1;
assert.expect(ASSERTIONS);

const done = assert.async();
const selectorsString = `#${PANEL_ID} > #${CLICKABLE_NAME}${ELEM_COUNT}`;

runScriptlet(name, [selectorsString, '', DELAY]);

const panelToRemove = createPanel();
const clickableToRemove = createClickable(1);
panelToRemove.appendChild(clickableToRemove);

let clickable;
setTimeout(() => {
removePanel();
const panel = createPanel();
clickable = createClickable(1);
panel.appendChild(clickable);
}, 10);

setTimeout(() => {
assert.ok(clickable.getAttribute('clicked'), 'Element should be clicked');
assert.strictEqual(window.hit, 'FIRED', 'hit func executed');
done();
}, 150);
});

test('Multiple elements clicked - one element loaded before scriptlet, rest added later', (assert) => {
const CLICK_ORDER = [1, 2, 3];
// Assert elements for being clicked, hit func execution & click order
Expand Down

0 comments on commit eb2f95b

Please sign in to comment.