Repository: exadel-inc/esl
Pull request #375 (2021-08-09 09:49:04)

fix(esl-popup): wrong popup position when the popup is shown at the first time
Fixes:
- wrong popup position when the popup is shown at the first time
- IE fix 'ShadowRoot is undefined'
- IE fix 'properties x and y are absent in DOMRect'


Patch:
diff --git a/.eslintrc.json b/.eslintrc.json
index c8c84dfc4..1b5c2dc17 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -18,8 +18,7 @@
     "import",
     "sonarjs",
     "editorconfig",
-    "@typescript-eslint",
-    "eslint-plugin-tsdoc"
+    "@typescript-eslint"
   ],
   "rules": {
     "comma-dangle": "off",
@@ -74,8 +73,6 @@
     "use-isnan": "error",
     "valid-typeof": "error",
 
-    "tsdoc/syntax": "warn",
-
     "@typescript-eslint/consistent-type-imports": ["error", { "prefer": "type-imports" }],
     "@typescript-eslint/object-curly-spacing": ["warn",  "never"],
     "@typescript-eslint/explicit-function-return-type": "off",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b73374ff1..4fa6d023d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,3 @@
-# [2.9.0](https://github.com/exadel-inc/esl/compare/v2.8.2...v2.9.0) (2021-08-08)
-
-
-### Features
-
-* **esl-utils:** promise functional declarations ([19b1e2b](https://github.com/exadel-inc/esl/commit/19b1e2b7e5b0fe2cf4431b35a06b07122108ddfb))
-
 # [3.0.0-beta.15](https://github.com/exadel-inc/esl/compare/v3.0.0-beta.14...v3.0.0-beta.15) (2021-08-05)
 
 
diff --git a/package-lock.json b/package-lock.json
index c3d9dac0e..8dd02e60c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1572,36 +1572,6 @@
       "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==",
       "dev": true
     },
-    "@microsoft/tsdoc": {
-      "version": "0.13.2",
-      "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz",
-      "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==",
-      "dev": true
-    },
-    "@microsoft/tsdoc-config": {
-      "version": "0.15.2",
-      "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz",
-      "integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==",
-      "dev": true,
-      "requires": {
-        "@microsoft/tsdoc": "0.13.2",
-        "ajv": "~6.12.6",
-        "jju": "~1.4.0",
-        "resolve": "~1.19.0"
-      },
-      "dependencies": {
-        "resolve": {
-          "version": "1.19.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
-          "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
-          "dev": true,
-          "requires": {
-            "is-core-module": "^2.1.0",
-            "path-parse": "^1.0.6"
-          }
-        }
-      }
-    },
     "@nodelib/fs.scandir": {
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
@@ -5242,16 +5212,6 @@
       "integrity": "sha512-KKFofk1LPjGHWeAZijYWv32c/C4mz+OAeBNVxhxHu1hknrTOhu415MWC8qKdAdsmOlBPShs9evM4mI1o7MNMhw==",
       "dev": true
     },
-    "eslint-plugin-tsdoc": {
-      "version": "0.2.14",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.14.tgz",
-      "integrity": "sha512-fJ3fnZRsdIoBZgzkQjv8vAj6NeeOoFkTfgosj6mKsFjX70QV256sA/wq+y/R2+OL4L8E79VVaVWrPeZnKNe8Ng==",
-      "dev": true,
-      "requires": {
-        "@microsoft/tsdoc": "0.13.2",
-        "@microsoft/tsdoc-config": "0.15.2"
-      }
-    },
     "eslint-scope": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -8575,12 +8535,6 @@
         }
       }
     },
