diff --git a/.changeset/wise-mirrors-grabberz.md b/.changeset/wise-mirrors-grabberz.md
new file mode 100644
index 000000000..5cf3307a8
--- /dev/null
+++ b/.changeset/wise-mirrors-grabberz.md
@@ -0,0 +1,6 @@
+---
+'@lion/ui': patch
+---
+
+make web-test-runner statements checking `documentOrShadowRoot.activeElement` debuggable by exposing
+a test-helper method `isActiveElement`.
diff --git a/packages/ui/components/combobox/test/lion-combobox.test.js b/packages/ui/components/combobox/test/lion-combobox.test.js
index f1e052f00..2a94f8636 100644
--- a/packages/ui/components/combobox/test/lion-combobox.test.js
+++ b/packages/ui/components/combobox/test/lion-combobox.test.js
@@ -1,20 +1,22 @@
-import {
- getComboboxMembers,
- getFilteredOptionValues,
- mimicKeyPress,
- mimicUserTyping,
- mimicUserTypingAdvanced,
-} from '@lion/ui/combobox-test-helpers.js';
+import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
+import { Required, Unparseable } from '@lion/ui/form-core.js';
+import { sendKeys } from '@web/test-runner-commands';
import { LionCombobox } from '@lion/ui/combobox.js';
import { browserDetection } from '@lion/ui/core.js';
import '@lion/ui/define/lion-combobox.js';
import '@lion/ui/define/lion-listbox.js';
import '@lion/ui/define/lion-option.js';
-import { Required, Unparseable } from '@lion/ui/form-core.js';
-import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
-import { sendKeys } from '@web/test-runner-commands';
import { LitElement } from 'lit';
import sinon from 'sinon';
+import {
+ getFilteredOptionValues,
+ mimicUserTypingAdvanced,
+ getComboboxMembers,
+ mimicUserTyping,
+ mimicKeyPress,
+} from '@lion/ui/combobox-test-helpers.js';
+
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
/**
* @typedef {import('../types/SelectionDisplay.js').SelectionDisplay} SelectionDisplay
@@ -986,7 +988,7 @@ describe('lion-combobox', () => {
options[0].click();
await el.updateComplete;
expect(el.opened).to.equal(false);
- expect(document.activeElement).to.equal(_inputNode);
+ expect(isActiveElement(_inputNode)).to.be.true;
// step [4]
await el.updateComplete;
diff --git a/packages/ui/components/overlays/src/utils/get-deep-active-element.js b/packages/ui/components/core/src/getDeepActiveElement.js
similarity index 100%
rename from packages/ui/components/overlays/src/utils/get-deep-active-element.js
rename to packages/ui/components/core/src/getDeepActiveElement.js
diff --git a/packages/ui/components/core/test-helpers/isActiveElement.js b/packages/ui/components/core/test-helpers/isActiveElement.js
new file mode 100644
index 000000000..759c8b649
--- /dev/null
+++ b/packages/ui/components/core/test-helpers/isActiveElement.js
@@ -0,0 +1,14 @@
+import { getDeepActiveElement } from '../src/getDeepActiveElement.js';
+/**
+ * Readable alternative for `expect(el).to.equal(document.activeElement);`.
+ * While this is readable by itself, it makes Web Test Runner hang completely in many occasions.
+ * Therefore it's better to write:
+ * `expect(isActiveElement(el)).to.be.true;`
+ * @param {Element} el
+ * @param {{deep?: boolean}} opts
+ * @returns {boolean}
+ */
+export function isActiveElement(el, { deep = false } = {}) {
+ const activeEl = deep ? getDeepActiveElement() : document.activeElement;
+ return el === activeEl;
+}
diff --git a/packages/ui/components/core/test/SlotMixin.test.js b/packages/ui/components/core/test/SlotMixin.test.js
index 21b1f0c2a..ffdce2c0a 100644
--- a/packages/ui/components/core/test/SlotMixin.test.js
+++ b/packages/ui/components/core/test/SlotMixin.test.js
@@ -4,6 +4,7 @@ import { LitElement } from 'lit';
import sinon from 'sinon';
import { ScopedElementsMixin, supportsScopedRegistry } from '../src/ScopedElementsMixin.js';
+import { isActiveElement } from '../test-helpers/isActiveElement.js';
/**
* @typedef {import('../types/SlotMixinTypes.js').SlotHost} SlotHost
@@ -210,12 +211,12 @@ describe('SlotMixin', () => {
);
const el = /** @type {* & SlotHost} */ (await fixture(`<${tag}>${tag}>`));
el._focusableNode.focus();
- expect(document.activeElement).to.equal(el._focusableNode);
+ expect(isActiveElement(el._focusableNode)).to.be.true;
el.currentValue = 1;
await el.updateComplete;
- expect(document.activeElement).to.equal(el._focusableNode);
+ expect(isActiveElement(el._focusableNode)).to.be.true;
});
it('keeps focus after rerendering complex shadow root into slot', async () => {
@@ -277,7 +278,7 @@ describe('SlotMixin', () => {
el.currentValue = 1;
await el.updateComplete;
- expect(document.activeElement).to.equal(el._focusableNode);
+ expect(isActiveElement(el._focusableNode)).to.be.true;
expect(el._focusableNode.shadowRoot.activeElement).to.equal(el._focusableNode._buttonNode);
});
@@ -335,12 +336,12 @@ describe('SlotMixin', () => {
el._focusableNode._buttonNode.focus();
- expect(el._focusableNode.shadowRoot.activeElement).to.equal(el._focusableNode._buttonNode);
+ expect(isActiveElement(el._focusableNode._buttonNode, { deep: true })).to.be.true;
el.currentValue = 1;
await el.updateComplete;
- expect(document.activeElement).to.equal(el._focusableNode);
+ expect(isActiveElement(el._focusableNode)).to.be.true;
expect(el._focusableNode.shadowRoot.activeElement).to.equal(el._focusableNode._buttonNode);
});
diff --git a/packages/ui/components/dialog/test/lion-dialog.test.js b/packages/ui/components/dialog/test/lion-dialog.test.js
index dcff2d60f..3838b6703 100644
--- a/packages/ui/components/dialog/test/lion-dialog.test.js
+++ b/packages/ui/components/dialog/test/lion-dialog.test.js
@@ -1,6 +1,7 @@
/* eslint-disable lit-a11y/no-autofocus */
import { expect, fixture as _fixture, html, unsafeStatic, aTimeout } from '@open-wc/testing';
import { runOverlayMixinSuite } from '../../overlays/test-suites/OverlayMixin.suite.js';
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
import '@lion/ui/define/lion-dialog.js';
/**
@@ -89,8 +90,8 @@ describe('lion-dialog', () => {
const invokerNode = el._overlayInvokerNode;
invokerNode.focus();
invokerNode.click();
- const contentNode = el.querySelector('[slot="content"]');
- expect(document.activeElement).to.equal(contentNode);
+ const contentNode = /** @type {Element} */ (el.querySelector('[slot="content"]'));
+ expect(isActiveElement(contentNode)).to.be.true;
});
it('sets focus on autofocused element', async () => {
@@ -107,8 +108,8 @@ describe('lion-dialog', () => {
const invokerNode = el._overlayInvokerNode;
invokerNode.focus();
invokerNode.click();
- const input = el.querySelector('input');
- expect(document.activeElement).to.equal(input);
+ const input = /** @type {Element} */ (el.querySelector('input'));
+ expect(isActiveElement(input)).to.be.true;
});
it('with trapsKeyboardFocus set to false the focus stays on the invoker', async () => {
@@ -125,7 +126,7 @@ describe('lion-dialog', () => {
const invokerNode = el._overlayInvokerNode;
invokerNode.focus();
invokerNode.click();
- expect(document.activeElement).to.equal(invokerNode);
+ expect(isActiveElement(invokerNode)).to.be.true;
});
it('opened-changed event should send detail object with opened state', async () => {
diff --git a/packages/ui/components/form-core/test-suites/NativeTextFieldMixin.suite.js b/packages/ui/components/form-core/test-suites/NativeTextFieldMixin.suite.js
index 95f6fa318..d3249a390 100644
--- a/packages/ui/components/form-core/test-suites/NativeTextFieldMixin.suite.js
+++ b/packages/ui/components/form-core/test-suites/NativeTextFieldMixin.suite.js
@@ -1,14 +1,16 @@
-import { LitElement } from 'lit';
-import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
import { defineCE, expect, fixture, html, triggerFocusFor, unsafeStatic } from '@open-wc/testing';
-import { sendKeys } from '@web/test-runner-commands';
-import { spy } from 'sinon';
+import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
import { NativeTextFieldMixin } from '@lion/ui/form-core.js';
+import { sendKeys } from '@web/test-runner-commands';
import { browserDetection } from '@lion/ui/core.js';
+import { LitElement } from 'lit';
+import { spy } from 'sinon';
+
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
/**
- * @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
* @typedef {ArrayConstructor | ObjectConstructor | NumberConstructor | BooleanConstructor | StringConstructor | DateConstructor | 'iban' | 'email'} modelValueType
+ * @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
*/
/**
@@ -56,7 +58,7 @@ export function runNativeTextFieldMixinSuite(customConfig) {
const { _inputNode } = getFormControlMembers(el);
await triggerFocusFor(el);
await el.updateComplete;
- expect(document.activeElement).to.equal(_inputNode);
+ expect(isActiveElement(_inputNode)).to.be.true;
await sendKeys({
press: 'h',
});
diff --git a/packages/ui/components/form-core/test/lion-field.test.js b/packages/ui/components/form-core/test/lion-field.test.js
index 195d3c787..e49afdda8 100644
--- a/packages/ui/components/form-core/test/lion-field.test.js
+++ b/packages/ui/components/form-core/test/lion-field.test.js
@@ -1,27 +1,28 @@
-import { unsafeHTML } from 'lit/directives/unsafe-html.js';
-import { Required, Validator } from '@lion/ui/form-core.js';
-import '@lion/ui/define/lion-field.js';
import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
import { getLocalizeManager } from '@lion/ui/localize-no-side-effects.js';
import { localizeTearDown } from '@lion/ui/localize-test-helpers.js';
-import {
- expect,
- fixture,
- html,
- triggerBlurFor,
- triggerFocusFor,
- unsafeStatic,
-} from '@open-wc/testing';
+import { Required, Validator } from '@lion/ui/form-core.js';
+import { unsafeHTML } from 'lit/directives/unsafe-html.js';
+import '@lion/ui/define/lion-field.js';
import sinon from 'sinon';
+import {
+ triggerFocusFor,
+ triggerBlurFor,
+ unsafeStatic,
+ fixture,
+ expect,
+ html,
+} from '@open-wc/testing';
+
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
/**
- * @typedef {import('../src/LionField.js').LionField} LionField
- * @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
* @typedef {FormControlHost & HTMLElement & {_parentFormGroup?:HTMLElement, checked?:boolean}} FormControl
+ * @typedef {HTMLElement & {shadowRoot: HTMLElement, assignedNodes: Function}} ShadowHTMLElement
+ * @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
+ * @typedef {import('../src/LionField.js').LionField} LionField
*/
-/** @typedef {HTMLElement & {shadowRoot: HTMLElement, assignedNodes: Function}} ShadowHTMLElement */
-
const tagString = 'lion-field';
const tag = unsafeStatic(tagString);
const inputSlotString = '';
@@ -106,7 +107,7 @@ describe('', () => {
await triggerFocusFor(el);
- expect(document.activeElement).to.equal(_inputNode);
+ expect(isActiveElement(_inputNode)).to.be.true;
expect(cbFocusHost.callCount).to.equal(1);
expect(cbFocusNativeInput.callCount).to.equal(1);
expect(cbBlurHost.callCount).to.equal(0);
@@ -117,7 +118,7 @@ describe('', () => {
expect(cbBlurNativeInput.callCount).to.equal(1);
await triggerFocusFor(el);
- expect(document.activeElement).to.equal(_inputNode);
+ expect(isActiveElement(_inputNode)).to.be.true;
expect(cbFocusHost.callCount).to.equal(2);
expect(cbFocusNativeInput.callCount).to.equal(2);
diff --git a/packages/ui/components/form-integrations/test/dialog-integrations.test.js b/packages/ui/components/form-integrations/test/dialog-integrations.test.js
index 0b5da6bc4..9e68e54a3 100644
--- a/packages/ui/components/form-integrations/test/dialog-integrations.test.js
+++ b/packages/ui/components/form-integrations/test/dialog-integrations.test.js
@@ -1,13 +1,14 @@
/* eslint-disable lit-a11y/no-autofocus */
import { expect, fixture } from '@open-wc/testing';
-import { html } from 'lit';
-import { getAllTagNames } from './helpers/helpers.js';
-import './helpers/umbrella-form.js';
-import '@lion/ui/define/lion-dialog.js';
import '@lion/ui/define/lion-checkbox.js';
+import '@lion/ui/define/lion-dialog.js';
import '@lion/ui/define/lion-option.js';
import '@lion/ui/define/lion-radio.js';
+import { html } from 'lit';
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
+import { getAllTagNames } from './helpers/helpers.js';
+import './helpers/umbrella-form.js';
/**
* @typedef {import('./helpers/umbrella-form.js').UmbrellaForm} UmbrellaForm
* @typedef {import('../../dialog/src/LionDialog.js').LionDialog} LionDialog
@@ -89,6 +90,6 @@ describe('Form inside dialog Integrations', () => {
el._overlayInvokerNode.click();
const lionInput = el.querySelector('[name="input"]');
// @ts-expect-error [allow-protected-in-tests]
- expect(document.activeElement).to.equal(lionInput._focusableNode);
+ expect(isActiveElement(lionInput._focusableNode)).to.be.true;
});
});
diff --git a/packages/ui/components/form/test/lion-form.test.js b/packages/ui/components/form/test/lion-form.test.js
index f1dcc0403..da824b43c 100644
--- a/packages/ui/components/form/test/lion-form.test.js
+++ b/packages/ui/components/form/test/lion-form.test.js
@@ -1,25 +1,27 @@
-import { LionFieldset } from '@lion/ui/fieldset.js';
-import '@lion/ui/define/lion-fieldset.js';
import { LionField, Required } from '@lion/ui/form-core.js';
-import '@lion/ui/define/lion-field.js';
import '@lion/ui/define/lion-validation-feedback.js';
+import { LionFieldset } from '@lion/ui/fieldset.js';
+import '@lion/ui/define/lion-checkbox-group.js';
+import '@lion/ui/define/lion-radio-group.js';
+import '@lion/ui/define/lion-fieldset.js';
+import '@lion/ui/define/lion-checkbox.js';
import '@lion/ui/define/lion-listbox.js';
import '@lion/ui/define/lion-option.js';
-import '@lion/ui/define/lion-checkbox-group.js';
-import '@lion/ui/define/lion-checkbox.js';
-import '@lion/ui/define/lion-radio-group.js';
+import '@lion/ui/define/lion-field.js';
import '@lion/ui/define/lion-radio.js';
import '@lion/ui/define/lion-form.js';
+import { spy } from 'sinon';
import {
+ fixture as _fixture,
+ unsafeStatic,
aTimeout,
defineCE,
- expect,
- fixture as _fixture,
- html,
oneEvent,
- unsafeStatic,
+ expect,
+ html,
} from '@open-wc/testing';
-import { spy } from 'sinon';
+
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
/**
* @typedef {import('../src/LionForm.js').LionForm} LionForm
@@ -225,7 +227,7 @@ describe('', () => {
button.click();
expect(dispatchSpy.args[0][0].type).to.equal('submit');
// @ts-ignore [allow-protected] in test
- expect(document.activeElement).to.equal(el.formElements[1]._inputNode);
+ expect(isActiveElement(el.formElements[1]._inputNode)).to.be.true;
});
it('sets focus on submit to the first erroneous form element within a fieldset', async () => {
@@ -248,7 +250,7 @@ describe('', () => {
expect(dispatchSpy.args[0][0].type).to.equal('submit');
const fieldset = el.formElements[0];
// @ts-ignore [allow-protected] in test
- expect(document.activeElement).to.equal(fieldset.formElements[1]._inputNode);
+ expect(isActiveElement(fieldset.formElements[1]._inputNode)).to.be.true;
});
it('sets focus on submit to the first form element within a erroneous fieldset', async () => {
@@ -268,7 +270,7 @@ describe('', () => {
button.click();
expect(dispatchSpy.args[0][0].type).to.equal('submit');
const fieldset = el.formElements[0];
- expect(document.activeElement).to.equal(fieldset.formElements[0]._inputNode);
+ expect(isActiveElement(fieldset.formElements[0]._inputNode)).to.be.true;
});
it('sets focus on submit to the first form element within a erroneous fieldset within another fieldset', async () => {
@@ -290,7 +292,7 @@ describe('', () => {
const childFieldsetEl = parentFieldSetEl.formElements[0];
const inputEl = childFieldsetEl.formElements[0];
button.click();
- expect(document.activeElement).to.equal(inputEl._focusableNode);
+ expect(isActiveElement(inputEl._focusableNode)).to.be.true;
});
it('sets focus on submit to the first form element within a erroneous listbox', async () => {
@@ -308,7 +310,7 @@ describe('', () => {
const button = /** @type {HTMLButtonElement} */ (el.querySelector('button'));
button.click();
const listboxEl = el.formElements[0];
- expect(document.activeElement).to.equal(listboxEl._inputNode);
+ expect(isActiveElement(listboxEl._inputNode)).to.be.true;
});
it('sets focus on submit to the first form element within a erroneous listbox within a fieldset', async () => {
@@ -329,7 +331,7 @@ describe('', () => {
button.click();
const fieldsetEl = el.formElements[0];
const listboxEl = fieldsetEl.formElements[0];
- expect(document.activeElement).to.equal(listboxEl._inputNode);
+ expect(isActiveElement(listboxEl._inputNode)).to.be.true;
});
it('sets focus on submit to the first form element within a erroneous checkbox-group', async () => {
@@ -348,7 +350,7 @@ describe('', () => {
const checkboxGroupEl = el.formElements[0];
const checkboxEl = checkboxGroupEl.formElements[0];
button.click();
- expect(document.activeElement).to.equal(checkboxEl._focusableNode);
+ expect(isActiveElement(checkboxEl._focusableNode)).to.be.true;
});
it('sets focus on submit to the first form element within a erroneous radio-group', async () => {
@@ -370,6 +372,6 @@ describe('', () => {
const radioGroupEl = el.formElements[0];
const radioEl = radioGroupEl.formElements[0];
button.click();
- expect(document.activeElement).to.equal(radioEl._focusableNode);
+ expect(isActiveElement(radioEl._focusableNode)).to.be.true;
});
});
diff --git a/packages/ui/components/overlays/src/utils/contain-focus.js b/packages/ui/components/overlays/src/utils/contain-focus.js
index 3fdf6e0c0..67a5590ad 100644
--- a/packages/ui/components/overlays/src/utils/contain-focus.js
+++ b/packages/ui/components/overlays/src/utils/contain-focus.js
@@ -6,7 +6,7 @@
* and contains several bugs on IE11.
*/
-import { getDeepActiveElement } from './get-deep-active-element.js';
+import { getDeepActiveElement } from '../../../core/src/getDeepActiveElement.js';
import { getFocusableElements } from './get-focusable-elements.js';
import { deepContains } from './deep-contains.js';
import { keyCodes } from './key-codes.js';
diff --git a/packages/ui/components/overlays/test/OverlayController.test.js b/packages/ui/components/overlays/test/OverlayController.test.js
index 7ce54420a..7b8640843 100644
--- a/packages/ui/components/overlays/test/OverlayController.test.js
+++ b/packages/ui/components/overlays/test/OverlayController.test.js
@@ -12,6 +12,7 @@ import {
html,
} from '@open-wc/testing';
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
import { createShadowHost } from '../test-helpers/createShadowHost.js';
import { _adoptStyleUtils } from '../src/utils/adopt-styles.js';
import { simulateTab } from '../src/utils/simulate-tab.js';
@@ -574,7 +575,8 @@ describe('OverlayController', () => {
trapsKeyboardFocus: true,
});
await ctrl.show();
- expect(ctrl.contentNode).to.equal(document.activeElement);
+
+ expect(isActiveElement(ctrl.contentNode)).to.be.true;
});
it('keeps focus within the overlay e.g. you can not tab out by accident', async () => {
@@ -1139,10 +1141,10 @@ describe('OverlayController', () => {
await ctrl.show();
const input = /** @type {HTMLInputElement} */ (contentNode.querySelector('input'));
input.focus();
- expect(document.activeElement).to.equal(input);
+ expect(isActiveElement(input)).to.be.true;
await ctrl.hide();
- expect(document.activeElement).to.equal(document.body);
+ expect(isActiveElement(document.body)).to.be.true;
});
it('supports elementToFocusAfterHide option to focus it when hiding', async () => {
@@ -1159,10 +1161,10 @@ describe('OverlayController', () => {
await ctrl.show();
const textarea = /** @type {HTMLTextAreaElement} */ (contentNode.querySelector('textarea'));
textarea.focus();
- expect(document.activeElement).to.equal(textarea);
+ expect(isActiveElement(textarea)).to.be.true;
await ctrl.hide();
- expect(document.activeElement).to.equal(input);
+ expect(isActiveElement(input)).to.be.true;
expect(isInViewport(input)).to.be.true;
});
@@ -1187,10 +1189,10 @@ describe('OverlayController', () => {
await ctrl.show();
const textarea = /** @type {HTMLTextAreaElement} */ (contentNode.querySelector('textarea'));
textarea.focus();
- expect(document.activeElement).to.equal(textarea);
+ expect(isActiveElement(textarea)).to.be.true;
await ctrl.hide();
- expect(document.activeElement).to.equal(input);
+ expect(isActiveElement(input)).to.be.true;
document.body.removeChild(shadowHost);
});
@@ -1208,10 +1210,10 @@ describe('OverlayController', () => {
await ctrl.show();
// an outside element has taken over focus
outsideButton.focus();
- expect(document.activeElement).to.equal(outsideButton);
+ expect(isActiveElement(outsideButton)).to.be.true;
await ctrl.hide();
- expect(document.activeElement).to.equal(outsideButton);
+ expect(isActiveElement(outsideButton)).to.be.true;
});
it('allows to set elementToFocusAfterHide on show', async () => {
@@ -1230,10 +1232,10 @@ describe('OverlayController', () => {
await ctrl.show(input);
const textarea = /** @type {HTMLTextAreaElement} */ (contentNode.querySelector('textarea'));
textarea.focus();
- expect(document.activeElement).to.equal(textarea);
+ expect(isActiveElement(textarea)).to.be.true;
await ctrl.hide();
- expect(document.activeElement).to.equal(input);
+ expect(isActiveElement(input)).to.be.true;
});
});
diff --git a/packages/ui/components/overlays/test/utils-tests/contain-focus.test.js b/packages/ui/components/overlays/test/utils-tests/contain-focus.test.js
index 2ad3c3fca..ce7e095aa 100644
--- a/packages/ui/components/overlays/test/utils-tests/contain-focus.test.js
+++ b/packages/ui/components/overlays/test/utils-tests/contain-focus.test.js
@@ -1,11 +1,12 @@
/* eslint-disable lit-a11y/no-autofocus */
import { expect, fixture, nextFrame } from '@open-wc/testing';
-import { html } from 'lit/static-html.js';
+import { getFocusableElements } from '@lion/ui/overlays.js';
import { renderLitAsNode } from '@lion/ui/helpers.js';
-import { getDeepActiveElement, getFocusableElements } from '@lion/ui/overlays.js';
+import { html } from 'lit/static-html.js';
-import { keyCodes } from '../../src/utils/key-codes.js';
+import { isActiveElement } from '../../../core/test-helpers/isActiveElement.js';
import { containFocus } from '../../src/utils/contain-focus.js';
+import { keyCodes } from '../../src/utils/key-codes.js';
function simulateTabWithinContainFocus() {
const event = new CustomEvent('keydown', { detail: 0, bubbles: true });
@@ -87,7 +88,7 @@ describe('containFocus()', () => {
const root = /** @type {HTMLElement} */ (document.getElementById('rootElement'));
const { disconnect } = containFocus(root);
- expect(getDeepActiveElement()).to.equal(root);
+ expect(isActiveElement(root, { deep: true })).to.be.true;
expect(root.getAttribute('tabindex')).to.equal('-1');
expect(root.style.getPropertyValue('outline-style')).to.equal('none');
@@ -99,7 +100,7 @@ describe('containFocus()', () => {
const el = /** @type {HTMLElement} */ (document.querySelector('input[autofocus]'));
const { disconnect } = containFocus(el);
- expect(getDeepActiveElement()).to.equal(el);
+ expect(isActiveElement(el, { deep: true })).to.be.true;
disconnect();
});
@@ -113,7 +114,7 @@ describe('containFocus()', () => {
/** @type {HTMLElement} */ (document.getElementById('outside-1')).focus();
simulateTabWithinContainFocus();
- expect(getDeepActiveElement()).to.equal(focusableElements[0]);
+ expect(isActiveElement(focusableElements[0], { deep: true })).to.be.true;
disconnect();
});
@@ -127,7 +128,7 @@ describe('containFocus()', () => {
focusableElements[focusableElements.length - 1].focus();
simulateTabWithinContainFocus();
- expect(getDeepActiveElement()).to.equal(focusableElements[0]);
+ expect(isActiveElement(focusableElements[0], { deep: true })).to.be.true;
disconnect();
});
@@ -146,7 +147,7 @@ describe('containFocus()', () => {
* actual tab key press. So the best we can do is if we didn't redirect focus
* to the first element.
*/
- expect(getDeepActiveElement()).to.equal(focusableElements[2]);
+ expect(isActiveElement(focusableElements[2], { deep: true })).to.be.true;
disconnect();
});
@@ -158,9 +159,10 @@ describe('containFocus()', () => {
const { disconnect } = containFocus(root);
focusableElements[2].focus();
- expect(getDeepActiveElement()).to.equal(focusableElements[2]);
+ expect(isActiveElement(focusableElements[2], { deep: true })).to.be.true;
+
document.body.click(); // this does not cause focusout event :( doesn't seem possible to mock
- expect(getDeepActiveElement()).to.equal(root);
+ expect(isActiveElement(root, { deep: true })).to.be.true;
disconnect();
});
@@ -185,11 +187,12 @@ describe('containFocus()', () => {
// Simulate tab in window
simulateTabInWindow(/** @type {HTMLElement} */ (document.getElementById('outside-1')));
- expect(getDeepActiveElement()).to.equal(focusableElements[0]);
+ expect(isActiveElement(focusableElements[0], { deep: true })).to.be.true;
// Simulate shift+tab in window
simulateTabInWindow(/** @type {HTMLElement} */ (document.getElementById('outside-2')));
- expect(getDeepActiveElement()).to.equal(focusableElements[focusableElements.length - 1]);
+ expect(isActiveElement(focusableElements[focusableElements.length - 1], { deep: true })).to.be
+ .true;
disconnect();
});
@@ -202,11 +205,12 @@ describe('containFocus()', () => {
// Simulate tab in window
simulateTabInWindow(/** @type {HTMLElement} */ (document.getElementById('outside-1')));
- expect(getDeepActiveElement()).to.equal(focusableElements[0]);
+ expect(isActiveElement(focusableElements[0], { deep: true })).to.be.true;
// Simulate shift+tab in window
simulateTabInWindow(/** @type {HTMLElement} */ (document.getElementById('outside-2')));
- expect(getDeepActiveElement()).to.equal(focusableElements[focusableElements.length - 1]);
+ expect(isActiveElement(focusableElements[focusableElements.length - 1], { deep: true })).to.be
+ .true;
disconnect();
});
@@ -219,11 +223,12 @@ describe('containFocus()', () => {
// Simulate tab in window
simulateTabInWindow(focusableElements[0]);
- expect(getDeepActiveElement()).to.equal(focusableElements[0]);
+ expect(isActiveElement(focusableElements[0], { deep: true })).to.be.true;
// Simulate shift+tab in window
simulateTabInWindow(focusableElements[focusableElements.length - 1]);
- expect(getDeepActiveElement()).to.equal(focusableElements[focusableElements.length - 1]);
+ expect(isActiveElement(focusableElements[focusableElements.length - 1], { deep: true })).to.be
+ .true;
disconnect();
});
diff --git a/packages/ui/components/select-rich/test/lion-select-rich.test.js b/packages/ui/components/select-rich/test/lion-select-rich.test.js
index a0db51cdf..fb7681bfa 100644
--- a/packages/ui/components/select-rich/test/lion-select-rich.test.js
+++ b/packages/ui/components/select-rich/test/lion-select-rich.test.js
@@ -1,30 +1,28 @@
-import { LitElement } from 'lit';
-import { LionOption } from '@lion/ui/listbox.js';
-import { OverlayController } from '@lion/ui/overlays.js';
-import { mimicClick } from '@lion/ui/overlays-test-helpers.js';
import { LionSelectInvoker, LionSelectRich } from '@lion/ui/select-rich.js';
-
-import '@lion/ui/define/lion-option.js';
-import '@lion/ui/define/lion-listbox.js';
+import { getSelectRichMembers } from '@lion/ui/select-rich-test-helpers.js';
+import { mimicClick } from '@lion/ui/overlays-test-helpers.js';
+import { OverlayController } from '@lion/ui/overlays.js';
+import { LionOption } from '@lion/ui/listbox.js';
import '@lion/ui/define/lion-select-rich.js';
+import '@lion/ui/define/lion-listbox.js';
+import '@lion/ui/define/lion-option.js';
+import { LitElement } from 'lit';
import {
+ fixture as _fixture,
+ unsafeStatic,
+ nextFrame,
aTimeout,
defineCE,
expect,
- fixture as _fixture,
html,
- nextFrame,
- unsafeStatic,
} from '@open-wc/testing';
-import { getSelectRichMembers } from '@lion/ui/select-rich-test-helpers.js';
+
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
/**
- * @typedef {import('../../listbox/src/LionOptions.js').LionOptions} LionOptions
- * @typedef {import('../../listbox/types/ListboxMixinTypes.js').ListboxHost} ListboxHost
* @typedef {import('../../form-core/types/FormControlMixinTypes.js').FormControlHost} FormControlHost
- */
-
-/**
+ * @typedef {import('../../listbox/types/ListboxMixinTypes.js').ListboxHost} ListboxHost
+ * @typedef {import('../../listbox/src/LionOptions.js').LionOptions} LionOptions
* @typedef {import('lit').TemplateResult} TemplateResult
*/
@@ -478,7 +476,7 @@ describe('lion-select-rich', () => {
el.opened = true;
await el.updateComplete;
- expect(document.activeElement).to.equal(_listboxNode);
+ expect(isActiveElement(_listboxNode)).to.be.true;
el.opened = false;
await el.updateComplete;
diff --git a/packages/ui/components/switch/test/lion-switch.test.js b/packages/ui/components/switch/test/lion-switch.test.js
index f3dfe2e10..359f846a9 100644
--- a/packages/ui/components/switch/test/lion-switch.test.js
+++ b/packages/ui/components/switch/test/lion-switch.test.js
@@ -1,16 +1,17 @@
+import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
import { expect, fixture as _fixture } from '@open-wc/testing';
-import { html } from 'lit/static-html.js';
-import sinon from 'sinon';
import { Validator } from '@lion/ui/form-core.js';
import { LionSwitch } from '@lion/ui/switch.js';
-import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
-
+import { html } from 'lit/static-html.js';
import '@lion/ui/define/lion-switch.js';
+import sinon from 'sinon';
+
+import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
/**
* @typedef {import('../src/LionSwitchButton.js').LionSwitchButton} LionSwitchButton
- * @typedef {import('lit').TemplateResult} TemplateResult
* @typedef {import('@lion/ui/types/form-core.js').FormControlHost} FormControlHost
+ * @typedef {import('lit').TemplateResult} TemplateResult
*/
const IsTrue = class extends Validator {
@@ -60,7 +61,7 @@ describe('lion-switch', () => {
const { _inputNode, _labelNode } = getSwitchMembers(el);
_labelNode.click();
- expect(document.activeElement).to.equal(_inputNode);
+ expect(isActiveElement(_inputNode)).to.be.true;
});
it('clicking the label should not focus the toggle button when disabled', async () => {
diff --git a/packages/ui/exports/overlays.js b/packages/ui/exports/overlays.js
index db0a17179..f7813d061 100644
--- a/packages/ui/exports/overlays.js
+++ b/packages/ui/exports/overlays.js
@@ -11,11 +11,12 @@ export { withTooltipConfig } from '../components/overlays/src/configurations/wit
export { containFocus, rotateFocus } from '../components/overlays/src/utils/contain-focus.js';
export { deepContains } from '../components/overlays/src/utils/deep-contains.js';
-export { getDeepActiveElement } from '../components/overlays/src/utils/get-deep-active-element.js';
+// re-export via this entrypoint for backwards compatibility
+export { getDeepActiveElement } from '../components/core/src/getDeepActiveElement.js';
export { getFocusableElements } from '../components/overlays/src/utils/get-focusable-elements.js';
export {
- setSiblingsInert,
unsetSiblingsInert,
+ setSiblingsInert,
} from '../components/overlays/src/utils/inert-siblings.js';
export { overlays } from '../components/overlays/src/singleton.js';
diff --git a/web-test-runner.config.mjs b/web-test-runner.config.mjs
index a37974224..033fab500 100644
--- a/web-test-runner.config.mjs
+++ b/web-test-runner.config.mjs
@@ -11,7 +11,8 @@ const groups = (
await optimisedGlob(['packages/*/test', 'packages/ui/components/**/test'], {
onlyDirectories: true,
})
-).map(dir => ({ name: dir.split('/').at(-2), files: `${dir}/**/*.test.js` }));
+) // @ts-expect-error [update-es-version-later]
+ .map(dir => ({ name: dir.split('/').at(-2), files: `${dir}/**/*.test.js` })); // .filter(({name}) => name === 'overlays');
/**
* @type {import('@web/test-runner').TestRunnerConfig['testRunnerHtml']}