Repository: exadel-inc/esl
Pull request #2355 (2024-04-17 21:59:40)

refactor(esl-utils): remove `DeviceDetector` use from code
Closes: #2348 


Patch:
diff --git a/site/views/examples/animate.njk b/site/views/examples/animate.njk
index d9a6fd065..bd7336a7a 100644
--- a/site/views/examples/animate.njk
+++ b/site/views/examples/animate.njk
@@ -24,19 +24,19 @@ aside:
       </script>
 
       <script type="text/html"
-              label="Grouped and repetitive animation with custom delay"
+              label="Grouped and repetitive animation"
               uip-snippet
               uip-snippet-js="js-snippet-animate-element">
         <ul class="round simple-grid">
           <esl-animate class="esl-animate-group" target="::parent::child(li)" group repeat></esl-animate>
           {% for i in range(0, 20) -%}
-          <li class="bg-blue round esl-animate-slide esl-animate-fade simple-grid-cell {{ 'hide-xs' if i >= 12 }}"></li>
+          <li class="bg-blue round esl-animate-slide up simple-grid-cell {{ 'hide-xs' if i >= 12 }}"></li>
           {%- endfor %}
         </ul>
       </script>
 
       <script type="text/html"
-              label="Grouped animation by mixin"
+              label="Grouped animation with custom delay by mixin"
               uip-snippet
               uip-snippet-js="js-snippet-animate-mixin">
         <button class="btn btn-sec-orange"
@@ -45,7 +45,7 @@ aside:
         </button>
         <ul class="round simple-grid">
           {% for i in range(0, 20) -%}
-          <li class="re-1 bg-orange round esl-animate-slide esl-animate-fade simple-grid-cell {{ 'hide-xs' if i >= 12 }}"
+          <li class="re-1 bg-orange round esl-animate-slide up left simple-grid-cell {{ 'hide-xs' if i >= 12 }}"
               esl-animate="{group: true, groupDelay: 50}"></li>
           {%- endfor %}
         </ul>
@@ -62,7 +62,7 @@ aside:
 
       <uip-snippets class="uip-toolbar" dropdown-view="@xs"></uip-snippets>
       <uip-preview></uip-preview>
-      <uip-settings target=".esl-animate-slide" label="Settings" resizable vertical="@+sm">
+      <uip-settings target="esl-animate" label="Settings" resizable vertical="@+sm">
         <uip-select-setting label="Animation type:" attribute="class" mode="append">
           <option value="esl-animate-fade">Fade</option>
           <option value="up">Slide Up</option>
diff --git a/src/modules/esl-alert/core/esl-alert.ts b/src/modules/esl-alert/core/esl-alert.ts
index 559145b07..6c194811d 100644
--- a/src/modules/esl-alert/core/esl-alert.ts
+++ b/src/modules/esl-alert/core/esl-alert.ts
@@ -2,7 +2,7 @@ import {ExportNs} from '../../esl-utils/environment/export-ns';
 import {attr, jsonAttr, prop, listen} from '../../esl-utils/decorators';
 import {isMatches} from '../../esl-utils/dom/traversing';
 import {ESLToggleable} from '../../esl-toggleable/core';
-import {DeviceDetector} from '../../esl-utils/environment/device-detector';
+import {isIE} from '../../esl-utils/environment/device-detector';
 import {CSSClassUtils} from '../../esl-utils/dom/class';
 import {createZIndexIframe} from '../../esl-utils/fixes/ie-fixes';
 import {ESLTraversingQuery} from '../../esl-traversing-query/core';
@@ -96,7 +96,7 @@ export class ESLAlert extends ESLToggleable {
     this.$content.className = 'esl-alert-content';
     this.innerHTML = '';
     this.appendChild(this.$content);
-    if (DeviceDetector.isIE) this.appendChild(createZIndexIframe());
+    if (isIE) this.appendChild(createZIndexIframe());
     if (this.target) {
       this.$target = ESLTraversingQuery.first(this.target, this) as EventTarget;
     }
diff --git a/src/modules/esl-event-listener/core/targets/intersection.event.ts b/src/modules/esl-event-listener/core/targets/intersection.event.ts
index b4ab66af4..771880f95 100644
--- a/src/modules/esl-event-listener/core/targets/intersection.event.ts
+++ b/src/modules/esl-event-listener/core/targets/intersection.event.ts
@@ -1,20 +1,11 @@
 import {overrideEvent} from '../../../esl-utils/dom/events/misc';
 
-export type ESLIntersectionEventType = typeof ESLIntersectionEvent.TYPE | typeof ESLIntersectionEvent.IN | typeof ESLIntersectionEvent.OUT;
-
 /**
  * An event that is fired when an element intersects with the device viewport by the {@link ESLIntersectionTarget}.
  * Based on the {@link IntersectionObserverEntry} object.
  */
 export class ESLIntersectionEvent extends Event implements IntersectionObserverEntry {
-  /** Type of event that will be dispatched on viewport exit */
-  public static readonly OUT = 'intersects:out';
-  /** Type of event that will be dispatched on viewport enter */
-  public static readonly IN = 'intersects:in';
-  /** Type of event that will be dispatched on both viewport enter and exit */
-  public static readonly TYPE = 'intersects';
-  /** @deprecated use {@link TYPE} instead */
-  public static readonly type = this.TYPE;
+  public static readonly type = 'intersects';
 
   /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/IntersectionObserverEntry/target) */
   public override readonly target: Element;
@@ -31,22 +22,14 @@ export class ESLIntersectionEvent extends Event implements IntersectionObserverE
   /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/IntersectionObserverEntry/time) */
   public readonly time: DOMHighResTimeStamp;
 
