appium-uiwatchers-plugin 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +7 -2
  2. package/lib/commands/clear.d.ts +12 -0
  3. package/lib/commands/clear.d.ts.map +1 -0
  4. package/lib/commands/clear.js +22 -0
  5. package/lib/commands/clear.js.map +1 -0
  6. package/lib/commands/list.d.ts +12 -0
  7. package/lib/commands/list.d.ts.map +1 -0
  8. package/lib/commands/list.js +19 -0
  9. package/lib/commands/list.js.map +1 -0
  10. package/lib/commands/register.d.ts +13 -0
  11. package/lib/commands/register.d.ts.map +1 -0
  12. package/lib/commands/register.js +34 -0
  13. package/lib/commands/register.js.map +1 -0
  14. package/lib/commands/toggle.d.ts +18 -0
  15. package/lib/commands/toggle.d.ts.map +1 -0
  16. package/lib/commands/toggle.js +34 -0
  17. package/lib/commands/toggle.js.map +1 -0
  18. package/lib/commands/unregister.d.ts +13 -0
  19. package/lib/commands/unregister.d.ts.map +1 -0
  20. package/lib/commands/unregister.js +32 -0
  21. package/lib/commands/unregister.js.map +1 -0
  22. package/lib/config.d.ts +21 -0
  23. package/lib/config.d.ts.map +1 -0
  24. package/lib/config.js +13 -0
  25. package/lib/config.js.map +1 -0
  26. package/lib/element-cache.d.ts +93 -0
  27. package/lib/element-cache.d.ts.map +1 -0
  28. package/lib/element-cache.js +207 -0
  29. package/lib/element-cache.js.map +1 -0
  30. package/lib/plugin.d.ts +73 -0
  31. package/lib/plugin.d.ts.map +1 -0
  32. package/lib/plugin.js +353 -0
  33. package/lib/plugin.js.map +1 -0
  34. package/lib/types.d.ts +163 -0
  35. package/lib/types.d.ts.map +1 -0
  36. package/lib/types.js +5 -0
  37. package/lib/types.js.map +1 -0
  38. package/lib/utils.d.ts +24 -0
  39. package/lib/utils.d.ts.map +1 -0
  40. package/lib/utils.js +33 -0
  41. package/lib/utils.js.map +1 -0
  42. package/lib/validators.d.ts +18 -0
  43. package/lib/validators.d.ts.map +1 -0
  44. package/lib/validators.js +112 -0
  45. package/lib/validators.js.map +1 -0
  46. package/lib/watcher-checker.d.ts +12 -0
  47. package/lib/watcher-checker.d.ts.map +1 -0
  48. package/lib/watcher-checker.js +88 -0
  49. package/lib/watcher-checker.js.map +1 -0
  50. package/lib/watcher-store.d.ts +91 -0
  51. package/lib/watcher-store.d.ts.map +1 -0
  52. package/lib/watcher-store.js +177 -0
  53. package/lib/watcher-store.js.map +1 -0
  54. package/package.json +4 -1
  55. package/.c8rc.json +0 -12
  56. package/.github/workflows/npm-publish.yml +0 -28
  57. package/.husky/pre-commit +0 -4
  58. package/.lintstagedrc.json +0 -4
  59. package/.mocharc.json +0 -10
  60. package/.prettierignore +0 -6
  61. package/.prettierrc +0 -11
  62. package/eslint.config.js +0 -65
  63. package/src/commands/clear.ts +0 -28
  64. package/src/commands/list.ts +0 -23
  65. package/src/commands/register.ts +0 -47
  66. package/src/commands/toggle.ts +0 -43
  67. package/src/commands/unregister.ts +0 -43
  68. package/src/config.ts +0 -30
  69. package/src/element-cache.ts +0 -262
  70. package/src/plugin.ts +0 -437
  71. package/src/types.ts +0 -207
  72. package/src/utils.ts +0 -47
  73. package/src/validators.ts +0 -131
  74. package/src/watcher-checker.ts +0 -113
  75. package/src/watcher-store.ts +0 -210
  76. package/test/e2e/config.e2e.spec.cjs +0 -420
  77. package/test/e2e/plugin.e2e.spec.cjs +0 -312
  78. package/test/unit/element-cache.spec.js +0 -269
  79. package/test/unit/plugin.spec.js +0 -52
  80. package/test/unit/utils.spec.js +0 -85
  81. package/test/unit/validators.spec.js +0 -246
  82. package/test/unit/watcher-checker.spec.js +0 -274
  83. package/test/unit/watcher-store.spec.js +0 -405
  84. package/tsconfig.json +0 -31
