Skip to content

Commit

Permalink
Feat: enhancing click checkpoint to cover focus dom event for form fi…
Browse files Browse the repository at this point in the history
…elds
  • Loading branch information
vdua committed Dec 12, 2024
1 parent 9d21c60 commit 9ecdb90
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
10 changes: 10 additions & 0 deletions modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,21 @@ function addViewMediaTracking(parent) {
}
}

function addFocusTracking(parent) {
parent.addEventListener('focusin', (event) => {
if (['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].includes(event.target.tagName)
|| event.target.getAttribute('contenteditable') === 'true') {
sampleRUM('click', { source: sourceSelector(event.target) });
}
});
}

function addFormTracking(parent) {
activateBlocksMO();
activateMediaMO();
parent.querySelectorAll('form').forEach((form) => {
form.addEventListener('submit', (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) }), { once: true });
addFocusTracking(form);
});
}

Expand Down
151 changes: 151 additions & 0 deletions test/it/focus.test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<html>

<head>
<title>Test Runner</title>
<script>
// we load from localhost, and have the ability to
// change the scripts that are being served. Check the
// web-test-runner.config.js file for details
window.RUM_BASE = window.origin;
window.hlx = {
RUM_MASK_URL: 'origin'
};
// we log what's being sent to the "server"
window.called = [];
// and navigator.sendBeacon has been replaced with
// a call to fakeSendBeacon
window.fakeSendBeacon = function (url, payload) {
// if payload is a string, we assume it's a JSON string
if (typeof payload === 'string') {
window.called.push(JSON.parse(payload));
} else {
// it's a blob
payload.text().then((text) => {
window.called.push(JSON.parse(text));
});
}
};

window.wait = async function (ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
</script>
<script defer type="text/javascript" src="/.rum/@adobe/helix-rum-js@^2/dist/rum-standalone.js"></script>
</head>

<body>
<div class="block" data-block-status="loaded">
The first block
<img src="/test/fixtures/fire.jpg" height="200" width="200">
</div>
<div id="focusable-element" tabindex="0">
Focusable element
</div>
<div id="editable-element-outside-form" contenteditable="true">
Editable element outside form
</div>
<input type="text" id="field-outside-form" name="field-outside-form" />
<form action="javascript:false;" method="POST">
<input type="text" name="name" value="John Doe">
<input type="email" name="email" value="[email protected]">
<button type="submit">Submit</button>
<div id="focusable-element-inside-form" tabindex="0">
Focusable element inside form
</div>
<div id="editable-element-inside-form" contenteditable="true">
Editable element inside form
</div>
</form>
<script type="module">
import { runTests } from '@web/test-runner-mocha';
import { sendMouse, setViewport } from '@web/test-runner-commands';
import { assert } from '@esm-bundle/chai';

runTests(async () => {
describe('Test Focus Tracking', () => {
beforeEach(async () => {
window.called = [];
});

it('can observe focus on form fields', async () => {
// wait for the adding focus event tracking
await window.wait(2000);

const form = document.querySelector('form');
form.querySelector('input[type="text"]').focus();
await window.wait(100);

assert(window.called.some((call) => call.checkpoint === 'click'), 'click checkpoint missing');
});

it('can not observe focus on elements outside of a form', async () => {
const focusableElement = document.querySelector('#focusable-element');
focusableElement.focus();
await window.wait(100);

const fieldOutsideForm = document.querySelector('#field-outside-form');
fieldOutsideForm.focus();
await window.wait(100);

const editableElementOutsideForm = document.querySelector('#editable-element-outside-form');
editableElementOutsideForm.focus();
await window.wait(100);

assert(window.called.every((call) => call.checkpoint !== 'click'), 'click checkpoint is present');
});

it('can not observe focus on non editable focusable elements', async () => {
const focusableElementInForm = document.querySelector('#focusable-element-inside-form');
focusableElementInForm.focus();
await window.wait(100);

assert(window.called.every((call) => call.checkpoint !== 'click'), 'click checkpoint is present');
});


it('can observe focus on contenteditable elements inside a form', async () => {
const editableElement = document.querySelector('#editable-element-inside-form');
editableElement.focus();
await window.wait(100);

assert(window.called.some((call) => call.checkpoint === 'click'), 'click checkpoint missing');
});

it('should send only one click checkpoint on click of a field', async () => {
const textInput = document.querySelector('form input[type="text"]');
const rect = textInput.getBoundingClientRect();
await sendMouse({
type: 'click',
position: [
Math.floor(rect.left + rect.width/2),
Math.floor(rect.top + rect.height/2)
]
});

await window.wait(100);

const clicks = window.called.filter((call) => call.checkpoint === 'click');
assert(clicks.length === 1, `click is called ${clicks.length} (Expected 1) times on a field`);
});

it('should send only one click checkpoint on click of a button', async () => {
const submitButton = document.querySelector('button');
const rect = submitButton.getBoundingClientRect();
await sendMouse({
type: 'click',
position: [
Math.floor(rect.left + rect.width/2),
Math.floor(rect.top + rect.height/2)
]
});

await window.wait(100);
const clicks = window.called.filter((call) => call.checkpoint === 'click');
assert(clicks.length === 1, `click is called ${clicks.length} (Expected 1) times on a button`);
});
});
});
</script>
</body>

0 comments on commit 9ecdb90

Please sign in to comment.