From b22ebaf08e6aeb634738664e4b7391125a8ab4af Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Wed, 26 Jun 2024 13:10:55 +0200 Subject: [PATCH 01/22] docs(readme): describe how source and sourceSelector are supposed to work --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index 07f295a..4e626bd 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,45 @@ It will add following new checkpoints: - `formsubmit`: when a form is submitted - `leave`: when the user leaves the page +### The `source` parameter and the `sourceSelector` + +The `source` parameter is a string that can be used to identify the source of the event. It can be used to identify the source of the event, e.g. a button, a link, a form, etc. +It represents an idealized CSS selector that is both human-readable and specific enough to identify the source of the event in the document, even when not having access to the +orginal document. It is idealized because it pretends the DOM would use modern HTML with concise semantics, even if the actual document uses `class` values for things that would +be better represented by semantic HTML elements. + +The `sourceSelector` function is a function that takes a DOM element and returns a descriptive `source` parameter. If the element has a `data-rum-source` attribute, its value is used as the `source` parameter. Otherwise, the function tries to generate a `source` parameter based on the element's tag name, class names, and text content. + +The structure of the `source` parameter is as follows: + +``` + # +``` +All three parts are optional + +`context` is +- `form` for form elements +- `dialog` for dialog elements, or parent containers that are fixed positioned and have a positive high z-index +- `.block-name` for Helix blocks + +`element` is +- `button` for buttons, or links that look like buttons (e.g. with a class `button` or `btn` or `cta`) +- `img` for images +- `video` for videos +- `a` for links that are not buttons +- `input[type="text"]` for input elements (all types are supported) +- `select`, `textarea`, etc. for other form elements + +`identifier` is +- the `id` attribute of the element, if provided + +Even if an `identifier` is provided, having a `context` and `element` is recommended, as it makes the `source` parameter more readable and easier to understand. + + +#### Examples + +- `` + ## Development ### Build From b007e4e9112ec42924adbd8378229ab58e82e3d0 Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Wed, 3 Jul 2024 16:56:13 +0200 Subject: [PATCH 02/22] docs(readme): add more site structure --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4e626bd..57bbf4c 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ All three parts are optional - `form` for form elements - `dialog` for dialog elements, or parent containers that are fixed positioned and have a positive high z-index - `.block-name` for Helix blocks +- `header`, `footer`, `nav`, `aside` for main site structure `element` is - `button` for buttons, or links that look like buttons (e.g. with a class `button` or `btn` or `cta`) From 629f8d5e02734adfcb1b5606f1800d7764d3eb65 Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Wed, 3 Jul 2024 16:57:10 +0200 Subject: [PATCH 03/22] docs(readme): more source selector details --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4e626bd..b73e2e9 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ All three parts are optional `identifier` is - the `id` attribute of the element, if provided +- the first `.class` if there are any +- else omitted Even if an `identifier` is provided, having a `context` and `element` is recommended, as it makes the `source` parameter more readable and easier to understand. From ada279d423590fb3feeb304b3a57d6da4ee3c9bf Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Wed, 3 Jul 2024 17:24:40 +0200 Subject: [PATCH 04/22] feat(dom): more fine-grained sourceselector implementation --- README.md | 1 + modules/dom.js | 91 ++++++++++++------- .../unit/dom.sourceSelector.buttons.test.html | 20 ++-- test/unit/dom.sourceSelector.test.html | 12 +-- 4 files changed, 73 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index f539f30..7497ea3 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ All three parts are optional - `dialog` for dialog elements, or parent containers that are fixed positioned and have a positive high z-index - `.block-name` for Helix blocks - `header`, `footer`, `nav`, `aside` for main site structure +- `#id` as a fallback, if a container ID is available `element` is - `button` for buttons, or links that look like buttons (e.g. with a class `button` or `btn` or `cta`) diff --git a/modules/dom.js b/modules/dom.js index 8fa86da..542a604 100644 --- a/modules/dom.js +++ b/modules/dom.js @@ -30,6 +30,58 @@ export const targetSelector = (element) => { } }; +function walk(element, checkFn) { + if (!element || element === document.body || element === document.documentElement) { + return undefined; + } + const checkValue = checkFn(element); + return checkValue || walk(element.parentElement, checkFn); +} + +function isFakeDialog(element) { + // doing it well + if (element.tagName === 'DIALOG') return true; + // making the best of it + if (element.getAttribute('role') === 'dialog') return true; + if (element.getAttribute('role') === 'alertdialog') return true; + if (element.getAttribute('aria-modal') === 'true') return true; + // doing it wrong + const computedStyle = window.getComputedStyle(element); + return (computedStyle && computedStyle.position === 'fixed' && computedStyle.zIndex > 100); +} + +function isFakeButton(element) { + if (element.tagName === 'BUTTON') return true; + if (element.tagName === 'INPUT' && element.getAttribute('type') === 'button') return true; + if (element.tagName === 'A') { + const classes = Array.from(element.classList); + return classes.some((className) => className.match(/button|cta/)); + } + return element.getAttribute('role') === 'button'; +} + +function getSourceContext(element) { + if (element.closest('form')) return 'form'; + if (element.closest('.block')) return `.${element.closest('.block').getAttribute('data-block-name')}`; + if (walk(element, isFakeDialog)) return 'dialog'; + if (element.closest('nav')) return 'nav'; + if (element.closest('header')) return 'header'; + if (element.closest('footer')) return 'footer'; + if (element.closest('aside')) return 'aside'; + return (walk(element, (e) => e.id && `#${e.id}`)); +} + +function getSourceElement(element) { + if (element.closest('form') && Array.from(element.closest('form').elements).includes(element)) return element.tagName.toLowerCase() + (element.tagName === 'INPUT' ? `[type='${element.getAttribute('type')}']` : ''); + if (walk(element, isFakeButton)) return 'button'; + return element.tagName.toLowerCase().match(/^(a|img|video)$/) && element.tagName.toLowerCase(); +} + +function getSourceIdentifier(element) { + if (element.id) return `#${element.id}`; + if (element.getAttribute('data-block-name')) return `.${element.getAttribute('data-block-name')}`; + return (element.classList.length > 0 && `.${element.classList[0]}`); +} export const sourceSelector = (element) => { try { if (!element || element === document.body || element === document.documentElement) { @@ -38,41 +90,10 @@ export const sourceSelector = (element) => { if (element.getAttribute('data-rum-source')) { return element.getAttribute('data-rum-source'); } - const form = element.closest('form'); - let formElementSelector = ''; - if (form && Array.from(form.elements).includes(element)) { - formElementSelector = element.tagName === 'INPUT' ? `form input[type='${element.getAttribute('type')}']` : `form ${element.tagName.toLowerCase()}`; - } - - const blockName = element.closest('.block') ? element.closest('.block').getAttribute('data-block-name') : ''; - if (element.id || formElementSelector) { - const id = element.id ? `#${element.id}` : ''; - return blockName ? `.${blockName} ${formElementSelector}${id}` : `${formElementSelector}${id}`; - } - - if (element.getAttribute('data-block-name')) { - return `.${element.getAttribute('data-block-name')}`; - } - - const classes = Array.from(element.classList); - const label = element.tagName.toLowerCase(); - const firstClass = classes.length > 0 ? `.${classes[0]}` : ''; - const labelWithClass = `${element.tagName.toLowerCase()}${firstClass}`; - if (element.tagName.toLowerCase() === 'button' - || element.type === 'button' - || classes.some((className) => className.match(/button|cta/))) { - let parent = element.parentElement; - if (!parent) return labelWithClass; - if (parent.id) return `#${parent.id} ${label}`; - while (parent.tagName !== 'BODY' && !parent.id) parent = parent.parentElement; - if (parent.id) return `#${parent.id} ${labelWithClass}`; - return blockName ? `.${blockName} ${labelWithClass}` : labelWithClass; - } - - const parent = sourceSelector(element.parentElement); - if (parent) return parent; - - return labelWithClass; + const context = getSourceContext(element.parentElement) || ''; + const elementName = getSourceElement(element) || ''; + const identifier = getSourceIdentifier(element) || ''; + return `${context} ${elementName}${identifier}`.trim() || `"${element.textContent.substring(0, 10)}"`; /* c8 ignore next 3 */ } catch (error) { return null; diff --git a/test/unit/dom.sourceSelector.buttons.test.html b/test/unit/dom.sourceSelector.buttons.test.html index 13bb0ce..c3a2354 100644 --- a/test/unit/dom.sourceSelector.buttons.test.html +++ b/test/unit/dom.sourceSelector.buttons.test.html @@ -54,26 +54,26 @@ runTests(async () => { describe('dom#sourceSelector', () => { it('sourceSelector - buttons', () => { - expect(sourceSelector(document.querySelector('#reject-all-handler'))).to.be.equal('#reject-all-handler'); - expect(sourceSelector(document.querySelector('#accept-btn-handler'))).to.be.equal('#accept-btn-handler'); + expect(sourceSelector(document.querySelector('#reject-all-handler'))).to.be.equal('#test button#reject-all-handler'); + expect(sourceSelector(document.querySelector('#accept-btn-handler'))).to.be.equal('#test button#accept-btn-handler'); - expect(sourceSelector(document.querySelector('.close-btn-handler'))).to.be.equal('#close-btn-container button'); + expect(sourceSelector(document.querySelector('.close-btn-handler'))).to.be.equal('#close-btn-container button.close-btn-handler'); expect(sourceSelector(document.querySelector('.orphan-btn-handler'))).to.be.equal('#test button.orphan-btn-handler'); - expect(sourceSelector(document.querySelector('.cta-1'))).to.be.equal('#cta-container input'); + expect(sourceSelector(document.querySelector('.cta-1'))).to.be.equal('#cta-container button.cta'); expect(sourceSelector(document.querySelector('.orphan'))).to.be.equal('button.orphan'); expect(sourceSelector(document.querySelector('.a-btn'))).to.be.equal('button.a-btn'); - expect(sourceSelector(document.querySelector('.a-btn-2'))).to.be.equal('#a-btn-container button'); + expect(sourceSelector(document.querySelector('.a-btn-2'))).to.be.equal('#a-btn-container button.a-btn-2'); - expect(sourceSelector(document.querySelector('.an-input'))).to.be.equal('input.an-input'); - expect(sourceSelector(document.querySelector('.an-input-2'))).to.be.equal('#an-input-container input'); + expect(sourceSelector(document.querySelector('.an-input'))).to.be.equal('button.an-input'); + expect(sourceSelector(document.querySelector('.an-input-2'))).to.be.equal('#an-input-container button.an-input-2'); - expect(sourceSelector(document.querySelector('a.cta'))).to.be.equal('#an-a-container a'); - expect(sourceSelector(document.querySelector('a.button'))).to.be.equal('#an-a-container-2 a'); + expect(sourceSelector(document.querySelector('a.cta'))).to.be.equal('#an-a-container button.cta'); + expect(sourceSelector(document.querySelector('a.button'))).to.be.equal('#an-a-container-2 button.button'); - expect(sourceSelector(document.querySelector('span.cta'))).to.be.equal('.a-block span.cta'); + expect(sourceSelector(document.querySelector('span.cta'))).to.be.equal('.a-block .cta'); }); }); }); diff --git a/test/unit/dom.sourceSelector.test.html b/test/unit/dom.sourceSelector.test.html index 7e94a1a..13b02cf 100644 --- a/test/unit/dom.sourceSelector.test.html +++ b/test/unit/dom.sourceSelector.test.html @@ -33,16 +33,16 @@ describe('dom#sourceSelector', () => { it('sourceSelector - generic', () => { expect(sourceSelector(document.querySelector('a.one'))).to.be.equal('a.one'); - expect(sourceSelector(document.querySelector('body > div:first-of-type'))).to.be.equal('div'); - expect(sourceSelector(document.querySelector('div.with-one-class'))).to.be.equal('div.with-one-class'); - expect(sourceSelector(document.querySelector('div.with-classes'))).to.be.equal('div.with-classes'); + expect(sourceSelector(document.querySelector('body > div:first-of-type'))).to.be.equal('"a div"'); + expect(sourceSelector(document.querySelector('div.with-one-class'))).to.be.equal('.with-one-class'); + expect(sourceSelector(document.querySelector('div.with-classes'))).to.be.equal('.with-classes'); expect(sourceSelector(document.querySelector('div.multi-1'))).to.be.equal('has-priority'); expect(sourceSelector(document.querySelector('div.multi-2'))).to.be.equal('has-priority-too'); expect(sourceSelector(document.querySelector('div.multi-3'))).to.be.equal('#id-has-priority'); - expect(sourceSelector(document.querySelector('input[type="text"'))).to.be.equal('form input[type=\'text\']'); - expect(sourceSelector(document.querySelector('input[type="checkbox"'))).to.be.equal('form input[type=\'checkbox\']'); - expect(sourceSelector(document.querySelector('textarea'))).to.be.equal('form textarea'); + expect(sourceSelector(document.querySelector('input[type="text"'))).to.be.equal('form input[type=\'text\'].a-text'); + expect(sourceSelector(document.querySelector('input[type="checkbox"'))).to.be.equal('form input[type=\'checkbox\'].a-checkbox'); + expect(sourceSelector(document.querySelector('textarea'))).to.be.equal('form textarea.a-textarea'); expect(sourceSelector(document.querySelector('.a-block'))).to.be.equal('.a-block'); expect(sourceSelector(document.querySelector('#a-div-in-block'))).to.be.equal('.a-block #a-div-in-block'); From 440473ea92fcd281eacad5a14478d3d4751a3c1a Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Tue, 16 Jul 2024 14:48:07 +0200 Subject: [PATCH 05/22] feat(onetrust): detect supressed consent banner --- modules/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/index.js b/modules/index.js index 19e061a..9d77739 100644 --- a/modules/index.js +++ b/modules/index.js @@ -275,8 +275,13 @@ function addCookieConsentTracking() { let consentMutationObserver; const trackShowConsent = () => { - if (document.querySelector('body > div#onetrust-consent-sdk')) { - sampleRUM('consent', { source: 'onetrust', target: 'show' }); + const otsdk = document.querySelector('body > div#onetrust-consent-sdk'); + if (otsdk) { + if (otsdk.checkVisibility && !otsdk.checkVisibility()) { + sampleRUM('consent', { source: 'onetrust', target: 'supressed' }); + } else { + sampleRUM('consent', { source: 'onetrust', target: 'show' }); + } if (consentMutationObserver) { consentMutationObserver.disconnect(); } From 1279aca5ab997b33428766a9c552cbde7007804a Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Tue, 16 Jul 2024 14:50:55 +0200 Subject: [PATCH 06/22] fix(consent): spelling --- modules/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/index.js b/modules/index.js index 9d77739..f90c38f 100644 --- a/modules/index.js +++ b/modules/index.js @@ -278,7 +278,7 @@ function addCookieConsentTracking() { const otsdk = document.querySelector('body > div#onetrust-consent-sdk'); if (otsdk) { if (otsdk.checkVisibility && !otsdk.checkVisibility()) { - sampleRUM('consent', { source: 'onetrust', target: 'supressed' }); + sampleRUM('consent', { source: 'onetrust', target: 'suppressed' }); } else { sampleRUM('consent', { source: 'onetrust', target: 'show' }); } From c80e1d06a26ed0057a9e1418bbe97769ea083731 Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Thu, 15 Aug 2024 14:29:58 +0200 Subject: [PATCH 07/22] test(onetrust): test with onetrust banner forcibly hidden --- test/it/onetrust-suppressed.test.html | 322 ++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 test/it/onetrust-suppressed.test.html diff --git a/test/it/onetrust-suppressed.test.html b/test/it/onetrust-suppressed.test.html new file mode 100644 index 0000000..b1ed5ab --- /dev/null +++ b/test/it/onetrust-suppressed.test.html @@ -0,0 +1,322 @@ + + + + Test Runner + + + + + + + + + + \ No newline at end of file From c9c53f1f9b4c803eda71d8d6005485f75bc807fd Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Tue, 17 Sep 2024 16:36:29 +0200 Subject: [PATCH 08/22] test(integration): adjust some newer source selector values --- test/it/viewblock.delayed.test.html | 2 +- test/it/viewblock.test.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/it/viewblock.delayed.test.html b/test/it/viewblock.delayed.test.html index 92badfe..e26e5c0 100644 --- a/test/it/viewblock.delayed.test.html +++ b/test/it/viewblock.delayed.test.html @@ -58,7 +58,7 @@ }); expect(window.called.length).to.equal(1); - expect(window.called[0].source).to.equal('div.block1'); + expect(window.called[0].source).to.equal('.block1'); }); }); }); diff --git a/test/it/viewblock.test.html b/test/it/viewblock.test.html index a19c5a7..47e07ba 100644 --- a/test/it/viewblock.test.html +++ b/test/it/viewblock.test.html @@ -58,7 +58,7 @@ }); expect(window.called.length).to.equal(1); - expect(window.called[0].source).to.equal('div.block1'); + expect(window.called[0].source).to.equal('.block1'); }); }); }); From c5df36efb68e7f927bf35f6245650e2581f42ce2 Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Tue, 17 Sep 2024 16:44:50 +0200 Subject: [PATCH 09/22] test(size): expect language checkpoint --- test/it/size.test.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/it/size.test.html b/test/it/size.test.html index 7224c88..90e30da 100644 --- a/test/it/size.test.html +++ b/test/it/size.test.html @@ -40,9 +40,10 @@ // click anywhere, just to make sure the script is running await sendMouse({ type: 'click', position: [10, 10] }); - expect(called).to.have.lengthOf(2); - expect(called[0][0]).to.equal('enter'); - expect(called[1][0]).to.equal('click'); + expect(called).to.have.lengthOf(3, 'three calls should have been made, got: ' + called.map((c) => c[0]).join(', ')); + expect(called[0][0]).to.equal('language'); + expect(called[1][0]).to.equal('enter'); + expect(called[2][0]).to.equal('click'); let transferSize = 0; // check the size of loaded JS files From 3c1bafd356c85f01fbe5d30d61eccdabe82ac2be Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Wed, 11 Sep 2024 13:05:27 +0200 Subject: [PATCH 10/22] build(rollup): relax eslint rules for bundle, so that we can meet size limits --- .eslintignore | 1 + rollup.config.js | 15 +++------------ test/it/size.test.html | 2 -- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/.eslintignore b/.eslintignore index ed36736..f67f9ee 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ .vscode/* coverage/* +src/* diff --git a/rollup.config.js b/rollup.config.js index 720b2c0..ec3cf1e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -11,7 +11,6 @@ */ import cleanup from 'rollup-plugin-cleanup'; -import eslint from 'rollup-plugin-eslint-bundle'; const banner = `/* * Copyright 2024 Adobe. All rights reserved. @@ -23,10 +22,7 @@ const banner = `/* * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. - */ - -/* eslint-disable max-classes-per-file, wrap-iife */ -// eslint-disable-next-line func-names`; + */`; const bundles = [ { @@ -55,13 +51,8 @@ export default [...bundles.map(({ outputFile, source }) => ({ ], plugins: [ cleanup({ - comments: ['eslint', 'jsdoc', /^\//, /^\*(?!\sc8\s)(?!\n \* Copyright)/], - maxEmptyLines: -1, - }), - eslint({ - eslintOptions: { - fix: true, - }, + comments: [/^\*(?!\sc8\s)(?!\n \* Copyright)/], + maxEmptyLines: 0, }), ], }))]; diff --git a/test/it/size.test.html b/test/it/size.test.html index 90e30da..ec700d1 100644 --- a/test/it/size.test.html +++ b/test/it/size.test.html @@ -41,8 +41,6 @@ await sendMouse({ type: 'click', position: [10, 10] }); expect(called).to.have.lengthOf(3, 'three calls should have been made, got: ' + called.map((c) => c[0]).join(', ')); - expect(called[0][0]).to.equal('language'); - expect(called[1][0]).to.equal('enter'); expect(called[2][0]).to.equal('click'); let transferSize = 0; From 87a2c1ce38003f921f540068f5c575273bd42dfd Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Tue, 17 Sep 2024 16:52:21 +0200 Subject: [PATCH 11/22] test(sourceselector): add test for empty input type --- test/unit/dom.sourceSelector.test.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unit/dom.sourceSelector.test.html b/test/unit/dom.sourceSelector.test.html index 13b02cf..0b2abcd 100644 --- a/test/unit/dom.sourceSelector.test.html +++ b/test/unit/dom.sourceSelector.test.html @@ -17,6 +17,7 @@ +
@@ -42,6 +43,7 @@ expect(sourceSelector(document.querySelector('input[type="text"'))).to.be.equal('form input[type=\'text\'].a-text'); expect(sourceSelector(document.querySelector('input[type="checkbox"'))).to.be.equal('form input[type=\'checkbox\'].a-checkbox'); + expect(sourceSelector(document.querySelector('input.a-none'))).to.be.equal('form input.a-none'); expect(sourceSelector(document.querySelector('textarea'))).to.be.equal('form textarea.a-textarea'); expect(sourceSelector(document.querySelector('.a-block'))).to.be.equal('.a-block'); From d53deb5c6e94ff18df2a01996c39b5cff7636fe4 Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Tue, 17 Sep 2024 16:59:57 +0200 Subject: [PATCH 12/22] fix(dom): better handling of empty type form inputs --- modules/dom.js | 2 +- test/unit/dom.sourceSelector.test.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dom.js b/modules/dom.js index 542a604..a9d7bae 100644 --- a/modules/dom.js +++ b/modules/dom.js @@ -72,7 +72,7 @@ function getSourceContext(element) { } function getSourceElement(element) { - if (element.closest('form') && Array.from(element.closest('form').elements).includes(element)) return element.tagName.toLowerCase() + (element.tagName === 'INPUT' ? `[type='${element.getAttribute('type')}']` : ''); + if (element.closest('form') && Array.from(element.closest('form').elements).includes(element)) return element.tagName.toLowerCase() + (element.tagName === 'INPUT' ? `[type='${element.getAttribute('type') || ''}']` : ''); if (walk(element, isFakeButton)) return 'button'; return element.tagName.toLowerCase().match(/^(a|img|video)$/) && element.tagName.toLowerCase(); } diff --git a/test/unit/dom.sourceSelector.test.html b/test/unit/dom.sourceSelector.test.html index 0b2abcd..3429308 100644 --- a/test/unit/dom.sourceSelector.test.html +++ b/test/unit/dom.sourceSelector.test.html @@ -43,7 +43,7 @@ expect(sourceSelector(document.querySelector('input[type="text"'))).to.be.equal('form input[type=\'text\'].a-text'); expect(sourceSelector(document.querySelector('input[type="checkbox"'))).to.be.equal('form input[type=\'checkbox\'].a-checkbox'); - expect(sourceSelector(document.querySelector('input.a-none'))).to.be.equal('form input.a-none'); + expect(sourceSelector(document.querySelector('input.a-none'))).to.be.equal('form input[type=\'\'].a-none'); expect(sourceSelector(document.querySelector('textarea'))).to.be.equal('form textarea.a-textarea'); expect(sourceSelector(document.querySelector('.a-block'))).to.be.equal('.a-block'); From 66a90497a45d754ed0fa4e8f8bc0917be64b3245 Mon Sep 17 00:00:00 2001 From: Lars Trieloff Date: Tue, 17 Sep 2024 17:06:10 +0200 Subject: [PATCH 13/22] fix(dom): better handling of fake blocks --- modules/dom.js | 2 +- test/unit/dom.sourceSelector.test.html | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/dom.js b/modules/dom.js index a9d7bae..15a63ac 100644 --- a/modules/dom.js +++ b/modules/dom.js @@ -62,7 +62,7 @@ function isFakeButton(element) { function getSourceContext(element) { if (element.closest('form')) return 'form'; - if (element.closest('.block')) return `.${element.closest('.block').getAttribute('data-block-name')}`; + if (element.closest('.block[data-block-name]')) return `.${element.closest('.block').getAttribute('data-block-name')}`; if (walk(element, isFakeDialog)) return 'dialog'; if (element.closest('nav')) return 'nav'; if (element.closest('header')) return 'header'; diff --git a/test/unit/dom.sourceSelector.test.html b/test/unit/dom.sourceSelector.test.html index 3429308..e592853 100644 --- a/test/unit/dom.sourceSelector.test.html +++ b/test/unit/dom.sourceSelector.test.html @@ -24,6 +24,10 @@
a div
+
+
a div, not inside a block
+
+