package/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  [![Appium 3.x](https://img.shields.io/badge/Appium-3.x-purple.svg)](https://appium.io/)
5
5
  [![Node.js 18+](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
6
6
  [![npm version](https://img.shields.io/npm/v/appium-uiwatchers-plugin.svg)](https://www.npmjs.com/package/appium-uiwatchers-plugin)
7
- [![Build Status](https://img.shields.io/github/actions/workflow/status/rajvinodh/appium-uiwatchers-plugin/ci.yml?branch=main)](https://github.com/rajvinodh/appium-uiwatchers-plugin/actions)
8
7
 
9
8
  ## Introduction
10
9
 
@@ -12,6 +11,12 @@ Mobile apps often display unexpected UI elements — cookie consent dialogs, per
12
11
 
13
12
  This plugin provides a centralized, declarative way to handle these interruptions automatically.
14
13
 
14
+ ## Motivation
15
+
16
+ Android's UiAutomator 1.0 had a [UiWatcher](https://developer.android.com/reference/androidx/test/uiautomator/UiWatcher) API that automatically handled unexpected dialogs during tests. When the framework couldn't find an element, it would run registered watchers to dismiss blockers like ANR dialogs or permission prompts.
17
+
18
+ This feature was deprecated and never made it to UiAutomator 2.0. This plugin brings the same concept to Appium — working across both Android and iOS.
19
+
15
20
  ## Features
16
21
 
17
22
  - **Zero Wait Overhead** — No waiting for UI elements that may never appear
@@ -25,7 +30,7 @@ This plugin provides a centralized, declarative way to handle these interruption
25
30
 
26
31
  ## Installation
27
32
 
28
- ### From npm (coming soon)
33
+ ### From npm
29
34
 
30
35
  ```bash
31
36
  appium plugin install appium-uiwatchers-plugin
@@ -0,0 +1,12 @@
1
+ /**
2
+ * clearAllUIWatchers command implementation
3
+ */
4
+ import type { WatcherStore } from '../watcher-store.js';
5
+ import type { ClearAllWatchersResult } from '../types.js';
6
+ /**
7
+ * Clear all UI watchers from the session
8
+ * @param store - Watcher store instance
9
+ * @returns Clear result with count of removed watchers
10
+ */
11
+ export declare function clearAllUIWatchers(store: WatcherStore): Promise<ClearAllWatchersResult>;
12
+ //# sourceMappingURL=clear.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear.d.ts","sourceRoot":"","sources":["../../src/commands/clear.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAI1D;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAY7F"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * clearAllUIWatchers command implementation
3
+ */
4
+ import { logger } from '@appium/support';
5
+ const log = logger.getLogger('AppiumUIWatchers');
6
+ /**
7
+ * Clear all UI watchers from the session
8
+ * @param store - Watcher store instance
9
+ * @returns Clear result with count of removed watchers
10
+ */
11
+ export async function clearAllUIWatchers(store) {
12
+ // Get count before clearing
13
+ const count = store.clear();
14
+ // Log successful clear
15
+ log.info(`[UIWatchers] All UI watchers cleared (removed ${count} watchers)`);
16
+ // Return success response
17
+ return {
18
+ success: true,
19
+ removedCount: count,
20
+ };
21
+ }
22
+ //# sourceMappingURL=clear.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear.js","sourceRoot":"","sources":["../../src/commands/clear.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAIzC,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;AAEjD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAmB;IAC1D,4BAA4B;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAE5B,uBAAuB;IACvB,GAAG,CAAC,IAAI,CAAC,iDAAiD,KAAK,YAAY,CAAC,CAAC;IAE7E,0BAA0B;IAC1B,OAAO;QACL,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,KAAK;KACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * listUIWatchers command implementation
3
+ */
4
+ import type { WatcherStore } from '../watcher-store.js';
5
+ import type { ListWatchersResult } from '../types.js';
6
+ /**
7
+ * List all active UI watchers with their state
8
+ * @param store - Watcher store instance
9
+ * @returns List of all active watchers
10
+ */
11
+ export declare function listUIWatchers(store: WatcherStore): Promise<ListWatchersResult>;
12
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAUrF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * listUIWatchers command implementation
3
+ */
4
+ /**
5
+ * List all active UI watchers with their state
6
+ * @param store - Watcher store instance
7
+ * @returns List of all active watchers
8
+ */
9
+ export async function listUIWatchers(store) {
10
+ // Get all active watchers (this automatically filters out expired ones)
11
+ const activeWatchers = store.getActiveWatchers();
12
+ // Return success response
13
+ return {
14
+ success: true,
15
+ watchers: activeWatchers,
16
+ totalCount: activeWatchers.length,
17
+ };
18
+ }
19
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAmB;IACtD,wEAAwE;IACxE,MAAM,cAAc,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAEjD,0BAA0B;IAC1B,OAAO;QACL,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,cAAc;QACxB,UAAU,EAAE,cAAc,CAAC,MAAM;KAClC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * registerUIWatcher command implementation
3
+ */
4
+ import type { WatcherStore } from '../watcher-store.js';
5
+ import type { UIWatcher, RegisterWatcherResult } from '../types.js';
6
+ /**
7
+ * Register a new UI watcher
8
+ * @param store - Watcher store instance
9
+ * @param params - Watcher registration parameters
10
+ * @returns Registration result
11
+ */
12
+ export declare function registerUIWatcher(store: WatcherStore, params: UIWatcher): Promise<RegisterWatcherResult>;
13
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/commands/register.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAKpE;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,qBAAqB,CAAC,CA0BhC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * registerUIWatcher command implementation
3
+ */
4
+ import { logger } from '@appium/support';
5
+ import { validateWatcherParams } from '../validators.js';
6
+ const log = logger.getLogger('AppiumUIWatchers');
7
+ /**
8
+ * Register a new UI watcher
9
+ * @param store - Watcher store instance
10
+ * @param params - Watcher registration parameters
11
+ * @returns Registration result
12
+ */
13
+ export async function registerUIWatcher(store, params) {
14
+ // Get configuration from store
15
+ const config = store.getConfig();
16
+ // Validate parameters with configured max duration
17
+ validateWatcherParams(params, config.maxDurationMs);
18
+ // Add watcher to store (this will throw if validation fails at store level)
19
+ const watcherState = store.add(params);
20
+ // Log successful registration
21
+ log.info(`[UIWatchers] UIWatcher '${params.name}' registered (priority=${watcherState.priority}, duration=${params.duration}ms)`);
22
+ // Return success response
23
+ return {
24
+ success: true,
25
+ watcher: {
26
+ name: watcherState.name,
27
+ priority: watcherState.priority,
28
+ registeredAt: watcherState.registeredAt,
29
+ expiresAt: watcherState.expiresAt,
30
+ status: watcherState.status,
31
+ },
32
+ };
33
+ }
34
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/commands/register.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAGzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAmB,EACnB,MAAiB;IAEjB,+BAA+B;IAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;IAEjC,mDAAmD;IACnD,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IAEpD,4EAA4E;IAC5E,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAEvC,8BAA8B;IAC9B,GAAG,CAAC,IAAI,CACN,2BAA2B,MAAM,CAAC,IAAI,0BAA0B,YAAY,CAAC,QAAQ,cAAc,MAAM,CAAC,QAAQ,KAAK,CACxH,CAAC;IAEF,0BAA0B;IAC1B,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO,EAAE;YACP,IAAI,EAAE,YAAY,CAAC,IAAI;YACvB,QAAQ,EAAE,YAAY,CAAC,QAAQ;YAC/B,YAAY,EAAE,YAAY,CAAC,YAAY;YACvC,SAAS,EAAE,YAAY,CAAC,SAAS;YACjC,MAAM,EAAE,YAAY,CAAC,MAAM;SAC5B;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * disableUIWatchers and enableUIWatchers command implementations
3
+ */
4
+ import type { WatcherStore } from '../watcher-store.js';
5
+ import type { ToggleWatchersResult } from '../types.js';
6
+ /**
7
+ * Disable all UI watcher checking
8
+ * @param store - Watcher store instance
9
+ * @returns Disable result
10
+ */
11
+ export declare function disableUIWatchers(store: WatcherStore): Promise<ToggleWatchersResult>;
12
+ /**
13
+ * Enable all UI watcher checking
14
+ * @param store - Watcher store instance
15
+ * @returns Enable result
16
+ */
17
+ export declare function enableUIWatchers(store: WatcherStore): Promise<ToggleWatchersResult>;
18
+ //# sourceMappingURL=toggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle.d.ts","sourceRoot":"","sources":["../../src/commands/toggle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAIxD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAU1F;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAUzF"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * disableUIWatchers and enableUIWatchers command implementations
3
+ */
4
+ import { logger } from '@appium/support';
5
+ const log = logger.getLogger('AppiumUIWatchers');
6
+ /**
7
+ * Disable all UI watcher checking
8
+ * @param store - Watcher store instance
9
+ * @returns Disable result
10
+ */
11
+ export async function disableUIWatchers(store) {
12
+ store.disable();
13
+ // Log successful disable
14
+ log.info('[UIWatchers] All UI watchers disabled');
15
+ return {
16
+ success: true,
17
+ message: 'All UI watchers disabled',
18
+ };
19
+ }
20
+ /**
21
+ * Enable all UI watcher checking
22
+ * @param store - Watcher store instance
23
+ * @returns Enable result
24
+ */
25
+ export async function enableUIWatchers(store) {
26
+ store.enable();
27
+ // Log successful enable
28
+ log.info('[UIWatchers] All UI watchers enabled');
29
+ return {
30
+ success: true,
31
+ message: 'All UI watchers enabled',
32
+ };
33
+ }
34
+ //# sourceMappingURL=toggle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle.js","sourceRoot":"","sources":["../../src/commands/toggle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAIzC,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;AAEjD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAmB;IACzD,KAAK,CAAC,OAAO,EAAE,CAAC;IAEhB,yBAAyB;IACzB,GAAG,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IAElD,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,0BAA0B;KACpC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAmB;IACxD,KAAK,CAAC,MAAM,EAAE,CAAC;IAEf,wBAAwB;IACxB,GAAG,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;IAEjD,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,yBAAyB;KACnC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * unregisterUIWatcher command implementation
3
+ */
4
+ import type { WatcherStore } from '../watcher-store.js';
5
+ import type { UnregisterWatcherResult } from '../types.js';
6
+ /**
7
+ * Unregister a specific UI watcher by name
8
+ * @param store - Watcher store instance
9
+ * @param name - Name of the watcher to remove
10
+ * @returns Unregistration result
11
+ */
12
+ export declare function unregisterUIWatcher(store: WatcherStore, name: string): Promise<UnregisterWatcherResult>;
13
+ //# sourceMappingURL=unregister.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unregister.d.ts","sourceRoot":"","sources":["../../src/commands/unregister.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAI3D;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,uBAAuB,CAAC,CAuBlC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * unregisterUIWatcher command implementation
3
+ */
4
+ import { logger } from '@appium/support';
5
+ const log = logger.getLogger('AppiumUIWatchers');
6
+ /**
7
+ * Unregister a specific UI watcher by name
8
+ * @param store - Watcher store instance
9
+ * @param name - Name of the watcher to remove
10
+ * @returns Unregistration result
11
+ */
12
+ export async function unregisterUIWatcher(store, name) {
13
+ // Validate name parameter
14
+ if (!name || typeof name !== 'string') {
15
+ throw new Error('UIWatcher name is required');
16
+ }
17
+ // Check if watcher exists
18
+ const watcher = store.get(name);
19
+ if (!watcher) {
20
+ throw new Error(`UIWatcher '${name}' not found`);
21
+ }
22
+ // Remove watcher from store
23
+ store.remove(name);
24
+ // Log successful removal
25
+ log.info(`[UIWatchers] UIWatcher '${name}' unregistered`);
26
+ // Return success response
27
+ return {
28
+ success: true,
29
+ removed: name,
30
+ };
31
+ }
32
+ //# sourceMappingURL=unregister.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unregister.js","sourceRoot":"","sources":["../../src/commands/unregister.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAIzC,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAmB,EACnB,IAAY;IAEZ,0BAA0B;IAC1B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,0BAA0B;IAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,aAAa,CAAC,CAAC;IACnD,CAAC;IAED,4BAA4B;IAC5B,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEnB,yBAAyB;IACzB,GAAG,CAAC,IAAI,CAAC,2BAA2B,IAAI,gBAAgB,CAAC,CAAC;IAE1D,0BAA0B;IAC1B,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Configuration module for UI Watchers Plugin
3
+ */
4
+ /**
5
+ * Plugin configuration interface
6
+ */
7
+ export interface PluginConfig {
8
+ /** Maximum number of watchers allowed per session (1-20) */
9
+ maxWatchers?: number;
10
+ /** Maximum duration per watcher in milliseconds (1000-600000) */
11
+ maxDurationMs?: number;
12
+ /** Maximum element references to cache for stale recovery (10-200) */
13
+ maxCacheEntries?: number;
14
+ /** TTL for cached element references in milliseconds (5000-300000) */
15
+ elementTtlMs?: number;
16
+ }
17
+ /**
18
+ * Default configuration values
19
+ */
20
+ export declare const DefaultPluginConfig: Required<PluginConfig>;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,YAAY,CAKtD,CAAC"}
package/lib/config.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Configuration module for UI Watchers Plugin
3
+ */
4
+ /**
5
+ * Default configuration values
6
+ */
7
+ export const DefaultPluginConfig = {
8
+ maxWatchers: 5,
9
+ maxDurationMs: 60000,
10
+ maxCacheEntries: 50,
11
+ elementTtlMs: 60000,
12
+ };
13
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmBH;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAA2B;IACzD,WAAW,EAAE,CAAC;IACd,aAAa,EAAE,KAAK;IACpB,eAAe,EAAE,EAAE;IACnB,YAAY,EAAE,KAAK;CACpB,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Element Reference Cache for StaleElement Recovery
3
+ *
4
+ * This module provides caching of element locators to enable automatic recovery
5
+ * from StaleElementReferenceException on element action commands.
6
+ */
7
+ import type { CachedRef } from './types.js';
8
+ import type { PluginConfig } from './config.js';
9
+ import { type ElementObject } from './utils.js';
10
+ /**
11
+ * Configuration for ElementReferenceCache
12
+ */
13
+ export interface CacheConfig {
14
+ maxCacheEntries: number;
15
+ elementTtlMs: number;
16
+ }
17
+ /**
18
+ * ElementReferenceCache manages cached element locators for stale element recovery.
19
+ *
20
+ * Features:
21
+ * - Caches element locators on successful findElement/findElements
22
+ * - Provides element ID mapping for recovered elements
23
+ * - Supports transitive mapping updates (abc→efg, efg→hij => abc→hij)
24
+ * - LRU eviction when max entries reached
25
+ * - TTL-based expiry for old entries
26
+ */
27
+ export declare class ElementReferenceCache {
28
+ /** Cache storage: elementId → CachedRef */
29
+ private cache;
30
+ /** Element ID mappings: oldId → newId */
31
+ private idMappings;
32
+ /** Reverse index for transitive updates: newId → Set<oldId> */
33
+ private reverseIndex;
34
+ /** LRU order tracking (most recent at end) */
35
+ private accessOrder;
36
+ /** Configuration */
37
+ private config;
38
+ constructor(config: Required<PluginConfig>);
39
+ /**
40
+ * Cache an element reference from findElement
41
+ */
42
+ cacheElement(elementId: string, using: string, value: string): void;
43
+ /**
44
+ * Cache element references from findElements
45
+ * Each element is cached with its index position
46
+ */
47
+ cacheElements(elements: ElementObject[], using: string, value: string): void;
48
+ /**
49
+ * Get cached reference for an element
50
+ * Returns undefined if not found or expired
51
+ */
52
+ getRef(elementId: string): CachedRef | undefined;
53
+ /**
54
+ * Create element ID mapping with transitive update
55
+ *
56
+ * When we map oldId → newId, we also update any existing mappings
57
+ * that point to oldId to instead point to newId.
58
+ *
59
+ * Example:
60
+ * Existing: abc → efg
61
+ * New mapping: efg → hij
62
+ * Result: abc → hij, efg → hij
63
+ */
64
+ setMapping(oldId: string, newId: string): void;
65
+ /**
66
+ * Get mapped element ID if exists
67
+ * Returns the new ID if a mapping exists, otherwise returns undefined
68
+ */
69
+ getMappedId(elementId: string): string | undefined;
70
+ /**
71
+ * Remove expired entries from cache
72
+ */
73
+ cleanup(): void;
74
+ /**
75
+ * Clear all cache entries and mappings
76
+ */
77
+ clear(): void;
78
+ /**
79
+ * Get current cache size
80
+ */
81
+ get size(): number;
82
+ /**
83
+ * Get current mappings count
84
+ */
85
+ get mappingsCount(): number;
86
+ private addToCache;
87
+ private removeFromCache;
88
+ private evictLRU;
89
+ private updateAccessOrder;
90
+ private isExpired;
91
+ private addToReverseIndex;
92
+ }
93
+ //# sourceMappingURL=element-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-cache.d.ts","sourceRoot":"","sources":["../src/element-cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAuC,MAAM,YAAY,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAIlE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,qBAAa,qBAAqB;IAChC,2CAA2C;IAC3C,OAAO,CAAC,KAAK,CAAyB;IAEtC,yCAAyC;IACzC,OAAO,CAAC,UAAU,CAAsB;IAExC,+DAA+D;IAC/D,OAAO,CAAC,YAAY,CAA2B;IAE/C,8CAA8C;IAC9C,OAAO,CAAC,WAAW,CAAW;IAE9B,oBAAoB;IACpB,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC;IAW1C;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAYnE;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAqB5E;;;OAGG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAgBhD;;;;;;;;;;OAUG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAqB9C;;;OAGG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIlD;;OAEG;IACH,OAAO,IAAI,IAAI;IAmBf;;OAEG;IACH,KAAK,IAAI,IAAI;IASb;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,CAE1B;IAMD,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,iBAAiB;CAM1B"}
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Element Reference Cache for StaleElement Recovery
3
+ *
4
+ * This module provides caching of element locators to enable automatic recovery
5
+ * from StaleElementReferenceException on element action commands.
6
+ */
7
+ import { logger } from '@appium/support';
8
+ import { extractElementId } from './utils.js';
9
+ const log = logger.getLogger('AppiumUIWatchers');
10
+ /**
11
+ * ElementReferenceCache manages cached element locators for stale element recovery.
12
+ *
13
+ * Features:
14
+ * - Caches element locators on successful findElement/findElements
15
+ * - Provides element ID mapping for recovered elements
16
+ * - Supports transitive mapping updates (abc→efg, efg→hij => abc→hij)
17
+ * - LRU eviction when max entries reached
18
+ * - TTL-based expiry for old entries
19
+ */
20
+ export class ElementReferenceCache {
21
+ constructor(config) {
22
+ this.cache = new Map();
23
+ this.idMappings = new Map();
24
+ this.reverseIndex = new Map();
25
+ this.accessOrder = [];
26
+ this.config = {
27
+ maxCacheEntries: config.maxCacheEntries,
28
+ elementTtlMs: config.elementTtlMs,
29
+ };
30
+ }
31
+ /**
32
+ * Cache an element reference from findElement
33
+ */
34
+ cacheElement(elementId, using, value) {
35
+ const ref = {
36
+ using,
37
+ value,
38
+ source: 'findElement',
39
+ createdAt: Date.now(),
40
+ };
41
+ this.addToCache(elementId, ref);
42
+ log.debug(`[UIWatchers] Cached element reference: ${elementId} (${using}=${value})`);
43
+ }
44
+ /**
45
+ * Cache element references from findElements
46
+ * Each element is cached with its index position
47
+ */
48
+ cacheElements(elements, using, value) {
49
+ for (let i = 0; i < elements.length; i++) {
50
+ const element = elements[i];
51
+ const elementId = extractElementId(element);
52
+ if (!elementId)
53
+ continue;
54
+ const ref = {
55
+ using,
56
+ value,
57
+ index: i,
58
+ source: 'findElements',
59
+ createdAt: Date.now(),
60
+ };
61
+ this.addToCache(elementId, ref);
62
+ }
63
+ log.debug(`[UIWatchers] Cached ${elements.length} element references from findElements (${using}=${value})`);
64
+ }
65
+ /**
66
+ * Get cached reference for an element
67
+ * Returns undefined if not found or expired
68
+ */
69
+ getRef(elementId) {
70
+ const ref = this.cache.get(elementId);
71
+ if (!ref)
72
+ return undefined;
73
+ // Check TTL
74
+ if (this.isExpired(ref)) {
75
+ this.removeFromCache(elementId);
76
+ return undefined;
77
+ }
78
+ // Update LRU order
79
+ this.updateAccessOrder(elementId);
80
+ return ref;
81
+ }
82
+ /**
83
+ * Create element ID mapping with transitive update
84
+ *
85
+ * When we map oldId → newId, we also update any existing mappings
86
+ * that point to oldId to instead point to newId.
87
+ *
88
+ * Example:
89
+ * Existing: abc → efg
90
+ * New mapping: efg → hij
91
+ * Result: abc → hij, efg → hij
92
+ */
93
+ setMapping(oldId, newId) {
94
+ // Step 1: Find all mappings where value == oldId (using reverse index)
95
+ const pointingToOld = this.reverseIndex.get(oldId);
96
+ if (pointingToOld) {
97
+ // Update all these mappings to point to newId
98
+ for (const sourceId of pointingToOld) {
99
+ this.idMappings.set(sourceId, newId);
100
+ // Update reverse index
101
+ this.addToReverseIndex(sourceId, newId);
102
+ }
103
+ // Remove old reverse index entry
104
+ this.reverseIndex.delete(oldId);
105
+ }
106
+ // Step 2: Add the new mapping
107
+ this.idMappings.set(oldId, newId);
108
+ this.addToReverseIndex(oldId, newId);
109
+ log.debug(`[UIWatchers] Created element ID mapping: ${oldId} → ${newId}`);
110
+ }
111
+ /**
112
+ * Get mapped element ID if exists
113
+ * Returns the new ID if a mapping exists, otherwise returns undefined
114
+ */
115
+ getMappedId(elementId) {
116
+ return this.idMappings.get(elementId);
117
+ }
118
+ /**
119
+ * Remove expired entries from cache
120
+ */
121
+ cleanup() {
122
+ const now = Date.now();
123
+ const toRemove = [];
124
+ for (const [elementId, ref] of this.cache) {
125
+ if (now - ref.createdAt >= this.config.elementTtlMs) {
126
+ toRemove.push(elementId);
127
+ }
128
+ }
129
+ for (const elementId of toRemove) {
130
+ this.removeFromCache(elementId);
131
+ }
132
+ if (toRemove.length > 0) {
133
+ log.debug(`[UIWatchers] Cleaned up ${toRemove.length} expired cache entries`);
134
+ }
135
+ }
136
+ /**
137
+ * Clear all cache entries and mappings
138
+ */
139
+ clear() {
140
+ const count = this.cache.size;
141
+ this.cache.clear();
142
+ this.idMappings.clear();
143
+ this.reverseIndex.clear();
144
+ this.accessOrder = [];
145
+ log.debug(`[UIWatchers] Cleared element cache (${count} entries)`);
146
+ }
147
+ /**
148
+ * Get current cache size
149
+ */
150
+ get size() {
151
+ return this.cache.size;
152
+ }
153
+ /**
154
+ * Get current mappings count
155
+ */
156
+ get mappingsCount() {
157
+ return this.idMappings.size;
158
+ }
159
+ // ============================================================================
160
+ // Private helper methods
161
+ // ============================================================================
162
+ addToCache(elementId, ref) {
163
+ // Evict if at capacity
164
+ while (this.cache.size >= this.config.maxCacheEntries) {
165
+ this.evictLRU();
166
+ }
167
+ this.cache.set(elementId, ref);
168
+ this.updateAccessOrder(elementId);
169
+ }
170
+ removeFromCache(elementId) {
171
+ this.cache.delete(elementId);
172
+ // Remove from access order
173
+ const index = this.accessOrder.indexOf(elementId);
174
+ if (index !== -1) {
175
+ this.accessOrder.splice(index, 1);
176
+ }
177
+ }
178
+ evictLRU() {
179
+ if (this.accessOrder.length === 0)
180
+ return;
181
+ // Remove least recently used (first in array)
182
+ const lruId = this.accessOrder.shift();
183
+ if (lruId) {
184
+ this.cache.delete(lruId);
185
+ log.debug(`[UIWatchers] Evicted LRU cache entry: ${lruId}`);
186
+ }
187
+ }
188
+ updateAccessOrder(elementId) {
189
+ // Remove from current position
190
+ const index = this.accessOrder.indexOf(elementId);
191
+ if (index !== -1) {
192
+ this.accessOrder.splice(index, 1);
193
+ }
194
+ // Add to end (most recently used)
195
+ this.accessOrder.push(elementId);
196
+ }
197
+ isExpired(ref) {
198
+ return Date.now() - ref.createdAt >= this.config.elementTtlMs;
199
+ }
200
+ addToReverseIndex(oldId, newId) {
201
+ if (!this.reverseIndex.has(newId)) {
202
+ this.reverseIndex.set(newId, new Set());
203
+ }
204
+ this.reverseIndex.get(newId).add(oldId);
205
+ }
206
+ }
207
+ //# sourceMappingURL=element-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-cache.js","sourceRoot":"","sources":["../src/element-cache.ts"],"names":[],"mappings":"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"}