-  protected constructor(eventName: ESLIntersectionEventType, target: Element) {
-    super(eventName, {bubbles: false, cancelable: false});
+  protected constructor(target: Element) {
+    super(ESLIntersectionEvent.type, {bubbles: false, cancelable: false});
     overrideEvent(this, 'target', target);
   }
 
-  /** Validates event name. Accepted values are 'intersects:out', 'intersects:in', 'intersects' */
-  public static isValidEventType(event: string): event is ESLIntersectionEventType {
-    return event === ESLIntersectionEvent.TYPE || event === ESLIntersectionEvent.IN || event === ESLIntersectionEvent.OUT;
-  }
-
-  /** Creates {@link ESLIntersectionEvent} from {@link ESLIntersectionEvent}
-    * @param eventName - Custom event name ('intersects' by default. Accepted values are 'intersects:out', 'intersects:in', 'intersects').
-    * @param entry - The intersection observer entry.
-   */
-  public static fromEntry(eventName: ESLIntersectionEventType, entry: IntersectionObserverEntry): ESLIntersectionEvent {
-    const event = new ESLIntersectionEvent(eventName, entry.target);
+  /** Creates {@link ESLIntersectionEvent} from {@link ESLIntersectionEvent} */
+  public static fromEntry(entry: IntersectionObserverEntry): ESLIntersectionEvent {
+    const event = new ESLIntersectionEvent(entry.target);
     const {
       boundingClientRect,
       intersectionRatio,
diff --git a/src/modules/esl-event-listener/core/targets/intersection.target.ts b/src/modules/esl-event-listener/core/targets/intersection.target.ts
index ca47a9010..1594fc107 100644
--- a/src/modules/esl-event-listener/core/targets/intersection.target.ts
+++ b/src/modules/esl-event-listener/core/targets/intersection.target.ts
@@ -4,8 +4,6 @@ import {wrap} from '../../../esl-utils/misc/array';
 
 import {ESLIntersectionEvent} from './intersection.event';
 
-import type {ESLIntersectionEventType} from './intersection.event';
-
 export {ESLIntersectionEvent};
 
 /**
@@ -52,35 +50,32 @@ export class ESLIntersectionTarget extends SyntheticEventTarget {
   /** Internal method to handle {@link IntersectionObserver} entry change */
   protected handleChange(entries: IntersectionObserverEntry[]): void {
     entries.forEach(
-      (entry) => {
-        this.dispatchEvent(ESLIntersectionEvent.fromEntry(entry.isIntersecting ? ESLIntersectionEvent.IN : ESLIntersectionEvent.OUT, entry));
-        this.dispatchEvent(ESLIntersectionEvent.fromEntry(ESLIntersectionEvent.TYPE, entry));
-      }
+      (entry) => this.dispatchEvent(ESLIntersectionEvent.fromEntry(entry))
     );
   }
 
   /** Subscribes to the observed target {@link Element} changes */
   public override addEventListener(callback: EventListener): void;
-  public override addEventListener(event: ESLIntersectionEventType, callback: EventListener): void;
+  public override addEventListener(event: typeof ESLIntersectionEvent.type, callback: EventListener): void;
   public override addEventListener(event: any, callback: EventListener = event): void {
-    if (typeof event !== 'string') event = ESLIntersectionEvent.TYPE;
-    if (!ESLIntersectionEvent.isValidEventType(event)) {
+    if (typeof event !== 'string') event = ESLIntersectionEvent.type;
+    if (event !== ESLIntersectionEvent.type) {
       console.warn(`[ESL]: ESLIntersectionTarget does not support '${event}' type`);
       return;
     }
-    super.addEventListener(event, callback);
-    if (this.getEventListeners(event).length > 1) return;
+    super.addEventListener(ESLIntersectionEvent.type, callback);
+    if (this.getEventListeners(ESLIntersectionEvent.type).length > 1) return;
     this.targets.forEach((target: Element) => this.observer$$.observe(target));
   }
 
   /** Unsubscribes from the observed target {@link Element} changes */
   public override removeEventListener(callback: EventListener): void;
-  public override removeEventListener(event: ESLIntersectionEventType, callback: EventListener): void;
+  public override removeEventListener(event: typeof ESLIntersectionEvent.type, callback: EventListener): void;
   public override removeEventListener(event: any, callback: EventListener = event): void {
-    if (typeof event !== 'string') event = ESLIntersectionEvent.TYPE;
-    if (!ESLIntersectionEvent.isValidEventType(event)) return;
+    if (typeof event !== 'string') event = ESLIntersectionEvent.type;
+    if (event !== ESLIntersectionEvent.type) return;
     super.removeEventListener(event, callback);
-    if (this.hasEventListener(event)) return;
+    if (this.hasEventListener(ESLIntersectionEvent.type)) return;
     this.observer$$.disconnect();
   }
 }
diff --git a/src/modules/esl-event-listener/core/targets/resize.target.event.ts b/src/modules/esl-event-listener/core/targets/resize.target.event.ts
index c920ddbbf..4770cccf5 100644
--- a/src/modules/esl-event-listener/core/targets/resize.target.event.ts
+++ b/src/modules/esl-event-listener/core/targets/resize.target.event.ts
@@ -4,9 +4,7 @@ import type {ESLResizeObserverTarget} from './resize.target';
 
 /** Custom event that {@link ESLResizeObserverTarget} produces */
 export class ESLElementResizeEvent extends UIEvent implements ResizeObserverEntry {
-  public static readonly TYPE = 'resize';
-  /** @deprecated Use {@link TYPE} instead */
-  public static readonly type = this.TYPE;
+  public static readonly type = 'resize';
 
   /** A reference to the {@link Element} or SVGElement being observed */
   public override readonly target: Element;
@@ -38,7 +36,7 @@ export class ESLElementResizeEvent extends UIEvent implements ResizeObserverEntr
   public readonly devicePixelContentBoxSize: readonly ResizeObserverSize[];
 
   protected constructor(target: Element) {
-    super(ESLElementResizeEvent.TYPE, {bubbles: false, cancelable: false});
+    super(ESLElementResizeEvent.type, {bubbles: false, cancelable: false});
     overrideEvent(this, 'target', target);
   }
 
diff --git a/src/modules/esl-event-listener/core/targets/resize.target.ts b/src/modules/esl-event-listener/core/targets/resize.target.ts
index 0ac6100ce..bcefeb0b9 100644
--- a/src/modules/esl-event-listener/core/targets/resize.target.ts
+++ b/src/modules/esl-event-listener/core/targets/resize.target.ts
@@ -54,7 +54,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget {
 
   /** Subscribes to the observed target {@link Element} changes */
   public override addEventListener(callback: EventListener): void;
-  public override addEventListener(event: typeof ESLElementResizeEvent.TYPE, callback: EventListener): void;
+  public override addEventListener(event: typeof ESLElementResizeEvent.type, callback: EventListener): void;
   public override addEventListener(event: any, callback: EventListener = event): void {
     if (typeof event === 'string' && event !== 'resize') {
       console.warn(`[ESL]: ESLResizeObserverTarget does not support '${event}' type`);
@@ -68,7 +68,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget {
 
   /** Unsubscribes from the observed target {@link Element} changes */
   public override removeEventListener(callback: EventListener): void;
-  public override removeEventListener(event: typeof ESLElementResizeEvent.TYPE, callback: EventListener): void;
+  public override removeEventListener(event: typeof ESLElementResizeEvent.type, callback: EventListener): void;
   public override removeEventListener(event: any, callback: EventListener = event): void {
     if (typeof event === 'string' && event !== 'resize') return;
 
diff --git a/src/modules/esl-event-listener/core/targets/swipe.target.event.ts b/src/modules/esl-event-listener/core/targets/swipe.target.event.ts
index 26bde0d6c..96b641fb7 100644
--- a/src/modules/esl-event-listener/core/targets/swipe.target.event.ts
+++ b/src/modules/esl-event-listener/core/targets/swipe.target.event.ts
@@ -31,9 +31,7 @@ export interface ESLSwipeGestureEventInfo {
  * Swipe event dispatched by {@link ESLSwipeGestureTarget}
  */
 export class ESLSwipeGestureEvent extends UIEvent implements ESLSwipeGestureEventInfo {
-  public static readonly TYPE = 'swipe';
-  /** @deprecated Use {@link TYPE} instead */
-  public static readonly type = this.TYPE;
+  public static readonly type = 'swipe';
 
   public override readonly target: Element;
 
@@ -47,7 +45,7 @@ export class ESLSwipeGestureEvent extends UIEvent implements ESLSwipeGestureEven
   public readonly duration: number;
 
   protected constructor(target: Element, swipeInfo: ESLSwipeGestureEventInfo) {
-    super(ESLSwipeGestureEvent.TYPE, {bubbles: false, cancelable: true});
+    super(ESLSwipeGestureEvent.type, {bubbles: false, cancelable: true});
     overrideEvent(this, 'target', target);
     Object.assign(this, swipeInfo);
   }
diff --git a/src/modules/esl-event-listener/core/targets/swipe.target.ts b/src/modules/esl-event-listener/core/targets/swipe.target.ts
index 86a1375b0..1e2aa493a 100644
--- a/src/modules/esl-event-listener/core/targets/swipe.target.ts
+++ b/src/modules/esl-event-listener/core/targets/swipe.target.ts
@@ -149,7 +149,7 @@ export class ESLSwipeGestureTarget extends SyntheticEventTarget {
    * Subscribes to pointerup and pointerdown event
    */
   public override addEventListener(callback: EventListener): void;
-  public override addEventListener(event: typeof ESLSwipeGestureEvent.TYPE, callback: EventListener): void;
+  public override addEventListener(event: typeof ESLSwipeGestureEvent.type, callback: EventListener): void;
   public override addEventListener(event: any, callback: EventListener = event): void {
     super.addEventListener(event, callback);
 
@@ -162,7 +162,7 @@ export class ESLSwipeGestureTarget extends SyntheticEventTarget {
    * Unsubscribes from the observed target {@link Element} changes
    */
   public override removeEventListener(callback: EventListener): void;
-  public override removeEventListener(event: typeof ESLSwipeGestureEvent.TYPE, callback: EventListener): void;
+  public override removeEventListener(event: typeof ESLSwipeGestureEvent.type, callback: EventListener): void;
   public override removeEventListener(event: any, callback: EventListener = event): void {
     super.removeEventListener(event, callback);
     if (!this.hasEventListener(event)) ESLEventListener.unsubscribe(this);
diff --git a/src/modules/esl-event-listener/core/targets/wheel.target.event.ts b/src/modules/esl-event-listener/core/targets/wheel.target.event.ts
index dd07b3e0d..33e3d9290 100644
--- a/src/modules/esl-event-listener/core/targets/wheel.target.event.ts
+++ b/src/modules/esl-event-listener/core/targets/wheel.target.event.ts
@@ -20,9 +20,7 @@ export interface ESLWheelEventInfo {
  * Wheel event dispatched by {@link ESLWheelTarget}
  */
 export class ESLWheelEvent extends UIEvent implements ESLWheelEventInfo {
-  public static readonly TYPE = 'longwheel';
-  /** @deprecated Use {@link TYPE} instead */
-  public static readonly type = this.TYPE;
+  public static readonly type = 'longwheel';
 
   public override readonly target: Element;
 
@@ -33,7 +31,7 @@ export class ESLWheelEvent extends UIEvent implements ESLWheelEventInfo {
   public readonly duration: number;
 
   protected constructor(target: Element, wheelInfo: ESLWheelEventInfo) {
-    super(ESLWheelEvent.TYPE);
+    super(ESLWheelEvent.type);
     overrideEvent(this, 'target', target);
     Object.assign(this, wheelInfo);
   }
diff --git a/src/modules/esl-event-listener/core/targets/wheel.target.ts b/src/modules/esl-event-listener/core/targets/wheel.target.ts
index b128fbacf..5820d82f6 100644
--- a/src/modules/esl-event-listener/core/targets/wheel.target.ts
+++ b/src/modules/esl-event-listener/core/targets/wheel.target.ts
@@ -138,7 +138,7 @@ export class ESLWheelTarget extends SyntheticEventTarget {
 
   /** Subscribes to wheel event */
   public override addEventListener(callback: EventListener): void;
-  public override addEventListener(event: typeof ESLWheelEvent.TYPE, callback: EventListener): void;
+  public override addEventListener(event: typeof ESLWheelEvent.type, callback: EventListener): void;
   public override addEventListener(event: any, callback: EventListener = event): void {
     super.addEventListener(event, callback);
 
@@ -148,7 +148,7 @@ export class ESLWheelTarget extends SyntheticEventTarget {
 
   /** Unsubscribes from the observed target {@link Element} wheel events */
   public override removeEventListener(callback: EventListener): void;
-  public override removeEventListener(event: typeof ESLWheelEvent.TYPE, callback: EventListener): void;
+  public override removeEventListener(event: typeof ESLWheelEvent.type, callback: EventListener): void;
   public override removeEventListener(event: any, callback: EventListener = event): void {
     super.removeEventListener(event, callback);
 
diff --git a/src/modules/esl-footnotes/core/esl-note.ts b/src/modules/esl-footnotes/core/esl-note.ts
index 36f5c5fe5..b7be484f4 100644
--- a/src/modules/esl-footnotes/core/esl-note.ts
+++ b/src/modules/esl-footnotes/core/esl-note.ts
@@ -6,7 +6,7 @@ import {promisifyTimeout, repeatSequence} from '../../esl-utils/async';
 import {ESLEventUtils} from '../../esl-utils/dom/events';
 import {ENTER, SPACE} from '../../esl-utils/dom/keys';
 import {scrollIntoView} from '../../esl-utils/dom/scroll';
-import {DeviceDetector} from '../../esl-utils/environment/device-detector';
+import {hasHover} from '../../esl-utils/environment/device-detector';
 import {ESLMediaQuery} from '../../esl-media-query/core';
 import {ESLTraversingQuery} from '../../esl-traversing-query/core';
 
@@ -61,7 +61,7 @@ export class ESLNote extends ESLBaseElement {
 
   /** Marker to allow track hover */
   public get allowHover(): boolean {
-    return DeviceDetector.hasHover && ESLMediaQuery.for(this.trackHover).matches;
+    return hasHover && ESLMediaQuery.for(this.trackHover).matches;
   }
   /** Marker to allow track clicks */
   public get allowClick(): boolean {
diff --git a/src/modules/esl-image/core/esl-image-iobserver.ts b/src/modules/esl-image/core/esl-image-iobserver.ts
index 8350f8d4a..00aba5f91 100644
--- a/src/modules/esl-image/core/esl-image-iobserver.ts
+++ b/src/modules/esl-image/core/esl-image-iobserver.ts
@@ -1,4 +1,4 @@
-import {DeviceDetector} from '../../esl-utils/environment/device-detector';
+import {isMobile} from '../../esl-utils/environment/device-detector';
 import type {ESLImage} from './esl-image';
 
 let iObserver: IntersectionObserver;
@@ -10,7 +10,7 @@ export function getIObserver(): IntersectionObserver {
       entries.forEach(handleViewport);
     }, {
       threshold: [0.01], // 0 + 1 are not correctly handled by all browsers
-      rootMargin: DeviceDetector.isMobile ? '250px' : '500px' // rootMargin value for IntersectionObserver
+      rootMargin: isMobile ? '250px' : '500px' // rootMargin value for IntersectionObserver
     });
   }
   return iObserver;
diff --git a/src/modules/esl-media-query/core/common/env-shortcuts.ts b/src/modules/esl-media-query/core/common/env-shortcuts.ts
index 85c8a2569..df550c6d2 100644
--- a/src/modules/esl-media-query/core/common/env-shortcuts.ts
+++ b/src/modules/esl-media-query/core/common/env-shortcuts.ts
@@ -1,4 +1,17 @@
-import {DeviceDetector} from '../../../esl-utils/environment/device-detector';
+import {
+  isAndroid,
+  isBlink,
+  isBot,
+  isEdgeHTML,
+  isGecko,
+  isIE,
+  isMobile,
+  isMobileIOS,
+  isMobileSafari,
+  isSafari,
+  isTouchDevice,
+  isWebkit
+} from '../../../esl-utils/environment/device-detector';
 import {ExportNs} from '../../../esl-utils/environment/export-ns';
 
 const shortcuts = new Map<string, string | boolean>();
@@ -36,23 +49,23 @@ export class ESLEnvShortcuts {
 }
 
 // Touch check
-ESLEnvShortcuts.add('touch', DeviceDetector.isTouchDevice);
+ESLEnvShortcuts.add('touch', isTouchDevice);
 
 // Basic device type shortcuts
-ESLEnvShortcuts.add('bot', DeviceDetector.isBot);
-ESLEnvShortcuts.add('mobile', DeviceDetector.isMobile);
-ESLEnvShortcuts.add('desktop', !DeviceDetector.isMobile);
-ESLEnvShortcuts.add('android', DeviceDetector.isAndroid);
-ESLEnvShortcuts.add('ios', DeviceDetector.isMobileIOS);
+ESLEnvShortcuts.add('bot', isBot);
+ESLEnvShortcuts.add('mobile', isMobile);
+ESLEnvShortcuts.add('desktop', !isMobile);
+ESLEnvShortcuts.add('android', isAndroid);
+ESLEnvShortcuts.add('ios', isMobileIOS);
 
 // Basic browser shortcuts
-ESLEnvShortcuts.add('ie', DeviceDetector.isIE);
-ESLEnvShortcuts.add('edge', DeviceDetector.isEdgeHTML);
-ESLEnvShortcuts.add('gecko', DeviceDetector.isGecko);
-ESLEnvShortcuts.add('webkit', DeviceDetector.isWebkit);
-ESLEnvShortcuts.add('blink', DeviceDetector.isBlink);
-ESLEnvShortcuts.add('safari', DeviceDetector.isSafari);
-ESLEnvShortcuts.add('safari-ios', DeviceDetector.isMobileSafari);
+ESLEnvShortcuts.add('ie', isIE);
+ESLEnvShortcuts.add('edge', isEdgeHTML);
+ESLEnvShortcuts.add('gecko', isGecko);
+ESLEnvShortcuts.add('webkit', isWebkit);
+ESLEnvShortcuts.add('blink', isBlink);
+ESLEnvShortcuts.add('safari', isSafari);
+ESLEnvShortcuts.add('safari-ios', isMobileSafari);
 
 declare global {
   export interface ESLLibrary {
diff --git a/src/modules/esl-media-query/core/common/screen-dpr.ts b/src/modules/esl-media-query/core/common/screen-dpr.ts
index 81c88b656..9732da80d 100644
--- a/src/modules/esl-media-query/core/common/screen-dpr.ts
+++ b/src/modules/esl-media-query/core/common/screen-dpr.ts
@@ -1,5 +1,5 @@
 import {ExportNs} from '../../../esl-utils/environment/export-ns';
-import {DeviceDetector} from '../../../esl-utils/environment/device-detector';
+import {isBot, isSafari} from '../../../esl-utils/environment/device-detector';
 
 /**
  * DPR preprocessor. Used to replace DPR shortcuts.
@@ -22,8 +22,8 @@ export class ESLScreenDPR {
     if (!ESLScreenDPR.VALUE_REGEXP.test(match)) return;
     const dpr = parseFloat(match);
     if (dpr < 0 || isNaN(dpr)) return;
-    if (ESLScreenDPR.ignoreBotsDpr && DeviceDetector.isBot && dpr > 1) return 'not all';
-    if (DeviceDetector.isSafari) return `(-webkit-min-device-pixel-ratio: ${dpr})`;
+    if (ESLScreenDPR.ignoreBotsDpr && isBot && dpr > 1) return 'not all';
+    if (isSafari) return `(-webkit-min-device-pixel-ratio: ${dpr})`;
     return `(min-resolution: ${ESLScreenDPR.toDPI(dpr)}dpi)`;
   }
 }
diff --git a/src/modules/esl-popup/core/esl-popup.ts b/src/modules/esl-popup/core/esl-popup.ts
index 0ffb6d4bd..bef11fe07 100644
--- a/src/modules/esl-popup/core/esl-popup.ts
+++ b/src/modules/esl-popup/core/esl-popup.ts
@@ -297,7 +297,7 @@ export class ESLPopup extends ESLToggleable {
   /** Actions to execute on activator intersection event. */
   @listen({
     auto: false,
-    event: ESLIntersectionEvent.TYPE,
+    event: ESLIntersectionEvent.type,
     target: ($popup: ESLPopup) => $popup.activator ? ESLIntersectionTarget.for($popup.activator, $popup.intersectionOptions) : [],
     condition: ($popup: ESLPopup) => !$popup.disableActivatorObservation
   })
diff --git a/src/modules/esl-toggleable/core/esl-toggleable.ts b/src/modules/esl-toggleable/core/esl-toggleable.ts
index 2a5d42b6b..09478b4d8 100644
--- a/src/modules/esl-toggleable/core/esl-toggleable.ts
+++ b/src/modules/esl-toggleable/core/esl-toggleable.ts
@@ -5,7 +5,7 @@ import {prop, attr, jsonAttr, listen} from '../../esl-utils/decorators';
 import {defined, copyDefinedKeys} from '../../esl-utils/misc/object';
 import {parseBoolean, toBooleanAttribute} from '../../esl-utils/misc/format';
 import {sequentialUID} from '../../esl-utils/misc/uid';
-import {DeviceDetector} from '../../esl-utils/environment/device-detector';
+import {hasHover} from '../../esl-utils/environment/device-detector';
 import {DelayedTask} from '../../esl-utils/async/delayed-task';
 import {ESLBaseElement} from '../../esl-base-element/core';
 import {findParent, isMatches} from '../../esl-utils/dom/traversing';
@@ -190,7 +190,7 @@ export class ESLToggleable extends ESLBaseElement {
   }
   /** Bind hover events listeners for the Toggleable itself */
   protected bindHoverStateTracking(track: boolean, hideDelay?: number | string): void {
-    if (!DeviceDetector.hasHover) return;
+    if (!hasHover) return;
     this._trackHoverDelay = track && hideDelay !== undefined ? +hideDelay : undefined;
     if (this._trackHover === track) return;
     this._trackHover = track;
diff --git a/src/modules/esl-trigger/core/esl-base-trigger.ts b/src/modules/esl-trigger/core/esl-base-trigger.ts
index 6f4b58a8c..81347e6c3 100644
--- a/src/modules/esl-trigger/core/esl-base-trigger.ts
+++ b/src/modules/esl-trigger/core/esl-base-trigger.ts
@@ -1,5 +1,5 @@
 import {ESLBaseElement} from '../../esl-base-element/core';
-import {DeviceDetector} from '../../esl-utils/environment/device-detector';
+import {hasHover} from '../../esl-utils/environment/device-detector';
 import {isElement} from '../../esl-utils/dom/api';
 import {setAttr} from '../../esl-utils/dom/attr';
 import {CSSClassUtils} from '../../esl-utils/dom/class';
@@ -74,7 +74,7 @@ export abstract class ESLBaseTrigger extends ESLBaseElement {
 
   /** Marker to allow track hover */
   public get allowHover(): boolean {
-    return DeviceDetector.hasHover && ESLMediaQuery.for(this.trackHover).matches;
+    return hasHover && ESLMediaQuery.for(this.trackHover).matches;
   }
   /** Marker to allow track clicks */
   public get allowClick(): boolean {
diff --git a/src/modules/esl-utils/decorators/prop.ts b/src/modules/esl-utils/decorators/prop.ts
index 8a578908b..98cd464f4 100644
--- a/src/modules/esl-utils/decorators/prop.ts
+++ b/src/modules/esl-utils/decorators/prop.ts
@@ -1,32 +1,11 @@
-import type {PropertyProvider} from '../misc/functions';
-
 /** Property configuration */
 export type OverrideDecoratorConfig = {
-  /** To define readonly property */
-  readonly?: boolean;
   /** To define enumerable property */
   enumerable?: boolean;
+  /** To define readonly property */
+  readonly?: boolean;
 };
 
-/** Builds getter from provider */
-function getter<T>(provider: PropertyProvider<T>) {
-  return function (this: any): T {
-    return provider.call(this, this);
-  };
-}
-
-/** Builds own property setter */
-function setter(name: string, readonly?: boolean) {
-  if (readonly) return (): void => void 0;
-  return function (value: any): void {
-    Object.defineProperty(this, name, {
-      value,
-      writable: true,
-      configurable: true
-    });
-  };
-}
-
 /**
  * `@prop` is auxiliary decorator to define a field on the prototype level.
  *` @prop` can be used to override decorated property from the parent level
@@ -37,31 +16,20 @@ function setter(name: string, readonly?: boolean) {
  * The class property initial value is a part of object creation, so it goes to the object itself,
  * while the @override value is defined on the prototype level.
  *
- * If the value is a provider function, it will be resolved via instance each time property accessed.
- *
- * @param value - value or PropertyProvider to set up in prototype
+ * @param value - value to setup in prototype
  * @param prototypeConfig - prototype property configuration
  */
-export function prop<T = any>(value?: T | PropertyProvider<T>, prototypeConfig: OverrideDecoratorConfig = {}) {
+export function prop(value?: any, prototypeConfig: OverrideDecoratorConfig = {}) {
   return function (obj: any, name: string): any {
     if (Object.hasOwnProperty.call(obj, name)) {
       throw new TypeError('Can\'t override own property');
     }
-    if (typeof value === 'function') {
-      Object.defineProperty(obj, name, {
-        get: getter(value as PropertyProvider<any>),
-        set: setter(name, prototypeConfig.readonly),
-        enumerable: prototypeConfig.enumerable,
-        configurable: true
-      });
-    } else {
-      Object.defineProperty(obj, name, {
-        value,
-        writable: !prototypeConfig.readonly,
-        enumerable:  prototypeConfig.enumerable,
-        configurable: true
-      });
-    }
-    return {/* To make babel transpiler work */};
+    Object.defineProperty(obj, name, {
+      value,
+      writable: !prototypeConfig.readonly,
+      enumerable:  !prototypeConfig.enumerable,
+      configurable: true
+    });
+    return {};
   };
 }
diff --git a/src/modules/esl-utils/dom/script.ts b/src/modules/esl-utils/dom/script.ts
index bcafa727e..43e77f275 100644
--- a/src/modules/esl-utils/dom/script.ts
+++ b/src/modules/esl-utils/dom/script.ts
@@ -1,20 +1,11 @@
-import {setAttr} from './attr';
-
-export interface LoadScriptAttributes {
-  crossorigin?: boolean | 'anonymous' | 'use-credentials' | '';
-  integrity?: string;
-  referrerpolicy?: ReferrerPolicy;
-}
-
 /**
- * Creates new script element with specified attributes
- * Note: as the script will be added dynamically, it will be treated as async by the browser
+ * Creates new async script tag by id and src
  */
-const createScript = (id: string, src: string, attrs: LoadScriptAttributes): HTMLScriptElement => {
+const createAsyncScript = (id: string, src: string): HTMLScriptElement => {
   const script = document.createElement('script');
   script.id = id;
+  script.async = true;
   script.src = src;
-  Object.entries(attrs).forEach(([name, value]) => setAttr(script, name, value));
   return script;
 };
 
@@ -22,12 +13,11 @@ const createScript = (id: string, src: string, attrs: LoadScriptAttributes): HTM
  * Common function that loads script async
  * @param id - unique script id that is used as a marker to prevent future load
  * @param src - script src (url) to load
- * @param attrs - additional attributes to set on the script tag
  */
-export function loadScript(id: string, src: string, attrs: LoadScriptAttributes = {}): Promise<Event> {
+export function loadScript(id: string, src: string): Promise<Event> {
   return new Promise((resolve, reject) => {
     const script: HTMLScriptElement =
-      (document.getElementById(id) || createScript(id, src, attrs)) as HTMLScriptElement;
+      (document.getElementById(id) || createAsyncScript(id, src)) as HTMLScriptElement;
     const state = script.getAttribute('state');
 
     switch (state) {
diff --git a/src/modules/esl-utils/fixes/viewport.ts b/src/modules/esl-utils/fixes/viewport.ts
index b96e0ab29..4db3a32f2 100644
--- a/src/modules/esl-utils/fixes/viewport.ts
+++ b/src/modules/esl-utils/fixes/viewport.ts
@@ -1,5 +1,5 @@
 import {rafDecorator} from '../async/raf';
-import {DeviceDetector} from '../environment/device-detector';
+import {isIE} from '../environment/device-detector';
 
 /**
  * Small utility to provide 100vw and 100vh alternative CSS Variables
@@ -13,7 +13,7 @@ export class ESLVSizeCSSProxy {
   /** Inits custom CSS variables for viewport sizes and it's observation */
   public static observe(): void {
     // IE doesn’t support CSS Variables (hopefully same as 100vh issue :D)
-    if (DeviceDetector.isIE) return;
+    if (isIE) return;
 
     this.update();
     window.addEventListener('resize', this.updateDebounced);


Test patch:
diff --git a/src/modules/esl-event-listener/test/targets/intersection.test.ts b/src/modules/esl-event-listener/test/targets/intersection.test.ts
index 12b83ab0f..a3467bf9d 100644
--- a/src/modules/esl-event-listener/test/targets/intersection.test.ts
+++ b/src/modules/esl-event-listener/test/targets/intersection.test.ts
@@ -114,7 +114,7 @@ describe('ESLIntersectionTarget proxy', () => {
         intersectionRatio: 0.5
       });
       IntersectionObserverMock.trigger($el, entry);
-      expect(listener).lastCalledWith(expect.objectContaining({type: ESLIntersectionEvent.TYPE}));
+      expect(listener).lastCalledWith(expect.objectContaining({type: ESLIntersectionEvent.type}));
       expect(listener).lastCalledWith(expect.objectContaining(entry));
     });
   });
diff --git a/src/modules/esl-media-query/test/esl-media-query.test.ts b/src/modules/esl-media-query/test/esl-media-query.test.ts
index b87532eec..3d0e46aa1 100644
--- a/src/modules/esl-media-query/test/esl-media-query.test.ts
+++ b/src/modules/esl-media-query/test/esl-media-query.test.ts
@@ -1,11 +1,11 @@
-import {DDMock} from '../../esl-utils/test/deviceDetector.mock';
+import {DevicesMock} from '../../esl-utils/test/deviceDetector.mock';
 import {ESLMediaQuery, ESLScreenBreakpoints, ESLScreenDPR} from '../core';
 import {getMatchMediaMock} from '../../esl-utils/test/matchMedia.mock';
 import {ESLMediaChangeEvent} from '../core/conditions/media-query-base';
 
 /**
  * ESL Media Query tests
- * Require mocked DeviceDetector and matchMedia
+ * Require mocked matchMedia and environment device checks
  *
  * Realization of ESL Media Query implies next query parsing cases
  * (examples for desktop device):
@@ -88,19 +88,19 @@ describe('ESLMediaQuery', () => {
       ['@1x', '(-webkit-min-device-pixel-ratio: 1)'],
       ['@2.3x', '(-webkit-min-device-pixel-ratio: 2.3)'],
     ])('Dpr %s replacement', (query, expected) => {
-      DDMock.isSafari = true;
+      DevicesMock.isSafari = true;
       expect(ESLMediaQuery.from(query).toString()).toBe(expected);
-      DDMock.isSafari = false;
+      DevicesMock.isSafari = false;
     });
 
     test('Bot DPR override test', () => {
-      DDMock.isBot = true;
+      DevicesMock.isBot = true;
       ESLScreenDPR.ignoreBotsDpr = true;
 
       expect(ESLMediaQuery.from('@1x').toString()).toBe('(min-resolution: 96.0dpi)');
       expect(ESLMediaQuery.from('@3x, @2.4x').toString()).toBe('not all');
 
-      DDMock.isBot = false;
+      DevicesMock.isBot = false;
       ESLScreenDPR.ignoreBotsDpr = false;
     });
   });
diff --git a/src/modules/esl-trigger/test/esl-trigger.actions.test.ts b/src/modules/esl-trigger/test/esl-trigger.actions.test.ts
index ee0e4e7d3..3ff2e30af 100644
--- a/src/modules/esl-trigger/test/esl-trigger.actions.test.ts
+++ b/src/modules/esl-trigger/test/esl-trigger.actions.test.ts
@@ -6,9 +6,7 @@ jest.mock('../../esl-utils/dom/ready', () => ({
 }));
 
 jest.mock('../../esl-utils/environment/device-detector', () => ({
-  DeviceDetector: {
-    hasHover: true
-  }
+  hasHover: true
 }));
 
 describe('ESLTrigger event handling', () => {
diff --git a/src/modules/esl-utils/decorators/test/prop.test.ts b/src/modules/esl-utils/decorators/test/prop.test.ts
index a7156a6d4..ed626e842 100644
--- a/src/modules/esl-utils/decorators/test/prop.test.ts
+++ b/src/modules/esl-utils/decorators/test/prop.test.ts
@@ -1,108 +1,22 @@
 import '../../../../polyfills/es5-target-shim';
-
 import {attr, boolAttr, jsonAttr} from '../../../esl-utils/decorators';
 import {prop} from '../prop';
 
 describe('Decorator: @prop', () => {
-  test('Declared property can be defined instance based through ES initial value', () => {
-    class TestClass {
-      @prop()
-      public field: string = '123';
-    }
-
-    const el = new TestClass();
-    expect(el.field).toBe('123');
-    expect(Object.getOwnPropertyDescriptor(el, 'field')).not.toBeNull();
-  });
-
-  describe('Provider based property resolves value trough it', () => {
-    const provider = jest.fn();
-    class TestClass {
-      @prop(provider)
-      public field: any;
-
-      @prop(provider, {readonly: true})
-      public readonlyField: any;
-
-      @prop(provider, {enumerable: true})
-      public enumerableField: any;
-    }
-
-    test('If provider function passed - value resolves trough it', () => {
-      const obj = new TestClass();
-      provider.mockReturnValue('initial');
-      expect(obj.field).toBe('initial');
-      provider.mockReturnValue('new');
-      expect(obj.field).toBe('new');
-    });
-
-    test('If provider function passed - provider receives instance as a context and argument', () => {
-      provider.mockImplementation(function() { return this; });
-      const obj = new TestClass();
-      expect(obj.field).toBe(obj);
-      expect(provider).toHaveBeenCalledWith(obj);
-    });
-
-    test('If provider function passed - resolver declared on prototype level', () => {
-      const obj = new TestClass();
-      expect(Object.hasOwnProperty.call(obj, 'field')).toBe(false);
-      expect(Object.getOwnPropertyDescriptor(TestClass.prototype, 'field')).not.toBeNull();
-    });
-
-    test('If provider function passed - value can be overwritten on instance', () => {
-      const obj = new TestClass();
-      const obj2 = new TestClass();
-      provider.mockReturnValue('initial');
-      obj.field = 'new';
-      expect(obj.field).toBe('new');
-      expect(obj2.field).toBe('initial');
-    });
-
-    test('If provider function passed - overwritten value could be cleared with delete', () => {
-      const obj = new TestClass();
-      provider.mockReturnValue('initial');
-      obj.field = 'new';
-      expect(obj.field).toBe('new');
-      delete obj.field;
-      expect(obj.field).toBe('initial');
-    });
-
-    test('If provider function passed - readonly property should ignores overwritten value', () => {
-      const obj = new TestClass();
-      provider.mockReturnValue('initial');
-      obj.readonlyField = 'new';
-      expect(obj.readonlyField).toBe('initial');
-    });
-
-    test('If provider function passed - property is not enumerable by default', () => {
-      expect(Object.keys(TestClass.prototype)).not.toContain('field');
-    });
-
-    test('If provider function passed - enumerable property should be enumerable', () => {
-      expect(Object.keys(TestClass.prototype)).toContain('enumerableField');
-    });
-  });
-
-  test('Overriding own property produce error', () => {
-    expect(() => {
-      class TestElement extends HTMLElement {
-        @prop({value: ''})
-        @attr()
-        public field: string;
-      }
-      new TestElement();
-    }).toThrowError(/own property/);
-  });
-
-  describe('Overriding @attr works fine', () => {
-    class TestBaseElement extends HTMLElement {
-      @attr()
-      public field: string;
-      @attr()
-      public field4?: string;
-      @attr({readonly: true})
-      public readonlyField: string;
-    }
+  class TestBaseElement extends HTMLElement {
+    @attr()
+    public field: string;
+    @boolAttr()
+    public field2: boolean;
+    @jsonAttr()
+    public field3: {a: number};
+    @attr()
+    public field4?: string;
+    @attr({readonly: true})
+    public readonlyField: string;
+  }
+
+  describe('Overriding @attr', () => {
     class TestElement extends TestBaseElement {
       @prop('test')
       public override field: string;
@@ -113,21 +27,21 @@ describe('Decorator: @prop', () => {
     }
     customElements.define('attr-override-1', TestElement);
 
-    test('@prop should override simple @attr decorator', () => {
+    test('should override simple @attr decorator', () => {
       const el = new TestElement();
       expect(el.field).toBe('test');
     });
-    test('@prop should override readonly @attr decorator', () => {
+    test('should override readonly @attr decorator', () => {
       const el = new TestElement();
       expect(el.readonlyField).toBe('test');
     });
-    test('@prop override should be writeable', () => {
+    test('override should be writeable', () => {
       const el = new TestElement();
       el.field = el.readonlyField = 'hi';
       expect(el.field).toBe('hi');
       expect(el.readonlyField).toBe('hi');
     });
-    test('Original decorator should not be executed', () => {
+    test('original decorator should not be executed', () => {
       const el = new TestElement();
       el.field = 'hi';
       expect(el.getAttribute('field')).toBe(null);
@@ -140,82 +54,100 @@ describe('Decorator: @prop', () => {
     });
   });
 
-  describe('Overriding @boolAttr works fine', () => {
-    class TestBaseElement extends HTMLElement {
-      @boolAttr()
-      public field: boolean;
+  describe('Overriding with a non writable', () => {
+    class TestElement extends TestBaseElement {
+      @prop('test', {enumerable: true, readonly: true}) public override field: string;
+      @prop(true, {enumerable: true, readonly: true}) public override field2: boolean;
     }
+    class TestElement2 extends TestElement {
+      @prop() public override field: string = 'test2';
+      @prop(false) public override field2: boolean;
+    }
+    customElements.define('attr-writable-override-1', TestElement);
+    customElements.define('attr-writable-override-2', TestElement2);
+
+    test('should override @attr and @boolAttr decorator', () => {
+      const el = new TestElement();
+      expect(el.field).toBe('test');
+      expect(el.field2).toBe(true);
+    });
+    test('override should not be writeable', () => {
+      const el = new TestElement();
+      expect(() => el.field = 'hi').toThrowError();
+      expect(() => el.field2 = false).toThrowError();
+    });
+    test('should have undefined as a default', () => {
+      const el = new TestElement2();
+      expect(el.field).toBe('test2');
+      expect(el.field2).toBe(false);
+    });
+  });
+
+  describe('Overriding @boolAttr', () => {
     class TestElement extends TestBaseElement {
       @prop(true)
-      public override field: boolean;
+      public override field2: boolean;
     }
     customElements.define('bool-attr-override-1', TestElement);
 
-    test('@prop should override simple @boolAttr decorator', () => {
+    test('should override simple @boolAttr decorator', () => {
       const el = new TestElement();
-      expect(el.field).toBe(true);
+      expect(el.field2).toBe(true);
     });
-    test('@prop override should be writeable', () => {
+    test('override should be writeable', () => {
       const el = new TestElement();
-      el.field = false;
-      expect(el.field).toBe(false);
+      el.field2 = false;
+      expect(el.field2).toBe(false);
     });
-    test('Original decorator should not be executed', () => {
+    test('original decorator should not be executed', () => {
       const el = new TestElement();
-      el.field = true;
       expect(el.getAttribute('field2')).toBe(null);
     });
   });
 
-  describe('Overriding @jsonAttr with @prop works fine', () => {
-    class TestBaseElement extends HTMLElement {
-      @jsonAttr()
-      public field: {a: number};
-    }
+  describe('Overriding @jsonAttr', () => {
     class TestElement extends TestBaseElement {
       @prop({a: 2})
-      public override field: {a: number};
+      public override field3: {a: number};
     }
     customElements.define('json-attr-override-1', TestElement);
 
-    test('@prop should override simple @jsonAttr decorator', () => {
+    test('should override simple @jsonAttr decorator', () => {
       const el = new TestElement();
-      expect(el.field).toEqual({a: 2});
+      expect(el.field3).toEqual({a: 2});
     });
-    test('@prop override should be writeable by default', () => {
+    test('override should be writeable', () => {
       const el = new TestElement();
-      el.field = {a: 4};
-      expect(el.field).toEqual({a: 4});
+      el.field3 = {a: 4};
+      expect(el.field3).toEqual({a: 4});
     });
-    test('Original decorator should not be executed', () => {
+    test('original decorator should not be executed', () => {
       const el = new TestElement();
-      el.field = {a: 4};
-      expect(el.getAttribute('field')).toBe(null);
+      expect(el.getAttribute('field3')).toBe(null);
     });
   });
 
-  describe('Overriding chain, with a non writable @prop', () => {
-    class TestBaseElement extends HTMLElement {
-      @attr()
-      public field: string;
-      @boolAttr()
-      public field2: boolean;
-    }
+  describe('Overridden property can be defined through ES initial value ', () => {
     class TestElement extends TestBaseElement {
-      @prop('test', {readonly: true}) public override field: string;
-      @prop(true, {readonly: true}) public override field2: boolean;
-    }
-    class TestElement2 extends TestElement {
-      @prop() public override field: string = 'test2';
-      @prop(false) public override field2: boolean;
+      @prop()
+      public override field: string = '123';
     }
-    customElements.define('attr-writable-override-1', TestElement);
-    customElements.define('attr-writable-override-2', TestElement2);
+    customElements.define('es-initial-attr-override-1', TestElement);
 
-    test('Double overriding works fine', () => {
-      const el = new TestElement2();
-      expect(el.field).toBe('test2');
-      expect(el.field2).toBe(false);
+    test('should override simple @attr decorator', () => {
+      const el = new TestElement();
+      expect(el.field).toBe('123');
     });
   });
+
+  test('Overriding own property produce error', () => {
+    expect(() => {
+      class TestElement extends HTMLElement {
+        @prop({value: ''})
+        @attr()
+        public field: string;
+      }
+      new TestElement();
+    }).toThrowError(/own property/);
+  });
 });
diff --git a/src/modules/esl-utils/dom/test/script.test.ts b/src/modules/esl-utils/dom/test/script.test.ts
deleted file mode 100644
index a8439c8d2..000000000
--- a/src/modules/esl-utils/dom/test/script.test.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import {loadScript} from '../script';
-
-describe('loadScript', () => {
-  beforeEach(() => {
-    document.title = 'loadScript test page';
-  });
-  afterEach(() => {
-    document.head.innerHTML = '';
-  });
-
-  test('should create a script element with a specified id in the head', () => {
-    loadScript('test-script', 'http://example.com');
-    const script = document.querySelector('head > script#test-script');
-    expect(script).toBeInstanceOf(HTMLScriptElement);
-  });
-  test('should create a script element with a specified URL as src', () => {
-    loadScript('test-script', 'http://example.org');
-    const script = document.querySelector('head > script#test-script') as HTMLScriptElement;
-    expect(script && script.getAttribute('src')).toBe('http://example.org');
-  });
-  test('should use the existing script element for the same content instead of creating a new one', () => {
-    loadScript('test-script', 'http://example.com');
-    loadScript('test-script', 'http://example.com');
-    loadScript('test-script', 'http://example.com');
-    const scripts = document.querySelectorAll('head > script[src="http://example.com"]');
-    expect(scripts).toHaveLength(1);
-  });
-
-  describe('should create attributes on the script element according to the passed list', () => {
-    test('should create an attribute with value when passed string value', () => {
-      loadScript('test', 'http://example.com', {crossorigin: 'anonymous'});
-      const script = document.querySelector('head > script#test') as HTMLScriptElement;
-      expect(script && script.getAttribute('crossorigin')).toBe('anonymous');
-    });
-    test('should create an attribute without value when passed a true boolean value', () => {
-      loadScript('test', 'http://example.com', {crossorigin: true});
-      const script = document.querySelector('head > script#test') as HTMLScriptElement;
-      expect(script && script.getAttribute('crossorigin')).toBe('');
-    });
-    test('should not create an attribute when passed a false boolean value', () => {
-      loadScript('test', 'http://example.com', {crossorigin: false});
-      const script = document.querySelector('head > script#test') as HTMLScriptElement;
-      expect(script && script.getAttribute('crossorigin')).toBe(null);
-    });
-    test('should have all attributes specified by the list', () => {
-      loadScript('test', 'http://example.com', {
-        crossorigin: true,
-        integrity: 'sha384-abcde...',
-        referrerpolicy: 'no-referrer'
-      });
-      const script = document.querySelector('head > script#test') as HTMLScriptElement;
-      expect(script && script.hasAttribute('crossorigin')).toBe(true);
-      expect(script && script.hasAttribute('integrity')).toBe(true);
-      expect(script && script.hasAttribute('referrerpolicy')).toBe(true);
-    });
-  });
-
-  describe('should return a Promise', () => {
-    let promise: Promise<Event>;
-    let script: HTMLScriptElement | null;
-    beforeEach(() => {
-      promise = loadScript('test', 'http://example.com/');
-      script = document.querySelector('head > script#test');
-    });
-    test('should return Promise', () => {
-      return expect(promise).toBeInstanceOf(Promise);
-    });
-
-    test('should return a Promise that resolves with instance of Event on successful script loading', () => {
-      script?.dispatchEvent(new Event('load'));
-      return expect(promise).resolves.toBeInstanceOf(Event);
-    });
-    test('should return a Promise that resolves with load type Event on successful script loading', () => {
-      script?.dispatchEvent(new Event('load'));
-      return expect(promise).resolves.toMatchObject({type: 'load'});
-    });
-    test('should return a Promise that rejects with instance of Event if the script fails to load', () => {
-      script?.dispatchEvent(new Event('error'));
-      return expect(promise).rejects.toBeInstanceOf(Event);
-    });
-    test('should return a Promise that resolves with error type Event if the script fails to load', () => {
-      script?.dispatchEvent(new Event('error'));
-      return expect(promise).rejects.toMatchObject({type: 'error'});
-    });
-  });
-
-  describe('should have proper state attribute', () => {
-    let script: HTMLScriptElement | null;
-    beforeEach(() => {
-      loadScript('test', 'http://example.com/').catch(() => {});
-      script = document.querySelector('head > script#test');
-    });
-    test('should not have a state attribute on pending script loading', () => {
-      expect(script?.getAttribute('state')).toBe(null);
-    });
-    test('should have state="success" attribute on successful script loading', () => {
-      script?.dispatchEvent(new Event('load'));
-      expect(script?.getAttribute('state')).toBe('success');
-    });
-    test('should have state="error" attribute on failed script loading', async () => {
-      script?.dispatchEvent(new Event('error'));
-      return expect(script?.getAttribute('state')).toBe('error');
-    });
-  });
-});
diff --git a/src/modules/esl-utils/test/deviceDetector.mock.ts b/src/modules/esl-utils/test/deviceDetector.mock.ts
index 7cf0bec57..4e02948c4 100644
--- a/src/modules/esl-utils/test/deviceDetector.mock.ts
+++ b/src/modules/esl-utils/test/deviceDetector.mock.ts
@@ -1,9 +1,7 @@
-export const DDMock = {
+export const DevicesMock = {
   isMobile: false,
   isSafari: false,
   isBot: false
 };
 
-jest.mock('../environment/device-detector', () => ({
-  DeviceDetector: DDMock
-}));
+jest.mock('../environment/device-detector', () => DevicesMock);