-    "jju": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
-      "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=",
-      "dev": true
-    },
     "js-beautify": {
       "version": "1.14.0",
       "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.0.tgz",
diff --git a/package.json b/package.json
index 7732d1d14..682bed2a0 100644
--- a/package.json
+++ b/package.json
@@ -97,7 +97,6 @@
     "eslint-plugin-editorconfig": "^3.0.2",
     "eslint-plugin-import": "^2.23.4",
     "eslint-plugin-sonarjs": "^0.9.1",
-    "eslint-plugin-tsdoc": "^0.2.14",
     "foreach-cli": "^1.8.1",
     "highlight.js": "^11.1.0",
     "html-minifier": "^4.0.0",
diff --git a/src/modules/draft/esl-carousel/core/esl-carousel.ts b/src/modules/draft/esl-carousel/core/esl-carousel.ts
index 8ccbbca5d..7d60517bd 100644
--- a/src/modules/draft/esl-carousel/core/esl-carousel.ts
+++ b/src/modules/draft/esl-carousel/core/esl-carousel.ts
@@ -68,7 +68,7 @@ export class ESLCarousel extends ESLBaseElement {
   }
 
   /**
-   * @returns number first active index
+   * @returns {number} first active index
    */
   get firstIndex(): number {
     const index = this.$slides.findIndex((slide) => {
diff --git a/src/modules/draft/esl-carousel/plugin/esl-carousel-plugin.ts b/src/modules/draft/esl-carousel/plugin/esl-carousel-plugin.ts
index b7e72cf63..3a92b8a30 100644
--- a/src/modules/draft/esl-carousel/plugin/esl-carousel-plugin.ts
+++ b/src/modules/draft/esl-carousel/plugin/esl-carousel-plugin.ts
@@ -3,12 +3,13 @@ import {ESLCarousel} from '../core/esl-carousel';
 
 /**
  * {@link ESLCarousel} Plugin base class.
- * The ESL Carousel Plugin should have the dom representation so it's extends {@link ESLBaseElement}
+ * The ESL Carousel Plugin should have the dom representation so it's {@extends HTMLElement}.
  * Use the attributes to path the plugin options, the same as with any custom elements.
+ * @abstract
  */
 export abstract class ESLCarouselPlugin extends ESLBaseElement {
   /**
-   * Marker to define the restriction for the plugin placement.
+   * {boolean} freePlacement marker define the restriction for the plugin placement.
    * If freePlacement is false - plugin element should be child of ESLCarousel element.
    * If freePlacement is true - plugin can be placed anywhere inside of carousel.
    */
@@ -28,13 +29,13 @@ export abstract class ESLCarouselPlugin extends ESLBaseElement {
   private _carousel?: ESLCarousel;
 
   /**
-   * @returns plugin unique key, ESLCarousel can not own more then one plugin with the same key
+   * @returns {string} plugin unique key, ESLCarousel can not own more then one plugin with the same key
    */
   public get key() {
     return this.nodeName.toLowerCase();
   }
   /**
-   * @returns owner of plugin
+   * @returns {ESLCarousel} owner of plugin
    */
   public get carousel(): ESLCarousel {
     return this._carousel as ESLCarousel;
diff --git a/src/modules/esl-a11y-group/core/esl-a11y-group.ts b/src/modules/esl-a11y-group/core/esl-a11y-group.ts
index e4e2e20a3..42a1e213c 100644
--- a/src/modules/esl-a11y-group/core/esl-a11y-group.ts
+++ b/src/modules/esl-a11y-group/core/esl-a11y-group.ts
@@ -22,11 +22,11 @@ export class ESLA11yGroup extends ESLBaseElement {
   /** Activate target (via click event) on selection */
   @boolAttr({}) public activateSelected: boolean;
 
-  /** @returns HTMLElement root element of the group */
+  /** @returns {HTMLElement} root element of the group */
   public get $root(): HTMLElement {
     return this.parentElement as HTMLElement;
   }
-  /** @returns HTMLElement[] targets of the group */
+  /** @returns {HTMLElement[]} targets of the group */
   public get $targets(): HTMLElement[] {
     return TraversingQuery.all(this.targets, this.$root) as [];
   }
@@ -73,21 +73,21 @@ export class ESLA11yGroup extends ESLBaseElement {
     this.activateSelected && targetEl.click();
   }
 
-  /** @returns HTMLElement next target fot trigger */
+  /** @returns {HTMLElement} next target fot trigger */
   public next(trigger: HTMLElement) {
     const triggers = this.$targets;
     const index = triggers.indexOf(trigger);
     return triggers[(index + 1) % triggers.length];
   }
 
-  /** @returns HTMLElement previous target fot trigger */
+  /** @returns {HTMLElement} previous target fot trigger */
   public prev(trigger: HTMLElement): HTMLElement | undefined {
     const triggers = this.$targets;
     const index = triggers.indexOf(trigger);
     return triggers[(index - 1 + triggers.length) % triggers.length];
   }
 
-  /** @returns HTMLElement currently focused element from targets */
+  /** @returns {HTMLElement} currently focused element from targets */
   public current(): HTMLElement | null {
     const $active = document.activeElement as HTMLElement;
     return this.$targets.includes($active) ? $active : null;
diff --git a/src/modules/esl-base-element/core/esl-base-element.ts b/src/modules/esl-base-element/core/esl-base-element.ts
index 8a31257c0..f3218ee88 100644
--- a/src/modules/esl-base-element/core/esl-base-element.ts
+++ b/src/modules/esl-base-element/core/esl-base-element.ts
@@ -27,7 +27,7 @@ export abstract class ESLBaseElement extends HTMLElement {
    * Dispatch component custom event.
    * Uses 'esl:' prefix for event name, overridable to customize event namespaces.
    * @param eventName - event name
-   * @param eventInit - custom event init. See {@link CustomEventInit}
+   * @param [eventInit] - custom event init. See {@link CustomEventInit}
    */
   public $$fire(eventName: string, eventInit?: CustomEventInit): boolean {
     return EventUtils.dispatch(this, 'esl:' + eventName, eventInit);
@@ -35,7 +35,7 @@ export abstract class ESLBaseElement extends HTMLElement {
 
   /**
    * Register component in the {@link customElements} registry
-   * @param tagName - custom tag name to register custom element
+   * @param [tagName] - custom tag name to register custom element
    */
   public static register(this: typeof ESLBaseElement, tagName?: string) {
     tagName = tagName || this.is;
diff --git a/src/modules/esl-base-element/decorators/attr.ts b/src/modules/esl-base-element/decorators/attr.ts
index e7a2e32c4..31ea77ae7 100644
--- a/src/modules/esl-base-element/decorators/attr.ts
+++ b/src/modules/esl-base-element/decorators/attr.ts
@@ -36,7 +36,7 @@ const buildAttrName =
 /**
  * Decorator to map current property to element attribute value.
  * Maps string type property.
- * @param config - mapping configuration. See {@link AttrDescriptor}
+ * @param [config] - mapping configuration. See {@link AttrDescriptor}
  */
 export const attr = (config: AttrDescriptor = {}) => {
   config = Object.assign({defaultValue: ''}, config);
diff --git a/src/modules/esl-base-element/decorators/bool-attr.ts b/src/modules/esl-base-element/decorators/bool-attr.ts
index bbb6552a1..9eb06163b 100644
--- a/src/modules/esl-base-element/decorators/bool-attr.ts
+++ b/src/modules/esl-base-element/decorators/bool-attr.ts
@@ -29,7 +29,7 @@ const buildAttrName =
 /**
  * Decorator to map current property to element boolean (marker) attribute state.
  * Maps boolean type property.
- * @param config - mapping configuration. See {@link BoolAttrDescriptor}
+ * @param [config] - mapping configuration. See {@link BoolAttrDescriptor}
  */
 export const boolAttr = (config: BoolAttrDescriptor = {}) => {
   return (target: ESLBaseElement, propName: string) => {
diff --git a/src/modules/esl-base-element/decorators/json-attr.ts b/src/modules/esl-base-element/decorators/json-attr.ts
index c43a7b05e..9157d8d29 100644
--- a/src/modules/esl-base-element/decorators/json-attr.ts
+++ b/src/modules/esl-base-element/decorators/json-attr.ts
@@ -44,7 +44,7 @@ const buildAttrName =
 /**
  * Decorator to map current property to element attribute value using JSON (de-)serialization rules.
  * Maps object type property.
- * @param config - mapping configuration. See {@link JsonAttrDescriptor}
+ * @param [config] - mapping configuration. See {@link JsonAttrDescriptor}
  */
 export const jsonAttr = <T>(config: JsonAttrDescriptor<T> = {}) => {
   config = Object.assign({defaultValue: {}}, config);
diff --git a/src/modules/esl-forms/esl-select-list/core/esl-select-wrapper.ts b/src/modules/esl-forms/esl-select-list/core/esl-select-wrapper.ts
index 5a71205b3..da0a7ee76 100644
--- a/src/modules/esl-forms/esl-select-list/core/esl-select-wrapper.ts
+++ b/src/modules/esl-forms/esl-select-list/core/esl-select-wrapper.ts
@@ -39,7 +39,7 @@ export interface ESLSelectModel {
 }
 
 /**
- * Base class for {@link HTMLSelectElement} wrapper element, implements {@link ESLSelectModel} options source
+ * Base class for {@link HTMLSelectElement} wrapper element, implements {@link ESLSelectModel) options source
  */
 export abstract class ESLSelectWrapper extends ESLBaseElement implements ESLSelectModel {
   protected static observationConfig: MutationObserverInit = {
diff --git a/src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts b/src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts
index 682250171..3645cfa90 100644
--- a/src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts
+++ b/src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts
@@ -12,7 +12,7 @@ import type {ToggleableActionParams} from '../../../esl-toggleable/core/esl-togg
  * ESLSelectDropdown component
  * @author Alexey Stsefanovich (ala'n)
  *
- * Auxiliary inner custom component to render {@link ESLSelect} dropdown section
+ * Auxiliary inner custom component to render {@link ESLSelect) dropdown section
  * Uses {@link ESLSelectList} to render the content
  */
 export class ESLSelectDropdown extends ESLToggleable {
diff --git a/src/modules/esl-image/core/esl-image.shape.ts b/src/modules/esl-image/core/esl-image.shape.ts
index 37c5986a1..ab1560848 100644
--- a/src/modules/esl-image/core/esl-image.shape.ts
+++ b/src/modules/esl-image/core/esl-image.shape.ts
@@ -3,7 +3,7 @@
  * Used for JSX declaration
  */
 export interface ESLImageTagShape {
-  /** Define {@link ESLMediaRuleList} query for image source */
+  /** Define {SmartRule} query for image source */
   'data-src': string;
   /** Define image alt text */
   alt: string;
diff --git a/src/modules/esl-media-query/core/common/screen-breakpoint.ts b/src/modules/esl-media-query/core/common/screen-breakpoint.ts
index 1dbc97d76..582f1b228 100644
--- a/src/modules/esl-media-query/core/common/screen-breakpoint.ts
+++ b/src/modules/esl-media-query/core/common/screen-breakpoint.ts
@@ -7,8 +7,6 @@ const registry = new Map<string, ESLScreenBreakpoint>();
  * @author Yuliya Adamskaya, Alexey Stsefanovich (ala'n)
  *
  * Screen Breakpoint registry is used to provide custom breakpoints for {@link ESLMediaQuery}
- *
- * @implements IMediaQueryPreprocessor statically
  */
 @ExportNs('ScreenBreakpoints')
 export abstract class ESLScreenBreakpoints {
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 5bfe4328f..d4b82761d 100644
--- a/src/modules/esl-media-query/core/common/screen-dpr.ts
+++ b/src/modules/esl-media-query/core/common/screen-dpr.ts
@@ -11,7 +11,7 @@ import {DeviceDetector} from '../../../esl-utils/environment/device-detector';
 export class ESLScreenDPR {
   protected static readonly VALUE_REGEXP = /(\d(\.\d)?)x/;
 
-  /** Option to exclude dpr greater then 2 for bots */
+  /** Option to exclude dpr > 2 for bots */
   public static ignoreBotsDpr = false;
 
   public static toDPI(dpr: number) {
diff --git a/src/modules/esl-media-query/core/esl-media-query.ts b/src/modules/esl-media-query/core/esl-media-query.ts
index 93d813ccd..e03cba1ca 100644
--- a/src/modules/esl-media-query/core/esl-media-query.ts
+++ b/src/modules/esl-media-query/core/esl-media-query.ts
@@ -23,9 +23,9 @@ export interface IMediaQueryPreprocessor {
  * Utility to support extended MediaQuery features
  * Supports
  * - CSS MediaQuery matching check
- * - DPR display queries (`@x1|@x2|@x3`)
- * - Registered screen default sizes (breakpoints) shortcuts (`@[-|+](XS|SM|MD|LG|XL)`)
- * - Device and browser shortcuts (`@MOBILE|@DESKTOP|@IE`)
+ * - DPR display queries (@x1 | @x2 | @x3)
+ * - Registered screen default sizes (breakpoints) shortcuts @[-|+](XS|SM|MD|LG|XL)
+ * - Device and browser shortcuts (@MOBILE|@DESKTOP|@ie)
  * - Custom static shortcuts and custom query preprocessors
  * - `not` logic operation (can have multiple not operators before any term of the query)
  * - `or` or `,` logical operator (have a lowest priority)
@@ -33,7 +33,7 @@ export interface IMediaQueryPreprocessor {
  *
  * Building query process:
  *
- * [Building query logical tree] - [preprocess nodes queries] - [building native MediaQueryList nodes] - [query tree optimization]
+ * [Building query logical tree] -> [preprocess nodes queries] -> [building native MediaQueryList nodes] -> [query tree optimization]
  */
 @ExportNs('MediaQuery')
 export abstract class ESLMediaQuery implements IMediaQueryCondition {
diff --git a/src/modules/esl-media-query/core/esl-media-rule-list.ts b/src/modules/esl-media-query/core/esl-media-rule-list.ts
index 37d119030..fabda416c 100644
--- a/src/modules/esl-media-query/core/esl-media-rule-list.ts
+++ b/src/modules/esl-media-query/core/esl-media-rule-list.ts
@@ -52,13 +52,11 @@ export class ESLMediaRuleList<T = any> extends Observable<RuleChangedCallback<T>
   /**
    * Creates `ESLMediaRuleList` from two strings with a value  and conditions tuple
    *
-   * @param values - values tuple string (uses '|' as separator)
-   * @param mask - media conditions tuple string (uses '|' as separator)
-   *
    * @example
-   * ```ts
    * ESLMediaRuleList.fromTuple('1|2|3|4|5', '@XS|@SM|@MD|@LG|@XL')
-   * ```
+   *
+   * @param values - values tuple string (uses '|' as separator)
+   * @param mask - media conditions tuple string (uses '|' as separator)
    */
   public static parseTuple(values: string, mask: string) {
     const valueList = values.split('|');
diff --git a/src/modules/esl-media/core/esl-media-manager.ts b/src/modules/esl-media/core/esl-media-manager.ts
index 4618ee906..763c7632d 100644
--- a/src/modules/esl-media/core/esl-media-manager.ts
+++ b/src/modules/esl-media/core/esl-media-manager.ts
@@ -9,12 +9,16 @@ const managerMap = new Map<string, ESLMedia>();
  * @author Alexey Stsefanovich (ala'n), Yuliya Adamskaya
  */
 export class MediaGroupRestrictionManager {
-  /** @internal */
+  /**
+   * @debug info
+   */
   static get managerMap() {
     return managerMap;
   }
 
-  /** Register instance play state in group */
+  /**
+   * Register instance play state in group
+   */
   public static registerPlay(instance: ESLMedia) {
     if (instance.group) {
       const current = managerMap.get(instance.group);
@@ -26,7 +30,9 @@ export class MediaGroupRestrictionManager {
     }
   }
 
-  /** Unregister instance */
+  /**
+   * Unregister instance
+   */
   public static unregister(instance: ESLMedia) {
     if (instance.group) {
       const reg = managerMap.get(instance.group);
diff --git a/src/modules/esl-media/core/esl-media-provider.ts b/src/modules/esl-media/core/esl-media-provider.ts
index 45ffc1bfa..d1c20a513 100644
--- a/src/modules/esl-media/core/esl-media-provider.ts
+++ b/src/modules/esl-media/core/esl-media-provider.ts
@@ -56,7 +56,10 @@ export abstract class BaseProvider {
     this.component = component;
   }
 
-  /** Wraps _ready promise */
+  /**
+   * Wraps _ready promise
+   * @returns {Promise}
+   */
   get ready() {
     if (!this._ready) {
       const res = Promise.reject('Not Initialized');
@@ -80,16 +83,16 @@ export abstract class BaseProvider {
     return (this.constructor as typeof BaseProvider).providerName;
   }
 
-  /** @returns current state of the player */
+  /** @returns {PlayerStates} - current state of the player */
   public abstract get state(): PlayerStates;
 
-  /** @returns recommended aspect ratio */
+  /** @returns {number} - recommended aspect ratio */
   public abstract get defaultAspectRatio(): number;
 
-  /** @returns resource duration */
+  /** @returns {number} - resource duration */
   public abstract get duration(): number;
 
-  /** @returns resource current time */
+  /** @returns {number} - resource current time */
   public abstract get currentTime(): number;
 
   /** Low-level provider 'seek to' method implementation */
diff --git a/src/modules/esl-media/core/esl-media.ts b/src/modules/esl-media/core/esl-media.ts
index 8ba7f0363..97d19a0da 100644
--- a/src/modules/esl-media/core/esl-media.ts
+++ b/src/modules/esl-media/core/esl-media.ts
@@ -91,8 +91,8 @@ export class ESLMedia extends ESLBaseElement {
   private deferredReinitialize = debounce(() => this.reinitInstance());
 
   /**
-   * Map object with possible Player States, values:
-   * BUFFERING, ENDED, PAUSED, PLAYING, UNSTARTED, VIDEO_CUED, UNINITIALIZED
+   * @enum Map with possible Player States
+   * values: BUFFERING, ENDED, PAUSED, PLAYING, UNSTARTED, VIDEO_CUED, UNINITIALIZED
    */
   static get PLAYER_STATES() {
     return PlayerStates;
@@ -208,42 +208,55 @@ export class ESLMedia extends ESLBaseElement {
     CSSClassUtils.toggle(targetEl, this.loadClsDeclined, !active);
   }
 
-  /** Seek to given position of media */
+  /**
+   * Seek to given position of media
+   * @returns {Promise | void}
+   */
   public seekTo(pos: number) {
     return this._provider && this._provider.safeSeekTo(pos);
   }
 
   /**
    * Start playing media
-   * @param allowActivate - allows to remove disabled marker
+   * @param {boolean} allowActivate
+   * @returns {Promise | void}
    */
-  public play(allowActivate: boolean = false): Promise<void> | null {
+  public play(allowActivate: boolean = false) {
     if (this.disabled && allowActivate) {
       this.disabled = false;
       this.deferredReinitialize.cancel();
       this.reinitInstance();
     }
-    if (!this.canActivate()) return null;
+    if (!this.canActivate()) return;
     return this._provider && this._provider.safePlay();
   }
 
-  /** Pause playing media */
-  public pause(): Promise<void> | null {
+  /**
+   * Pause playing media
+   * @returns {Promise | void}
+   */
+  public pause() {
     return this._provider && this._provider.safePause();
   }
 
-  /** Stop playing media */
-  public stop(): Promise<void> | null {
+  /**
+   * Stop playing media
+   * @returns {Promise | void}
+   */
+  public stop() {
     return this._provider && this._provider.safeStop();
   }
 
-  /** Toggle play/pause state of the media */
-  public toggle(): Promise<void> | null {
+  /**
+   * Toggle play/pause state of the media
+   * @returns {Promise | void}
+   */
+  public toggle() {
     return this._provider && this._provider.safeToggle();
   }
 
   /** @override */
-  public focus(): void {
+  public focus() {
     this._provider && this._provider.focus();
   }
 
diff --git a/src/modules/esl-media/providers/brightcove-provider.ts b/src/modules/esl-media/providers/brightcove-provider.ts
index 573c58aa3..666c92400 100644
--- a/src/modules/esl-media/providers/brightcove-provider.ts
+++ b/src/modules/esl-media/providers/brightcove-provider.ts
@@ -37,7 +37,9 @@ export class BrightcoveProvider extends BaseProvider {
     };
   }
 
-  /** Loads player API according defined settings */
+  /**
+   * Loads player API according defined settings
+   * */
   protected static loadAPI(account: BCPlayerAccount): Promise<Event> {
     const apiSrc =
       `//players.brightcove.net/${account.accountId}/${account.playerId}_default/index.min.js`;
@@ -48,7 +50,9 @@ export class BrightcoveProvider extends BaseProvider {
     return loadScript(API_SCRIPT_ID, apiSrc);
   }
 
-  /** Build video brightcove element */
+  /**
+   * Build video brightcove element
+   */
   protected buildVideo() {
     const el = document.createElement('video-js');
     el.id = 'esl-media-brightcove-' + randUID();
@@ -66,7 +70,9 @@ export class BrightcoveProvider extends BaseProvider {
     return el;
   }
 
-  /** Utility method to convert api event to promise */
+  /**
+   * Utility method to convert api event to promise
+   */
   protected $$fromEvent(eventName: string): Promise<void> {
     if (!this._api) return Promise.reject();
     return new Promise((resolve, reject) => this._api ? this._api.one(eventName, resolve) : reject());
@@ -74,7 +80,7 @@ export class BrightcoveProvider extends BaseProvider {
 
   /**
    * Executes as soon as api script detected or loaded.
-   * @returns promise with provided API
+   * @returns {Promise<VideoJsPlayer>} - promise with provided API
    */
   protected onAPILoaded(): Promise<void> | void {
     if (typeof window.bc !== 'function' || typeof window.videojs !== 'function') {
@@ -88,6 +94,7 @@ export class BrightcoveProvider extends BaseProvider {
   /**
    * Executes after API ready state resolved
    * Basic onAPIReady should be called to subscribe to API state
+   * @returns {Promise | void}
    */
   protected onAPIReady(): Promise<void> | void {
     console.debug('ESL Media: Brightcove API is ready ', this);
diff --git a/src/modules/esl-popup/core/esl-popup.ts b/src/modules/esl-popup/core/esl-popup.ts
index c616ffa9f..70f402022 100644
--- a/src/modules/esl-popup/core/esl-popup.ts
+++ b/src/modules/esl-popup/core/esl-popup.ts
@@ -62,18 +62,6 @@ export class ESLPopup extends ESLToggleable {
     this.$arrow = this.querySelector('span.esl-popup-arrow');
   }
 
-  protected bindEvents() {
-    super.bindEvents();
-    window.addEventListener('resize', this._deferredUpdatePosition);
-    window.addEventListener('scroll', this._deferredUpdatePosition);
-  }
-
-  protected unbindEvents() {
-    super.unbindEvents();
-    window.removeEventListener('resize', this._deferredUpdatePosition);
-    window.removeEventListener('scroll', this._deferredUpdatePosition);
-  }
-
   protected get _isPositioningAlongHorizontal() {
     return ['left', 'right'].includes(this.position);
   }
@@ -94,8 +82,13 @@ export class ESLPopup extends ESLToggleable {
     this._offsetTrigger = params.offsetTrigger || 0;
     this._offsetWindow = params.offsetWindow || 0;
 
-    this._updatePosition();
-    this.activator && this._addActivatorObserver(this.activator);
+    this.style.visibility = 'hidden'; // eliminates the blinking of the popup at the previous position
+    setTimeout(() => {
+      // running as a separate task solves the problem with incorrect positioning on the first showing
+      this._updatePosition();
+      this.style.visibility = 'visible';
+      this.activator && this._addActivatorObserver(this.activator);
+    });
   }
 
   public onHide(params: PopupActionParams) {
@@ -161,6 +154,9 @@ export class ESLPopup extends ESLToggleable {
     const observer = new IntersectionObserver(this.onActivatorIntersection, options);
     observer.observe(target);
 
+    window.addEventListener('resize', this._deferredUpdatePosition);
+    window.addEventListener('scroll', this._deferredUpdatePosition);
+
     this._activatorObserver = {
       unsubscribers,
       observer
@@ -168,6 +164,8 @@ export class ESLPopup extends ESLToggleable {
   }
 
   protected _removeActivatorObserver(target: HTMLElement) {
+    window.removeEventListener('resize', this._deferredUpdatePosition);
+    window.removeEventListener('scroll', this._deferredUpdatePosition);
     this._activatorObserver.observer?.disconnect();
     this._activatorObserver.observer = undefined;
     this._activatorObserver.unsubscribers?.forEach((cb) => cb());
@@ -180,7 +178,7 @@ export class ESLPopup extends ESLToggleable {
     const triggerRect = this.activator.getBoundingClientRect();
     const popupRect = this.getBoundingClientRect();
     const arrowRect = this.$arrow ? this.$arrow.getBoundingClientRect() : new DOMRect();
-    const trigger = new Rect(triggerRect.x, triggerRect.y + window.pageYOffset, triggerRect.width, triggerRect.height);
+    const trigger = new Rect(triggerRect.left, triggerRect.top + window.pageYOffset, triggerRect.width, triggerRect.height);
     const innerMargin = this._offsetTrigger + arrowRect.width / 2;
 
     const config = {
diff --git a/src/modules/esl-traversing-query/core/esl-traversing-query.ts b/src/modules/esl-traversing-query/core/esl-traversing-query.ts
index 9cb609de7..118277533 100644
--- a/src/modules/esl-traversing-query/core/esl-traversing-query.ts
+++ b/src/modules/esl-traversing-query/core/esl-traversing-query.ts
@@ -18,20 +18,19 @@ type CollectionProcessor = (els: Element[], sel: string) => Element[];
  * - ::first, ::last and :nth(#) limitation pseudo-selectors
  * - ::filter, ::not filtration pseudo-selectors
  *
- * @example
- * - `#id .class [attr]` - find by CSS selector in a current document
- * - ` ` - get current base element
- * - `::next` - get next sibling element
- * - `::prev` - get previous sibling element
- * - `::parent` - get base element parent
- * - `::parent(#id .class [attr])` - find the closest parent matching passed selector
- * - `::child(#id .class [attr])` - find direct child element(s) that match passed selector
- * - `::find(#id .class [attr])` - find child element(s) that match passed selector
- * - `::find(buttons, a)::not([hidden])` - find all buttons and anchors that are not have hidden attribute
- * - `::find(buttons, a)::filter(:first-child)` - find all buttons and anchors that are first child in container
- * - `::parent::child(some-tag)` - find direct child element(s) that match tag 'some-tag' in the parent
- * - `#id .class [attr]::parent` - find parent of element matching selector '#id .class [attr]' in document
- * - `::find(.row)::last::parent` - find parent of the last element matching selector '.row' from the base element subtree
+ * @example "#id .class [attr]" - find by CSS selector in a current document
+ * @example "" - get current base element
+ * @example "::next" - get next sibling element
+ * @example "::prev" - get previous sibling element
+ * @example "::parent" - get base element parent
+ * @example "::parent(#id .class [attr])" - find the closest parent matching passed selector
+ * @example "::child(#id .class [attr])" - find direct child element(s) that match passed selector
+ * @example "::find(#id .class [attr])" - find child element(s) that match passed selector
+ * @example "::find(buttons, a)::not([hidden])" - find all buttons and anchors that are not have hidden attribute
+ * @example "::find(buttons, a)::filter(:first-child)" - find all buttons and anchors that are first child in container
+ * @example "::parent::child(some-tag)" - find direct child element(s) that match tag 'some-tag' in the parent
+ * @example "#id .class [attr]::parent" - find parent of element matching selector '#id .class [attr]' in document
+ * @example "::find(.row)::last::parent" - find parent of the last element matching selector '.row' from the base element subtree
  */
 @ExportNs('TraversingQuery')
 export class TraversingQuery {
@@ -54,7 +53,7 @@ export class TraversingQuery {
   };
 
   /**
-   * @returns RegExp that selects all known processors in query string
+   * @return RegExp that selects all known processors in query string
    * e.g. /(::parent|::child|::next|::prev)/
    */
   private static get PROCESSORS_REGEX() {
@@ -102,11 +101,11 @@ export class TraversingQuery {
     return this.traverseChain(initial, tuple(parts), findFirst);
   }
 
-  /** @returns first matching element reached via {@link TraversingQuery} rules */
+  /** @return first matching element reached via {@class TraversingQuery} rules */
   static first(query: string, base?: Element): Element | null {
     return TraversingQuery.traverse(query, true, base)[0] || null;
   }
-  /** @returns Array of all matching elements reached via {@link TraversingQuery} rules */
+  /** @return Array of all matching elements reached via {@class TraversingQuery} rules */
   static all(query: string, base?: Element): Element[] {
     return TraversingQuery.traverse(query, false, base);
   }
diff --git a/src/modules/esl-utils/async/debounce.ts b/src/modules/esl-utils/async/debounce.ts
index 4a1ff1358..6a203efc1 100644
--- a/src/modules/esl-utils/async/debounce.ts
+++ b/src/modules/esl-utils/async/debounce.ts
@@ -1,11 +1,11 @@
-import {createDeferred} from './promise';
+import {PromiseUtils} from './promise';
 
 import type {AnyToAnyFnSignature} from '../misc/functions';
 import type {Deferred, PromisifyResultFn} from './promise';
 
 /** Debounced<F> is a function wrapper type for a function decorated via debounce */
 export interface Debounced<F extends AnyToAnyFnSignature> extends PromisifyResultFn<F> {
-  /** Promise of deferred function call */
+  /** {Promise} of deferred function call */
   promise: Promise<ReturnType<F> | void>;
   /** Cancel debounced call */
   cancel(): void;
@@ -16,15 +16,16 @@ export interface Debounced<F extends AnyToAnyFnSignature> extends PromisifyResul
  * Debounced function delays invoking func until after wait milliseconds have elapsed
  * since the last time the debounced function was invoked.
  * The func is invoked with the last arguments provided to the debounced function.
- * @param fn - function to decorate
- * @param wait - time to debounce
+ * @param fn
+ * @param [wait]
+ * @returns {Function}
  */
 export function debounce<F extends AnyToAnyFnSignature>(fn: F, wait = 10): Debounced<F> {
   let timeout: number | null = null;
   let deferred: Deferred<ReturnType<F>> | null = null;
 
   function debouncedSubject(...args: any[]) {
-    deferred = deferred || createDeferred();
+    deferred = deferred || PromiseUtils.deferred();
     (typeof timeout === 'number') && clearTimeout(timeout);
     timeout = window.setTimeout(() => {
       timeout = null;
diff --git a/src/modules/esl-utils/async/delayed-task.ts b/src/modules/esl-utils/async/delayed-task.ts
index 8fe8a181b..faeaa038d 100644
--- a/src/modules/esl-utils/async/delayed-task.ts
+++ b/src/modules/esl-utils/async/delayed-task.ts
@@ -15,14 +15,13 @@ export class DelayedTask {
     this._fn && this._fn();
   };
 
-  /** @returns Function of currently deferred (planned) task */
+  /** @return {Function} of currently deferred (planned) task */
   public get fn() {
     return this._fn;
   }
 
   /**
-   * Cancel deferred task and planning passed
-   * @param task - task function
+   * Cancel deferred task and planning passed {@param task}
    * @param delay - time to delay task execution
    *  - pass negative or false to execute task immediately
    *  - pass 0 to plan task to the macrotask
diff --git a/src/modules/esl-utils/async/promise.ts b/src/modules/esl-utils/async/promise.ts
index 57ff63cc8..db8bb332f 100644
--- a/src/modules/esl-utils/async/promise.ts
+++ b/src/modules/esl-utils/async/promise.ts
@@ -1,14 +1,5 @@
 import type {AnyToAnyFnSignature} from '../misc/functions';
 
-/**
- * @returns Promise that will be resolved in `timeout` with optional `payload`
- */
-export function promisifyTimeout<T>(timeout: number, payload?: T): Promise<T> {
-  return new Promise<T>((resolve) =>
-    setTimeout(resolve.bind(null, payload), timeout)
-  );
-}
-
 /** Interface to describe abstract listenable target */
 export type ListenableTarget = {
   addEventListener: (
@@ -23,64 +14,6 @@ export type ListenableTarget = {
   ) => void;
 };
 
-/**
- * @returns Promise that will be resolved by dispatching `event` on `target`
- * Or it will be rejected in `timeout` if it's specified
- * Optional `options` for addEventListener can be also specified
- */
-export function promisifyEvent(
-  target: ListenableTarget,
-  event: string,
-  timeout?: number | null | undefined,
-  options?: boolean | AddEventListenerOptions
-): Promise<Event> {
-  return new Promise((resolve, reject) => {
-    function eventCallback(e: Event) {
-      target.removeEventListener(event, eventCallback, options);
-      resolve(e);
-    }
-
-    target.addEventListener(event, eventCallback, options);
-    if (typeof timeout === 'number' && timeout >= 0) {
-      setTimeout(() => reject(new Error('Rejected by timeout')), timeout);
-    }
-  });
-}
-
-/**
- * Short helper to make Promise from element state marker
- * @returns Promise that will be resolved if the target `marker` property is truthful or `event` is dispatched
- * @example
- * `const imgReady = promisifyMarker(eslImage, 'ready');`
- */
-export function promisifyMarker(target: HTMLElement, marker: string, event: string = marker): Promise<HTMLElement> {
-  if ((target as any)[marker]) return Promise.resolve(target);
-  return promisifyEvent(target, event).then(() => target);
-}
-
-/**
- * Call `callback` limited by `tryCount` amount of times with interval in `timeout` ms
- * @returns Promise that will be resolved as soon as callback returns truthy value, or reject it by limit.
- */
-export function tryUntil<T>(callback: () => T, tryCount = 2, timeout = 100): Promise<T> {
-  return new Promise((resolve, reject) => {
-    (function check(){
-      let result: T | undefined;
-      try {
-        result = callback();
-      } catch {
-        result = undefined;
-      }
-
-      if (result || (tryCount--) < 0) {
-        result ? resolve(result) : reject(new Error('Rejected by limit of tries'));
-      } else {
-        setTimeout(check, timeout);
-      }
-    })();
-  });
-}
-
 /** Deferred object represents promise with it's resolve/reject methods */
 export type Deferred<T> = {
   /** Wrapped promise */
@@ -91,54 +24,108 @@ export type Deferred<T> = {
   reject: (arg?: any) => void;
 };
 
-/**
- * Create Deferred Object that wraps promise and its resolve and reject callbacks
- */
-export function createDeferred<T>(): Deferred<T> {
-  let reject: any;
-  let resolve: any;
-  // Both reject and resolve will be assigned anyway while the Promise constructing.
-  const promise = new Promise<T>((res, rej) => {
-    resolve = res;
-    reject = rej;
-  });
-  return {promise, resolve, reject};
-}
-
-/** Creates function type with the same signature but with the result type wrapped into promise */
+/** Return function type with the same signature but with the result type wrapped into promise */
 export type PromisifyResultFn<F extends AnyToAnyFnSignature> =
   ((...args: Parameters<F>) => Promise<ReturnType<F> | void>);
 
-/**
- * Safe wrap for Promise.resolve to use in Promise chain
- * @example
- * `const resolvedPromise = rejectedPromise.catch(resolvePromise);`
- */
-export function resolvePromise<T>(arg: T | PromiseLike<T>): Promise<T> {
-  return Promise.resolve(arg);
-}
-
-/**
- * Safe wrap for Promise.reject to use in Promise chain
- * @example
- * `const rejectedPromise = resolvedPromise.then(rejectPromise);`
- */
-export function rejectPromise<T = never>(arg?: T | PromiseLike<T>): Promise<T> {
-  return Promise.reject(arg);
-}
-
 /**
  * Promise utils helper class
- * Note: use individual methods in case you need correct "tree shaking"
  */
 export abstract class PromiseUtils {
-  static fromTimeout = promisifyTimeout;
-  static fromEvent = promisifyEvent;
-  static fromMarker = promisifyMarker;
+  /**
+   * @return {Promise} that will be resolved in {@param timeout} with optional {@param payload}
+   */
+  static fromTimeout<T>(timeout: number, payload?: T): Promise<T> {
+    return new Promise<T>((resolve) =>
+      setTimeout(resolve.bind(null, payload), timeout)
+    );
+  }
+
+  /**
+   * @return {Promise} that will be resolved by dispatching {@param event} on {@param target}
+   * Or it will be rejected in {@param timeout} if it's specified
+   * Optional {@param options} for addEventListener can be also specified
+   */
+  static fromEvent(
+    target: ListenableTarget,
+    event: string,
+    timeout?: number | null | undefined,
+    options?: boolean | AddEventListenerOptions
+  ): Promise<Event> {
+    return new Promise((resolve, reject) => {
+      function eventCallback(e: Event) {
+        target.removeEventListener(event, eventCallback, options);
+        resolve(e);
+      }
+
+      target.addEventListener(event, eventCallback, options);
+      if (typeof timeout === 'number' && timeout >= 0) {
+        setTimeout(() => reject(new Error('Rejected by timeout')), timeout);
+      }
+    });
+  }
+
+  /**
+   * Short helper to make Promise from element state marker
+   * Marker should be accessible and listenable
+   * @example
+   * const imgReady = PromiseUtils.fromMarker(eslImage, 'ready');
+   */
+  static fromMarker(target: HTMLElement, marker: string, event?: string): Promise<HTMLElement> {
+    if ((target as any)[marker]) return Promise.resolve(target);
+    return PromiseUtils.fromEvent(target, event || marker).then(() => target);
+  }
+
+  /**
+   * Safe wrap for Promise.resolve to use in Promise chain
+   * @example
+   * const resolvedPromise = rejectedPromise.catch(PromiseUtils.resolve);
+   */
+  static resolve<T>(arg: T | PromiseLike<T>): Promise<T> {
+    return Promise.resolve(arg);
+  }
+
+  /**
+   * Safe wrap for Promise.reject to use in Promise chain
+   * @example
+   * const rejectedPromise = resolvedPromise.then(PromiseUtils.resolve);
+   */
+  static reject<T = never>(arg?: T | PromiseLike<T>): Promise<T> {
+    return Promise.reject(arg);
+  }
 
-  static tryUntil = tryUntil;
+  /**
+   * Call {@param callback} limited by {@param tryCount} amount of times with interval in {@param timeout} ms
+   * @return {Promise} that will be resolved as soon as callback returns truthy value, or reject it by limit.
+   */
+  static tryUntil<T>(callback: () => T, tryCount = 2, timeout = 100): Promise<T> {
+    return new Promise((resolve, reject) => {
+      const interval = setInterval(() => {
+        let result: T | undefined;
+        try {
+          result = callback();
+        } catch {
+          result = undefined;
+        }
+        if (result || --tryCount < 0) {
+          clearInterval(interval);
+          result ? resolve(result) : reject(new Error('Rejected by limit of tries'));
+        }
+      }, timeout);
+    });
+  }
 
-  static deferred = createDeferred;
-  static resolve = resolvePromise;
-  static reject = rejectPromise;
+  /**
+   * Create Deferred Object that wraps promise and its resolve and reject callbacks
+   */
+  static deferred<T>(): Deferred<T> {
+    let reject: any;
+    let resolve: any;
+    // Both reject and resolve will be assigned anyway while the Promise constructing.
+    const promise = new Promise<T>((res, rej) => {
+      resolve = res;
+      reject = rej;
+    });
+    return {promise, resolve, reject};
+  }
 }
diff --git a/src/modules/esl-utils/async/raf.ts b/src/modules/esl-utils/async/raf.ts
index 047469268..956564511 100644
--- a/src/modules/esl-utils/async/raf.ts
+++ b/src/modules/esl-utils/async/raf.ts
@@ -2,12 +2,14 @@ import type {AnyToVoidFnSignature} from '../misc/functions';
 
 /**
  * Postpone action after next render
+ * @param {function} callback
  */
 export const afterNextRender = (callback: () => void) => requestAnimationFrame(() => requestAnimationFrame(callback));
 
 /**
  * Decorate function to schedule execution after next render
- * @returns decorated function
+ * @param {function} fn
+ * @returns {function} - decorated function
  */
 export const rafDecorator = <T extends AnyToVoidFnSignature>(fn: T): T => {
   let lastArgs: any[] | null = null; // null if no calls requested
diff --git a/src/modules/esl-utils/async/throttle.ts b/src/modules/esl-utils/async/throttle.ts
index a07c68bee..80e1126fa 100644
--- a/src/modules/esl-utils/async/throttle.ts
+++ b/src/modules/esl-utils/async/throttle.ts
@@ -1,19 +1,20 @@
-import {createDeferred} from './promise';
+import {PromiseUtils} from './promise';
 
 import type {AnyToAnyFnSignature} from '../misc/functions';
 import type {Deferred, PromisifyResultFn} from './promise';
 
 /** Throttled<F> is a function wrapper type for a function decorated via throttle */
 export interface Throttled<F extends AnyToAnyFnSignature> extends PromisifyResultFn<F> {
-  /** Promise of throttled function call */
+  /** {Promise} of throttled function call */
   promise: Promise<ReturnType<F> | void>;
 }
 
 /**
  * Creates a throttled executed function.
  * The func is invoked with the last arguments provided to the throttled function.
- * @param fn - function to decorate
+ * @param fn
  * @param threshold - indicates how often function could be called
+ * @returns {Function}
  */
 export function throttle<F extends AnyToAnyFnSignature>(fn: F, threshold = 250): Throttled<F> {
   let last: number;
@@ -28,7 +29,7 @@ export function throttle<F extends AnyToAnyFnSignature>(fn: F, threshold = 250):
       return Promise.resolve(fn.apply(this, args));
     }
 
-    deferred = deferred || createDeferred();
+    deferred = deferred || PromiseUtils.deferred();
     (typeof timeout === 'number') && clearTimeout(timeout);
     timeout = window.setTimeout(() => {
       last = now;
diff --git a/src/modules/esl-utils/decorators/prop.ts b/src/modules/esl-utils/decorators/prop.ts
index 229a03628..8242e7c1f 100644
--- a/src/modules/esl-utils/decorators/prop.ts
+++ b/src/modules/esl-utils/decorators/prop.ts
@@ -21,7 +21,7 @@ export type OverrideDecoratorConfig = {
  * 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.
  *
- * @param prototypeConfig - prototype property configuration
+ *  @param [prototypeConfig] - prototype property configuration
  */
 export function prop(prototypeConfig: OverrideDecoratorConfig = {}) {
   return function (obj: any, name: string): void {
diff --git a/src/modules/esl-utils/dom/element.ts b/src/modules/esl-utils/dom/element.ts
index 639aecf58..42aa3b3d3 100644
--- a/src/modules/esl-utils/dom/element.ts
+++ b/src/modules/esl-utils/dom/element.ts
@@ -25,7 +25,7 @@ export function getParentNode(element: Element | ShadowRoot): Node {
     return element;
   }
 
-  return (ShadowRoot
+  return (window.ShadowRoot
     ? element instanceof ShadowRoot
       ? element.host
       : element.assignedSlot || element.parentNode
diff --git a/src/modules/esl-utils/dom/events.ts b/src/modules/esl-utils/dom/events.ts
index 7884963cd..759c625f2 100644
--- a/src/modules/esl-utils/dom/events.ts
+++ b/src/modules/esl-utils/dom/events.ts
@@ -6,10 +6,10 @@ export interface Point {
 export abstract class EventUtils {
   /**
    * Dispatch custom event.
-   * Event bubbles and is cancelable by default, use `eventInit` to override that.
+   * Event bubbles and is cancelable by default, use {@param eventInit} to override that.
    * @param el - element target
    * @param eventName - event name
-   * @param eventInit - custom event init. See {@link CustomEventInit}
+   * @param [eventInit] - custom event init. See {@link CustomEventInit}
    */
   public static dispatch(el: HTMLElement, eventName: string, eventInit?: CustomEventInit) {
     const init = Object.assign({
diff --git a/src/modules/esl-utils/dom/rect.ts b/src/modules/esl-utils/dom/rect.ts
index baf6e3744..5c92ab611 100644
--- a/src/modules/esl-utils/dom/rect.ts
+++ b/src/modules/esl-utils/dom/rect.ts
@@ -15,8 +15,8 @@ export class Rect {
    * The static method creates a new Rect instance from a rect-like object.
    * @param rect - rect-like object
    * */
-  public static from(rect: {x?: number; y?: number; width?: number; height?: number} = {}): Rect {
-    return new this(rect.x, rect.y, rect.width, rect.height);
+  public static from(rect: {x?: number; left?: number; y?: number; top?: number; width?: number; height?: number} = {}): Rect {
+    return new this(rect.x || rect.left, rect.y || rect.top, rect.width, rect.height);
   }
 
   public constructor(x?: number, y?: number, width?: number, height?: number) {
diff --git a/src/modules/esl-utils/dom/rtl.ts b/src/modules/esl-utils/dom/rtl.ts
index 56a33e67a..a38912e02 100644
--- a/src/modules/esl-utils/dom/rtl.ts
+++ b/src/modules/esl-utils/dom/rtl.ts
@@ -11,7 +11,8 @@ export abstract class RTLUtils {
   }
 
   /**
-   * @returns RTL scroll type (lazy, memoized)
+   * @returns {ScrollType} RTL scroll type
+   * Lazy, memoized.
    */
   @memoize()
   static get scrollType(): ScrollType {
diff --git a/src/modules/esl-utils/dom/script.ts b/src/modules/esl-utils/dom/script.ts
index b1c76e3fb..2106405f1 100644
--- a/src/modules/esl-utils/dom/script.ts
+++ b/src/modules/esl-utils/dom/script.ts
@@ -11,8 +11,8 @@ const createAsyncScript = (id: string, src: string) => {
 
 /**
  * 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 {string} id - unique script id that is used as a marker to prevent future load
+ * @param {string} src - script src (url) to load
  */
 export function loadScript(id: string, src: string): Promise<Event> {
   return new Promise((resolve, reject) => {
diff --git a/src/modules/esl-utils/dom/scroll.ts b/src/modules/esl-utils/dom/scroll.ts
index 90e206860..c27c7f427 100644
--- a/src/modules/esl-utils/dom/scroll.ts
+++ b/src/modules/esl-utils/dom/scroll.ts
@@ -6,14 +6,16 @@ const $html = document.documentElement;
 const initiatorSet = new Set();
 
 export abstract class ScrollUtils {
-  /** Check vertical scroll based on content height */
+  /**
+   * Check vertical scroll based on content height
+   * */
   static hasVerticalScroll(target = $html) {
     return target.scrollHeight > target.clientHeight;
   }
 
   /**
    * Disable scroll on the page.
-   * @param strategy - to make scroll visually disabled
+   * @param [strategy] - to make scroll visually disabled
    * */
   public static lock(strategy?: ScrollStrategy) {
     const hasScroll = ScrollUtils.hasVerticalScroll();
@@ -33,7 +35,7 @@ export abstract class ScrollUtils {
   /**
    * Disable scroll on the page.
    * @param initiator - object to associate request with
-   * @param strategy - to make scroll visually disabled
+   * @param [strategy] - to make scroll visually disabled
    *
    * TODO: currently requests with different strategy is not taken into account
    * */
@@ -45,8 +47,8 @@ export abstract class ScrollUtils {
   /**
    * Enable scroll on the page in case it was requested with given initiator.
    * @param initiator - object to associate request with
-   * @param strategy - to make scroll visually disabled
-   */
+   * @param [strategy] - to make scroll visually disabled
+   * */
   public static requestUnlock(initiator: any, strategy?: ScrollStrategy) {
     initiator && initiatorSet.delete(initiator);
     (initiatorSet.size === 0) && ScrollUtils.unlock();
@@ -56,8 +58,8 @@ export abstract class ScrollUtils {
 /**
  * Get the list of all scroll parents, up the list of ancestors until we get to the top window object.
  * @param element - element for which you want to get the list of all scroll parents
- * @param list - array of elements to concatenate with the list of all scroll parents of element (optional)
- */
+ * Optional @param [list] - array of elements to concatenate with the list of all scroll parents of element
+ * */
 export function getListScrollParents(element: Node, list: Element[] = []): Element[] {
   const scrollParent = getScrollParent(element);
   const isBody = scrollParent === element.ownerDocument?.body;
diff --git a/src/modules/esl-utils/dom/traversing.ts b/src/modules/esl-utils/dom/traversing.ts
index ab1014d1a..fa280325f 100644
--- a/src/modules/esl-utils/dom/traversing.ts
+++ b/src/modules/esl-utils/dom/traversing.ts
@@ -1,14 +1,14 @@
 export abstract class TraversingUtils {
   /**
-   * Check that `nodeA` and `nodeB` is from the same tree path.
+   * Check that {@param nodeA} and {@param nodeB} is from the same tree path.
    */
   static isRelative(nodeA: Node | null, nodeB: Node | null) {
     return nodeA && nodeB && (nodeA.contains(nodeB) || nodeB.contains(nodeA));
   }
 
   /**
-   * Find closest parent node of `node` by `predicate`.
-   * Optional `skipSelf` to skip initial node
+   * Find closest parent node of {@param node} by {@param predicate}.
+   * Optional {@param skipSelf} to skip initial node
    */
   static closestBy(node: Node | null, predicate: (node: Node) => boolean, skipSelf = false): Node | null {
     let current = skipSelf && node ? node.parentNode : node;
@@ -20,7 +20,7 @@ export abstract class TraversingUtils {
   }
 
   /**
-   * Create function that find next dom element, that matches selector, in the sequence declared by `next` function
+   * Create function that find next dom element, that matches selector, in the sequence declared by {@param next} function
    */
   static createSequenceFinder(next: (el: Element) => Element | null) {
     return function (base: Element, sel: string): Element | null {
@@ -31,18 +31,18 @@ export abstract class TraversingUtils {
     };
   }
 
-  /** @returns first matching next sibling or null*/
+  /** @return first matching next sibling or null*/
   static findNext = TraversingUtils.createSequenceFinder((el) => el.nextElementSibling);
-  /** @returns first matching previous sibling or null*/
+  /** @return first matching previous sibling or null*/
   static findPrev = TraversingUtils.createSequenceFinder((el) => el.previousElementSibling);
-  /** @returns first matching parent or null*/
+  /** @return first matching parent or null*/
   static findParent = TraversingUtils.createSequenceFinder((el) => el.parentElement);
 
-  /** @returns Array of all matching elements in subtree or empty array*/
+  /** @return Array of all matching elements in subtree or empty array*/
   static findAll(base: Element, sel: string): Element[] {
     return sel ? Array.from(base.querySelectorAll(sel)) : [base];
   }
-  /** @returns Array of all matching children or empty array*/
+  /** @return Array of all matching children or empty array*/
   static findChildren(base: Element, sel: string): Element[] {
     return Array.from(base.children).filter((el: Element) => !sel || el.matches(sel));
   }
diff --git a/src/modules/esl-utils/environment/export-ns.ts b/src/modules/esl-utils/environment/export-ns.ts
index 35c4ab28d..cb2f671e7 100644
--- a/src/modules/esl-utils/environment/export-ns.ts
+++ b/src/modules/esl-utils/environment/export-ns.ts
@@ -27,12 +27,9 @@ export const exportNs = (name: string, module: any) => {
 
 /**
  * Decorator to declare function or class in a global ns
- * @param name - key path to declare in ESL global ns
+ * @param {string} name - key path to declare in ESL global ns
  * NOTE: path parts should be separated by dots
- * @example
- * ```ts
- * @Export('Package.Component')
- * ```
+ * @example @Export('Package.Component')
  * NOTE: in case declaration contains components-packages, their origins will be mixed with declaration in a Runtime
  */
 // eslint-disable-next-line @typescript-eslint/ban-types
diff --git a/src/modules/esl-utils/misc/format.ts b/src/modules/esl-utils/misc/format.ts
index 433aab4c4..f4ebbae6b 100644
--- a/src/modules/esl-utils/misc/format.ts
+++ b/src/modules/esl-utils/misc/format.ts
@@ -28,10 +28,9 @@ export const parseNumber = (str: string | number, nanValue?: number | undefined)
 /**
  * Common function that returns coefficient aspect ratio
  * Supported formats: w:h, w/h, coefficient
- * @example
- * `16:9`, `16/9`, `1.77`
+ * @example '16:9', '16/9', '1.77'
  * @param str - string to parse
- * @returns aspect ratio coefficient
+ * @return aspect ratio coefficient
  */
 export function parseAspectRatio(str: string): number {
   const [w, h] = str.split(/[:/]/);
@@ -49,7 +48,7 @@ export function evaluate(str: string, defaultValue?: any): any {
   }
 }
 
-/** Replace `{key}` patterns in the string from the source object */
+/** Replace '{key}' patterns in the string from the source object */
 export function format(str: string, source: Record<string, any>) {
   return str.replace(/{([\w.]+)}/g, (match, key) => {
     const val = get(source, key);
diff --git a/src/modules/esl-utils/misc/functions.ts b/src/modules/esl-utils/misc/functions.ts
index c1bd46939..f5911ae4c 100644
--- a/src/modules/esl-utils/misc/functions.ts
+++ b/src/modules/esl-utils/misc/functions.ts
@@ -1,7 +1,11 @@
-/** Function that does nothing */
+/**
+ * Function that does nothing
+ */
 export const noop: AnyToVoidFnSignature = () => undefined;
 
-/** Function that returns the first argument */
+/**
+ * Function that returns the first argument
+ */
 export const identity = <T>(arg: T): T => arg;
 
 export type NoopFnSignature = () => void;
diff --git a/src/modules/esl-utils/misc/object.ts b/src/modules/esl-utils/misc/object.ts
index 5f07293b3..b95af567a 100644
--- a/src/modules/esl-utils/misc/object.ts
+++ b/src/modules/esl-utils/misc/object.ts
@@ -93,7 +93,7 @@ export const get = (data: any, path: string, defaultValue?: any): any => {
 /**
  * Performs a deep merge of objects and returns new object.
  * Does not modify objects (immutable)
- * @param objects - objects to merge
+ * @param objects to merge
  * @returns new object with merged key/values
  */
 export function deepMerge(...objects: any[]): any {
diff --git a/src/polyfills/list/dom.element.closest.ts b/src/polyfills/list/dom.element.closest.ts
index 66b1328ed..ff94c9566 100644
--- a/src/polyfills/list/dom.element.closest.ts
+++ b/src/polyfills/list/dom.element.closest.ts
@@ -1,6 +1,6 @@
 /**
  * Group: DOM API shims ~ ES6 shim group
- * Target Browsers: `IE11`, `Edge < 14`
+ * Target Browsers: IE11, Edge < 14
  * Element.closest polyfill
  */
 (function (e: ElementEx) {
diff --git a/src/polyfills/list/dom.element.toggleAttribute.ts b/src/polyfills/list/dom.element.toggleAttribute.ts
index fc794a33b..b52146a44 100644
--- a/src/polyfills/list/dom.element.toggleAttribute.ts
+++ b/src/polyfills/list/dom.element.toggleAttribute.ts
@@ -1,6 +1,6 @@
 /**
  * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`, `Safari < 13`
+ * Target Browsers: IE11, Edge < 18, Safari < 13
  * Element.toggleAttribute polyfill
  */
 if (!Element.prototype.toggleAttribute) {
diff --git a/src/polyfills/list/dom.keyboard.key.ts b/src/polyfills/list/dom.keyboard.key.ts
index 11a0b8669..274755706 100644
--- a/src/polyfills/list/dom.keyboard.key.ts
+++ b/src/polyfills/list/dom.keyboard.key.ts
@@ -1,6 +1,6 @@
 /**
  * Group: DOM API shims
- * Target Browsers: `IE11`, `Edge < 18`
+ * Target Browsers: IE11, Edge < 18
  * KeyboardEvent.prototype.key shim to normalize key values to W3C spec
  * Based on published shim https://www.npmjs.com/package/shim-keyboard-event-key
  */
diff --git a/src/polyfills/list/dom.node.isConnected.ts b/src/polyfills/list/dom.node.isConnected.ts
index 3044301e6..02832b0b1 100644
--- a/src/polyfills/list/dom.node.isConnected.ts
+++ b/src/polyfills/list/dom.node.isConnected.ts
@@ -1,6 +1,6 @@
 /**
  * Group: DOM polyfills
- * Target browsers: `IE11`, `Edge <= 18`
+ * Target browsers: IE11, Edge <= 18
  * Node.isConnected polyfill
  */
 if (!('isConnected' in Node.prototype)) {
diff --git a/src/polyfills/list/dom.tokenList.toggle.ts b/src/polyfills/list/dom.tokenList.toggle.ts
index 772fc780e..c7734a578 100644
--- a/src/polyfills/list/dom.tokenList.toggle.ts
+++ b/src/polyfills/list/dom.tokenList.toggle.ts
@@ -1,6 +1,6 @@
 /**
  * Group: ES6 shims
- * Target Browsers: `IE11`
+ * Target Browsers: IE11
  * DOMTokenList.toggle polyfill to support force option
  */
 (function (DOMTokenListProto) {
diff --git a/src/polyfills/list/es6.array.find.ts b/src/polyfills/list/es6.array.find.ts
index 62a6d79fc..12155de9a 100644
--- a/src/polyfills/list/es6.array.find.ts
+++ b/src/polyfills/list/es6.array.find.ts
@@ -2,7 +2,7 @@ type FindPredicate<T> = (T: any, index: number, array: T[]) => boolean;
 
 /**
  * Group: ES6 shim
- * Target Browsers: `IE11`, `Edge < 14`
+ * Target Browsers: IE11, Edge < 14
  * Array.prototype.findIndex && Array.prototype.findIndex
  */
 if (typeof Array.prototype.findIndex !== 'function') {
diff --git a/src/polyfills/list/es6.array.includes.ts b/src/polyfills/list/es6.array.includes.ts
index 6f5c0a233..430d6eedc 100644
--- a/src/polyfills/list/es6.array.includes.ts
+++ b/src/polyfills/list/es6.array.includes.ts
@@ -1,6 +1,6 @@
 /**
  * Group: ES6 shim
- * Target Browsers: `IE11`, `Edge < 14`
+ * Target Browsers: IE11, Edge < 14
  * Array.prototype.includes polyfill
  */
 if (!Array.prototype.includes) {
diff --git a/src/polyfills/list/es6.object.is.ts b/src/polyfills/list/es6.object.is.ts
index e34fba24c..641ff925c 100644
--- a/src/polyfills/list/es6.object.is.ts
+++ b/src/polyfills/list/es6.object.is.ts
@@ -1,6 +1,6 @@
 /**
  * Group: ES6 shims
- * Target Browsers: `IE11`
+ * Target Browsers: IE11
  * Object.is support
  * Blatantly stolen from ES6 specs. Kept here for reference.
  * http://wiki.ecmascript.org/doku.php?id=harmony:egal
diff --git a/tsdoc.json b/tsdoc.json
deleted file mode 100644
index 17099b3c9..000000000
--- a/tsdoc.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
-  "tagDefinitions": [
-    {
-      "tagName": "@author",
-      "syntaxKind": "block"
-    },
-    {
-      "tagName": "@implements",
-      "syntaxKind": "modifier"
-    }
-  ]
-}


Test patch:
diff --git a/src/modules/esl-utils/async/test/debounce.test.ts b/src/modules/esl-utils/async/test/debounce.test.ts
index 6e03a3124..def5e68e4 100644
--- a/src/modules/esl-utils/async/test/debounce.test.ts
+++ b/src/modules/esl-utils/async/test/debounce.test.ts
@@ -1,30 +1,29 @@
 import {debounce} from '../debounce';
 
 describe('async/debounce', () => {
-  beforeAll(() =>  jest.useFakeTimers());
-  afterAll(() => jest.useRealTimers());
-
-  test('basic scenario', () => {
+  test('basic scenario', (done) => {
     const fn = jest.fn();
-    const debounced = debounce(fn, 50);
+    const debounced = debounce(fn, 20);
+
+    expect(typeof debounced).toBe('function');
 
     expect(debounced()).toBeInstanceOf(Promise);
     expect(debounced()).toBeInstanceOf(Promise);
-    jest.advanceTimersByTime(25);
-    expect(debounced()).toBeInstanceOf(Promise);
+    setTimeout(() => debounced());
     expect(fn).toBeCalledTimes(0);
-    jest.advanceTimersByTime(50);
-    expect(fn).toBeCalledTimes(1);
-  });
+    setTimeout(() => {
+      expect(fn).toBeCalledTimes(1);
+      done();
+    }, 100);
+  }, 150);
 
   test('call context', () => {
     const fn = function () { return this; };
     const debounced = debounce(fn, 0);
+
     const context = {};
-    const promise$ = debounced.call(context);
-    jest.runAllTimers();
-    return expect(promise$).resolves.toBe(context);
-  });
+    return debounced.call(context).then((val: any) => expect(val).toBe(context));
+  }, 50);
 
   test('cancel debounce', () => {
     const fn = jest.fn();
@@ -38,19 +37,18 @@ describe('async/debounce', () => {
 
   test('deferred result', () => {
     const fn = jest.fn((n) => n + 1);
-    const debounced = debounce(fn as (n: number) => number, 20);
+    const debounced = debounce(fn as (n: number) => number);
 
     expect(debounced.promise).toBeInstanceOf(Promise);
     expect(debounced(1)).toBe(debounced(2));
     expect(debounced.promise).toBe(debounced(3));
     expect(debounced.promise).toBeInstanceOf(Promise);
+    setTimeout(() => debounced(4), 20);
     expect(fn).toBeCalledTimes(0);
-    jest.advanceTimersByTime(10);
-    expect(fn).toBeCalledTimes(0);
-    const promise$ = debounced(4);
-    jest.advanceTimersByTime(20);
-    expect(fn).toBeCalledTimes(1);
 
-    return expect(promise$).resolves.toBe(5);
-  });
+    return debounced.promise.then((n) => {
+      expect(n).toBe(4);
+      expect(fn).toBeCalledTimes(1);
+    });
+  }, 100);
 });
diff --git a/src/modules/esl-utils/async/test/promise.test.ts b/src/modules/esl-utils/async/test/promise.test.ts
index 2c8846a27..feab9c26d 100644
--- a/src/modules/esl-utils/async/test/promise.test.ts
+++ b/src/modules/esl-utils/async/test/promise.test.ts
@@ -1,122 +1,83 @@
-import {createDeferred, PromiseUtils, promisifyEvent, promisifyMarker, promisifyTimeout, rejectPromise, resolvePromise, tryUntil} from '../promise';
+import {PromiseUtils} from '../promise';
 
-describe('promise utils', () => {
-  beforeAll(() =>  jest.useFakeTimers());
-  afterAll(() => jest.useRealTimers());
-
-  describe('promise chain', () => {
-    test('resolve', () => {
-      const payload = Symbol();
-      return expect(Promise.reject(payload).catch(resolvePromise)).resolves.toBe(payload);
-    });
-    test('reject', () => {
-      const payload = Symbol();
-      return expect(Promise.resolve(payload).then(rejectPromise)).rejects.toBe(payload);
-    });
+describe('PromiseUtils', () => {
+  describe('common', () => {
+    test('resolve', () => PromiseUtils.resolve(true).then((v) => expect(v).toBe(true)));
+    test('reject', () => PromiseUtils.reject(true).catch((v) => expect(v).toBe(true)));
   });
 
   describe('deferred', () => {
     test('resolve', () => {
-      const def$$ = createDeferred();
+      const def$$ = PromiseUtils.deferred();
       def$$.resolve(1);
       return def$$.promise.then((n) => expect(n).toBe(1));
     });
     test('reject', () => {
-      const def$$ = createDeferred();
+      const def$$ = PromiseUtils.deferred();
       def$$.reject(1);
       return def$$.promise.catch((n) => expect(n).toBe(1));
     });
   });
 
-  describe('promisifyTimeout', () => {
-    test('promisifyTimeout', () => {
-      const resCb = jest.fn();
-      const rejCb = jest.fn();
-
-      const promise$ = promisifyTimeout(100, true);
-      promise$.then(resCb, rejCb);
-
-      jest.advanceTimersByTime(50);
-      expect(resCb).not.toBeCalled();
-      expect(rejCb).not.toBeCalled();
-      jest.advanceTimersByTime(100);
-
-      return expect(promise$).resolves.toBe(true);
-    });
+  describe('fromTimeout', () => {
+    test('fromTimeout', () =>
+        PromiseUtils.fromTimeout(30, true)
+          .then((v) => expect(v).toBe(true))
+    );
   });
 
-  describe('promisifyEvent', () => {
+  describe('fromEvent', () => {
     test('simple', () => {
       const el = document.createElement('div');
-      const promise$ = promisifyEvent(el, 'test');
-      el.dispatchEvent(new CustomEvent('test'));
-      return expect(promise$.then((e) => e.type)).resolves.toBe('test');
+      setTimeout(() => el.dispatchEvent(new CustomEvent('test')), 20);
+      return PromiseUtils.fromEvent(el, 'test').then((e) => expect(e.type).toBe('test'));
     });
     test('timeout', () => {
       const el = document.createElement('div');
-      const promise$ = promisifyEvent(el, 'test', 10);
-      jest.advanceTimersByTime(100);
-      return expect(promise$).rejects.toBeInstanceOf(Error);
+      setTimeout(() => el.dispatchEvent(new CustomEvent('test')), 100);
+      return PromiseUtils.fromEvent(el, 'test', 10).catch(() => expect(true).toBe(true));
     });
   });
 
-  describe('promisifyMarker', () => {
+  describe('fromMarker', () => {
     test('attribute', () => {
       const el = document.createElement('div');
       (el as any).test = true;
-      const promise$ = promisifyMarker(el, 'test');
-      return expect(promise$).resolves.toBe(el);
+      return PromiseUtils.fromMarker(el, 'test').then((e) => expect(true).toBe(true));
     });
 
     test('event', () => {
       const el = document.createElement('div');
-      const promise$ = promisifyMarker(el, 'test');
-      jest.advanceTimersByTime(50);
-      el.dispatchEvent(new CustomEvent('test'));
-      return expect(promise$).resolves.toBe(el);
+      setTimeout(() => el.dispatchEvent(new CustomEvent('test')), 10);
+      return PromiseUtils.fromMarker(el, 'test').then((e) => expect(true).toBe(true));
     });
   });
 
   describe('tryUntil', () => {
-    test.each([
-      [(): any => undefined],
-      [(): any => {throw new Error()}]
-    ])('successful ( %s )', (testFn) => {
-      const observedFn = jest.fn(testFn);
-      const successFn = jest.fn();
-      const promise$ = tryUntil(observedFn, 3, 100);
-      promise$.then(successFn);
-
-      expect(observedFn).toBeCalled();
-      expect(successFn).not.toBeCalled();
-
-      jest.advanceTimersByTime(110);
-
-      expect(observedFn).toBeCalledTimes(2);
-      expect(successFn).not.toBeCalled();
+    test('successful', () => {
+      let i = 0;
 
-      observedFn.mockReturnValue(true);
-      jest.advanceTimersByTime(110);
+      setTimeout(() => i++, 30);
+      setTimeout(() => i++, 60);
 
-      return expect(promise$).resolves.toBe(true);
+      return PromiseUtils.tryUntil(() => i > 1, 10, 10)
+        .then(() => expect(true).toBe(true));
     });
 
-    test('failed (() => false)', () => {
-      const promise$ = tryUntil(() => false, 3, 100);
-      jest.advanceTimersByTime(500);
-      return expect(promise$).rejects.toBeTruthy();
+    test('error', () => {
+      let i = false;
+      function fn() {
+        if (i) return true;
+        i = true;
+        throw new Error('Error test');
+      }
+      return PromiseUtils.tryUntil(fn, 2, 10)
+        .then(() => expect(true).toBe(true));
     });
-  });
-
-  test('PromiseUtils wrapper', () => {
-    expect(PromiseUtils.fromEvent).toBe(promisifyEvent);
-    expect(PromiseUtils.fromTimeout).toBe(promisifyTimeout);
-    expect(PromiseUtils.fromMarker).toBe(promisifyMarker);
 
-    expect(PromiseUtils.deferred).toBe(createDeferred);
-    expect(PromiseUtils.tryUntil).toBe(tryUntil);
-
-    expect(PromiseUtils.resolve).toBe(resolvePromise);
-    expect(PromiseUtils.reject).toBe(rejectPromise);
+    test('failed', () => {
+      return PromiseUtils.tryUntil(() => false, 5, 10)
+        .catch(() => expect(true).toBe(true));
+    });
   });
 });
diff --git a/src/modules/esl-utils/async/test/throttle.test.ts b/src/modules/esl-utils/async/test/throttle.test.ts
index 6c2d43c2b..cb1e87c4c 100644
--- a/src/modules/esl-utils/async/test/throttle.test.ts
+++ b/src/modules/esl-utils/async/test/throttle.test.ts
@@ -1,24 +1,28 @@
 import {throttle} from '../throttle';
 
+// TODO: sometimes test timeouts is ont ok!
 describe('async/throttle', () => {
-  beforeAll(() =>  jest.useFakeTimers());
-  afterAll(() => jest.useRealTimers());
-
-  test('basic scenario', () => {
-    jest.useFakeTimers()
+  test('basic scenario', (done) => {
     const fn = jest.fn();
-    const throttled = throttle(fn, 100);
+    const throttled = throttle(fn, 80);
+
+    expect(typeof throttled).toBe('function');
 
     expect(throttled()).toBeInstanceOf(Promise);
     expect(fn).toBeCalledTimes(1);
-    jest.advanceTimersByTime(50)
     expect(throttled()).toBeInstanceOf(Promise);
     expect(fn).toBeCalledTimes(1);
-    jest.advanceTimersByTime(100)
-    expect(fn).toBeCalledTimes(2);
-    expect(throttled()).toBeInstanceOf(Promise);
-    expect(fn).toBeCalledTimes(3);
-  });
+    setTimeout(() => {
+      throttled();
+      expect(fn).toBeCalledTimes(1);
+    }, 20);
+    setTimeout(() => {
+      expect(fn).toBeCalledTimes(2);
+      throttled();
+      expect(fn).toBeCalledTimes(3);
+      done();
+    }, 250);
+  }, 350);
 
   test('test context', () => {
     const fn = function () { return this; };
@@ -33,18 +37,16 @@ describe('async/throttle', () => {
     const throttled = throttle(fn as (n: number) => number, 50);
 
     expect(throttled.promise).toBeInstanceOf(Promise);
-    expect(throttled(1)).toBeInstanceOf(Promise);
+
+    throttled(1);
     expect(throttled(2)).toBe(throttled.promise);
     expect(throttled.promise).toBeInstanceOf(Promise);
-    expect(throttled(4)).toBeInstanceOf(Promise);
-
+    throttled(4);
     expect(fn).toBeCalledTimes(1);
-    expect(fn).lastCalledWith(1);
-
-    const {promise} = throttled;
 
-    jest.advanceTimersByTime(100);
-    expect(fn).toBeCalledTimes(2);
-    return expect(promise).resolves.toBe(5);
-  });
+    return throttled.promise.then((n) => {
+      expect(n).toBe(5);
+      expect(fn).toBeCalledTimes(2);
+    });
+  }, 100);
 });