appium-uiautomator2-driver 7.6.0 → 7.6.2

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [7.6.2](https://github.com/appium/appium-uiautomator2-driver/compare/v7.6.1...v7.6.2) (2026-06-19)
2
+
3
+ ### Miscellaneous Chores
4
+
5
+ * **deps-dev:** bump @types/node from 25.9.4 to 26.0.0 ([#1011](https://github.com/appium/appium-uiautomator2-driver/issues/1011)) ([6783d39](https://github.com/appium/appium-uiautomator2-driver/commit/6783d39dafe7d79ab0b4d5ad43f11004f9e745bb))
6
+
7
+ ## [7.6.1](https://github.com/appium/appium-uiautomator2-driver/compare/v7.6.0...v7.6.1) (2026-06-09)
8
+
9
+ ### Miscellaneous Chores
10
+
11
+ * Refactor css locator transformer ([#1009](https://github.com/appium/appium-uiautomator2-driver/issues/1009)) ([cba226a](https://github.com/appium/appium-uiautomator2-driver/commit/cba226a075cac199e7df3183f34e7266e1b51e19))
12
+
1
13
  ## [7.6.0](https://github.com/appium/appium-uiautomator2-driver/compare/v7.5.2...v7.6.0) (2026-06-04)
2
14
 
3
15
  ### Features
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.doFindElementOrEls = doFindElementOrEls;
4
- const css_converter_1 = require("../css-converter");
4
+ const css_1 = require("../css");
5
5
  // we override the xpath search for this first-visible-child selector, which
6
6
  // looks like /*[@firstVisible="true"]
7
7
  const MAGIC_FIRST_VIS_CHILD_SEL = /\/\*\[@firstVisible ?= ?('|")true\1\]/;
@@ -18,8 +18,7 @@ async function doFindElementOrEls(params) {
18
18
  params.selector = MAGIC_SCROLLABLE_BY;
19
19
  }
20
20
  if (params.strategy === 'css selector') {
21
- params.strategy = '-android uiautomator';
22
- params.selector = new css_converter_1.CssConverter(params.selector, this.opts.appPackage).toUiAutomatorSelector();
21
+ ({ strategy: params.strategy, selector: params.selector } = await (0, css_1.cssToNativeLocator)(params.selector, this.opts.appPackage));
23
22
  }
24
23
  return (await uiautomator2.jwproxy.command(`/element${params.multiple ? 's' : ''}`, 'POST', params));
25
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"find.js","sourceRoot":"","sources":["../../../lib/commands/find.ts"],"names":[],"mappings":";;AA0BA,gDA6BC;AAvDD,oDAA8C;AAK9C,4EAA4E;AAC5E,sCAAsC;AACtC,MAAM,yBAAyB,GAAG,uCAAuC,CAAC;AAE1E,MAAM,oBAAoB,GAAG,uCAAuC,CAAC;AACrE,MAAM,mBAAmB,GAAG,mCAAmC,CAAC;AAgBzD,KAAK,UAAU,kBAAkB,CAEtC,MAAuB;IAEvB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;IACvC,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,yBAAyB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnF,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC;QACjC,OAAO,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,CACxC,mBAAmB,SAAS,gBAAgB,EAC5C,KAAK,EACL,EAAE,CACH,CAAkB,CAAC;IACtB,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9E,MAAM,CAAC,QAAQ,GAAG,sBAAsB,CAAC;QACzC,MAAM,CAAC,QAAQ,GAAG,mBAAmB,CAAC;IACxC,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,cAAc,EAAE,CAAC;QACvC,MAAM,CAAC,QAAQ,GAAG,sBAAsB,CAAC;QACzC,MAAM,CAAC,QAAQ,GAAG,IAAI,4BAAY,CAChC,MAAM,CAAC,QAAQ,EACf,IAAI,CAAC,IAAI,CAAC,UAAU,CACrB,CAAC,qBAAqB,EAAE,CAAC;IAC5B,CAAC;IACD,OAAO,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,CACxC,WAAW,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EACvC,MAAM,EACN,MAAM,CACP,CAAoC,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"find.js","sourceRoot":"","sources":["../../../lib/commands/find.ts"],"names":[],"mappings":";;AA0BA,gDA4BC;AAtDD,gCAA0C;AAK1C,4EAA4E;AAC5E,sCAAsC;AACtC,MAAM,yBAAyB,GAAG,uCAAuC,CAAC;AAE1E,MAAM,oBAAoB,GAAG,uCAAuC,CAAC;AACrE,MAAM,mBAAmB,GAAG,mCAAmC,CAAC;AAgBzD,KAAK,UAAU,kBAAkB,CAEtC,MAAuB;IAEvB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;IACvC,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,yBAAyB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnF,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC;QACjC,OAAO,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,CACxC,mBAAmB,SAAS,gBAAgB,EAC5C,KAAK,EACL,EAAE,CACH,CAAkB,CAAC;IACtB,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9E,MAAM,CAAC,QAAQ,GAAG,sBAAsB,CAAC;QACzC,MAAM,CAAC,QAAQ,GAAG,mBAAmB,CAAC;IACxC,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,cAAc,EAAE,CAAC;QACvC,CAAC,EAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAC,GAAG,MAAM,IAAA,wBAAkB,EAChF,MAAM,CAAC,QAAQ,EACf,IAAI,CAAC,IAAI,CAAC,UAAU,CACrB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,CACxC,WAAW,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EACvC,MAAM,EACN,MAAM,CACP,CAAoC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** Locator strategy name used for UiAutomator queries. */
2
+ export declare const UI_AUTOMATOR_STRATEGY = "-android uiautomator";
3
+ /** Registry key for the UiAutomator CSS emitter. */
4
+ export declare const UI_AUTOMATOR_EMITTER_KEY = "uiautomator";
5
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../lib/css/constants.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,yBAAyB,CAAC;AAE5D,oDAAoD;AACpD,eAAO,MAAM,wBAAwB,gBAAgB,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UI_AUTOMATOR_EMITTER_KEY = exports.UI_AUTOMATOR_STRATEGY = void 0;
4
+ /** Locator strategy name used for UiAutomator queries. */
5
+ exports.UI_AUTOMATOR_STRATEGY = '-android uiautomator';
6
+ /** Registry key for the UiAutomator CSS emitter. */
7
+ exports.UI_AUTOMATOR_EMITTER_KEY = 'uiautomator';
8
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../lib/css/constants.ts"],"names":[],"mappings":";;;AAAA,0DAA0D;AAC7C,QAAA,qBAAqB,GAAG,sBAAsB,CAAC;AAE5D,oDAAoD;AACvC,QAAA,wBAAwB,GAAG,aAAa,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { NativeLocator } from '@appium/css-locator-to-native';
2
+ export { UI_AUTOMATOR_STRATEGY } from './constants';
3
+ /**
4
+ * Converts a CSS selector string into a native locator for the resolved strategy.
5
+ *
6
+ * @param css - CSS selector to transform
7
+ * @param appPackage - Optional application package used to qualify resource ids
8
+ * @returns Native locator strategy name and selector string
9
+ */
10
+ export declare function cssToNativeLocator(css: string, appPackage?: string | null): Promise<NativeLocator>;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/css/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiB,aAAa,EAAc,MAAM,+BAA+B,CAAC;AAE9F,OAAO,EAAC,qBAAqB,EAAC,MAAM,aAAa,CAAC;AAiBlD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,aAAa,CAAC,CAOxB"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UI_AUTOMATOR_STRATEGY = void 0;
4
+ exports.cssToNativeLocator = cssToNativeLocator;
5
+ const driver_1 = require("appium/driver");
6
+ const utils_1 = require("../utils");
7
+ const constants_1 = require("./constants");
8
+ const schema_1 = require("./schema");
9
+ const ui_automator_emitter_1 = require("./ui-automator-emitter");
10
+ var constants_2 = require("./constants");
11
+ Object.defineProperty(exports, "UI_AUTOMATOR_STRATEGY", { enumerable: true, get: function () { return constants_2.UI_AUTOMATOR_STRATEGY; } });
12
+ const emitters = {
13
+ [constants_1.UI_AUTOMATOR_EMITTER_KEY]: new ui_automator_emitter_1.UiAutomatorEmitter(constants_1.UI_AUTOMATOR_STRATEGY),
14
+ };
15
+ const getTransformCss = (0, utils_1.memoize)(async function loadTransformCss() {
16
+ const mod = await import('@appium/css-locator-to-native');
17
+ return mod.createCssTransformer({
18
+ schema: schema_1.ATTRIBUTE_SCHEMA,
19
+ emitters,
20
+ resolveStrategy() {
21
+ return constants_1.UI_AUTOMATOR_EMITTER_KEY;
22
+ },
23
+ });
24
+ });
25
+ /**
26
+ * Converts a CSS selector string into a native locator for the resolved strategy.
27
+ *
28
+ * @param css - CSS selector to transform
29
+ * @param appPackage - Optional application package used to qualify resource ids
30
+ * @returns Native locator strategy name and selector string
31
+ */
32
+ async function cssToNativeLocator(css, appPackage) {
33
+ try {
34
+ const transformCss = await getTransformCss();
35
+ return transformCss(css, { appPackage });
36
+ }
37
+ catch (err) {
38
+ throw mapCssError(err, css);
39
+ }
40
+ }
41
+ function mapCssError(err, css) {
42
+ if (isPackageError(err, 'InvalidSelectorError')) {
43
+ return new driver_1.errors.InvalidSelectorError(`Invalid CSS selector '${css}'`, err);
44
+ }
45
+ if (isPackageError(err, 'UnsupportedSelectorError')) {
46
+ return new driver_1.errors.InvalidSelectorError(`Unsupported CSS selector '${css}'`, err);
47
+ }
48
+ if (err instanceof Error) {
49
+ return err;
50
+ }
51
+ return new Error(String(err));
52
+ }
53
+ function isPackageError(err, name) {
54
+ return err instanceof Error && err.name === name;
55
+ }
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/css/index.ts"],"names":[],"mappings":";;;AA+BA,gDAUC;AAzCD,0CAAqC;AACrC,oCAAiC;AACjC,2CAA4E;AAC5E,qCAA0C;AAC1C,iEAA0D;AAG1D,yCAAkD;AAA1C,kHAAA,qBAAqB,OAAA;AAE7B,MAAM,QAAQ,GAAG;IACf,CAAC,oCAAwB,CAAC,EAAE,IAAI,yCAAkB,CAAC,iCAAqB,CAAC;CAC1E,CAAC;AAEF,MAAM,eAAe,GAAG,IAAA,eAAO,EAAC,KAAK,UAAU,gBAAgB;IAC7D,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,+BAA+B,CAAC,CAAC;IAC1D,OAAO,GAAG,CAAC,oBAAoB,CAAC;QAC9B,MAAM,EAAE,yBAAgB;QACxB,QAAQ;QACR,eAAe;YACb,OAAO,oCAAwB,CAAC;QAClC,CAAC;KACF,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH;;;;;;GAMG;AACI,KAAK,UAAU,kBAAkB,CACtC,GAAW,EACX,UAA0B;IAE1B,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,eAAe,EAAE,CAAC;QAC7C,OAAO,YAAY,CAAC,GAAG,EAAE,EAAC,UAAU,EAAC,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAY,EAAE,GAAW;IAC5C,IAAI,cAAc,CAAC,GAAG,EAAE,sBAAsB,CAAC,EAAE,CAAC;QAChD,OAAO,IAAI,eAAM,CAAC,oBAAoB,CAAC,yBAAyB,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,cAAc,CAAC,GAAG,EAAE,0BAA0B,CAAC,EAAE,CAAC;QACpD,OAAO,IAAI,eAAM,CAAC,oBAAoB,CAAC,6BAA6B,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,cAAc,CAAC,GAAY,EAAE,IAAY;IAChD,OAAO,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;AACnD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { AttributeSchema } from '@appium/css-locator-to-native';
2
+ export declare const ATTRIBUTE_SCHEMA: AttributeSchema;
3
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../lib/css/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAEnE,eAAO,MAAM,gBAAgB,EAAE,eAuB9B,CAAC"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ATTRIBUTE_SCHEMA = void 0;
4
+ exports.ATTRIBUTE_SCHEMA = {
5
+ attributes: {
6
+ checkable: { type: 'boolean' },
7
+ checked: { type: 'boolean' },
8
+ clickable: { type: 'boolean' },
9
+ enabled: { type: 'boolean' },
10
+ focusable: { type: 'boolean' },
11
+ focused: { type: 'boolean' },
12
+ 'long-clickable': { type: 'boolean' },
13
+ scrollable: { type: 'boolean' },
14
+ selected: { type: 'boolean' },
15
+ index: { type: 'numeric', aliases: ['nth-child'] },
16
+ instance: { type: 'numeric' },
17
+ description: {
18
+ type: 'string',
19
+ aliases: ['content-description', 'content-desc', 'desc', 'accessibility-id'],
20
+ },
21
+ 'resource-id': { type: 'string', aliases: ['id'] },
22
+ text: { type: 'string' },
23
+ 'class-name': { type: 'string' },
24
+ 'package-name': { type: 'string' },
25
+ },
26
+ booleanFormat: 'true-false',
27
+ };
28
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../lib/css/schema.ts"],"names":[],"mappings":";;;AAEa,QAAA,gBAAgB,GAAoB;IAC/C,UAAU,EAAE;QACV,SAAS,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC5B,OAAO,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC1B,SAAS,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC5B,OAAO,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC1B,SAAS,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC5B,OAAO,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC1B,gBAAgB,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QACnC,UAAU,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC7B,QAAQ,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC3B,KAAK,EAAE,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,WAAW,CAAC,EAAC;QAChD,QAAQ,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;QAC3B,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC,qBAAqB,EAAE,cAAc,EAAE,MAAM,EAAE,kBAAkB,CAAC;SAC7E;QACD,aAAa,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAC;QAChD,IAAI,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC;QACtB,YAAY,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC;QAC9B,cAAc,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC;KACjC;IACD,aAAa,EAAE,YAAY;CAC5B,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { ParsedSelector, StrategyEmitter } from '@appium/css-locator-to-native';
2
+ export interface UiAutomatorEmitterContext {
3
+ appPackage?: string | null;
4
+ }
5
+ /** Converts parsed CSS selectors into UiAutomator selector strings. */
6
+ export declare class UiAutomatorEmitter implements StrategyEmitter<UiAutomatorEmitterContext> {
7
+ readonly strategy: string;
8
+ constructor(strategy: string);
9
+ emit(parsed: ParsedSelector, ctx?: UiAutomatorEmitterContext): string;
10
+ private emitRule;
11
+ private formatEntity;
12
+ private formatBoolean;
13
+ private formatStringAttr;
14
+ private formatIdLocator;
15
+ }
16
+ //# sourceMappingURL=ui-automator-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-automator-emitter.d.ts","sourceRoot":"","sources":["../../../lib/css/ui-automator-emitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,cAAc,EACd,eAAe,EAChB,MAAM,+BAA+B,CAAC;AAoBvC,MAAM,WAAW,yBAAyB;IACxC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,uEAAuE;AACvE,qBAAa,kBAAmB,YAAW,eAAe,CAAC,yBAAyB,CAAC;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM;IAI5B,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE,yBAAyB,GAAG,MAAM;IAIrE,OAAO,CAAC,QAAQ;IAsChB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAkCxB,OAAO,CAAC,eAAe;CAGxB"}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UiAutomatorEmitter = void 0;
4
+ const utils_1 = require("../utils");
5
+ const BOOLEAN_ATTRS = new Set([
6
+ 'checkable',
7
+ 'checked',
8
+ 'clickable',
9
+ 'enabled',
10
+ 'focusable',
11
+ 'focused',
12
+ 'long-clickable',
13
+ 'scrollable',
14
+ 'selected',
15
+ ]);
16
+ const NUMERIC_ATTRS = new Set(['index', 'instance']);
17
+ const STRING_ATTRS = new Set(['description', 'resource-id', 'text', 'class-name', 'package-name']);
18
+ const ID_LOCATOR_PATTERN = /^[a-zA-Z_][a-zA-Z0-9._]*:id\/[\S]+$/;
19
+ /** Converts parsed CSS selectors into UiAutomator selector strings. */
20
+ class UiAutomatorEmitter {
21
+ strategy;
22
+ constructor(strategy) {
23
+ this.strategy = strategy;
24
+ }
25
+ emit(parsed, ctx) {
26
+ return this.emitRule(parsed.rule, ctx);
27
+ }
28
+ emitRule(rule, ctx) {
29
+ const parts = ['new UiSelector()'];
30
+ const tagName = rule.tag;
31
+ const classNames = [...rule.classes];
32
+ if (tagName && tagName !== '*') {
33
+ if (classNames.length) {
34
+ parts.push(`.className("${[tagName, ...classNames].join('.')}")`);
35
+ }
36
+ else {
37
+ parts.push(`.classNameMatches("${tagName}")`);
38
+ }
39
+ }
40
+ else if (classNames.length) {
41
+ parts.push(`.classNameMatches("${classNames.join('\\.')}")`);
42
+ }
43
+ if (rule.id) {
44
+ parts.push(`.resourceId("${this.formatIdLocator(rule.id, ctx?.appPackage)}")`);
45
+ }
46
+ for (const attr of rule.attributes) {
47
+ parts.push(this.formatEntity(attr, ctx));
48
+ }
49
+ for (const pseudo of rule.pseudos) {
50
+ const formatted = this.formatEntity(pseudo, ctx);
51
+ if (formatted) {
52
+ parts.push(formatted);
53
+ }
54
+ }
55
+ if (rule.nested) {
56
+ parts.push(`.childSelector(${this.emitRule(rule.nested, ctx)})`);
57
+ }
58
+ return parts.join('');
59
+ }
60
+ formatEntity(attr, ctx) {
61
+ if (BOOLEAN_ATTRS.has(attr.name)) {
62
+ return this.formatBoolean(attr);
63
+ }
64
+ if (NUMERIC_ATTRS.has(attr.name)) {
65
+ return `.${attr.name}(${attr.value})`;
66
+ }
67
+ if (STRING_ATTRS.has(attr.name)) {
68
+ return this.formatStringAttr(attr, ctx);
69
+ }
70
+ return '';
71
+ }
72
+ formatBoolean(attr) {
73
+ const value = attr.value ?? 'true';
74
+ return `.${toSnakeCase(attr.name)}(${value})`;
75
+ }
76
+ formatStringAttr(attr, ctx) {
77
+ const methodName = toSnakeCase(attr.name);
78
+ let value = attr.value ?? '';
79
+ if (attr.name === 'resource-id') {
80
+ value = this.formatIdLocator(value, ctx?.appPackage);
81
+ }
82
+ if (value === '') {
83
+ return `.${methodName}Matches("")`;
84
+ }
85
+ switch (attr.operator) {
86
+ case '=':
87
+ return `.${methodName}("${value}")`;
88
+ case '*=':
89
+ if (['description', 'text'].includes(attr.name)) {
90
+ return `.${methodName}Contains("${value}")`;
91
+ }
92
+ return `.${methodName}Matches("${(0, utils_1.escapeRegExp)(value)}")`;
93
+ case '^=':
94
+ if (['description', 'text'].includes(attr.name)) {
95
+ return `.${methodName}StartsWith("${value}")`;
96
+ }
97
+ return `.${methodName}Matches("^${(0, utils_1.escapeRegExp)(value)}")`;
98
+ case '$=':
99
+ return `.${methodName}Matches("${(0, utils_1.escapeRegExp)(value)}$")`;
100
+ case '~=':
101
+ return `.${methodName}Matches("${getWordMatcherRegex(value)}")`;
102
+ default:
103
+ return `.${methodName}("${value}")`;
104
+ }
105
+ }
106
+ formatIdLocator(locator, appPackage) {
107
+ return ID_LOCATOR_PATTERN.test(locator) ? locator : `${appPackage || 'android'}:id/${locator}`;
108
+ }
109
+ }
110
+ exports.UiAutomatorEmitter = UiAutomatorEmitter;
111
+ function toSnakeCase(str) {
112
+ const tokens = str
113
+ .split('-')
114
+ .map((token) => token.charAt(0).toUpperCase() + token.slice(1).toLowerCase());
115
+ const out = tokens.join('');
116
+ return out.charAt(0).toLowerCase() + out.slice(1);
117
+ }
118
+ function getWordMatcherRegex(word) {
119
+ return `\\b(\\w*${(0, utils_1.escapeRegExp)(word)}\\w*)\\b`;
120
+ }
121
+ //# sourceMappingURL=ui-automator-emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-automator-emitter.js","sourceRoot":"","sources":["../../../lib/css/ui-automator-emitter.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import {CssConverter} from '../css-converter';
1
+ import {cssToNativeLocator} from '../css';
2
2
  import type {Element as AppiumElement} from '@appium/types';
3
3
  import type {FindElementOpts} from 'appium-android-driver';
4
4
  import type {AndroidUiautomator2Driver} from '../driver';
@@ -42,11 +42,10 @@ export async function doFindElementOrEls(
42
42
  params.selector = MAGIC_SCROLLABLE_BY;
43
43
  }
44
44
  if (params.strategy === 'css selector') {
45
- params.strategy = '-android uiautomator';
46
- params.selector = new CssConverter(
45
+ ({strategy: params.strategy, selector: params.selector} = await cssToNativeLocator(
47
46
  params.selector,
48
47
  this.opts.appPackage,
49
- ).toUiAutomatorSelector();
48
+ ));
50
49
  }
51
50
  return (await uiautomator2.jwproxy.command(
52
51
  `/element${params.multiple ? 's' : ''}`,
@@ -0,0 +1,5 @@
1
+ /** Locator strategy name used for UiAutomator queries. */
2
+ export const UI_AUTOMATOR_STRATEGY = '-android uiautomator';
3
+
4
+ /** Registry key for the UiAutomator CSS emitter. */
5
+ export const UI_AUTOMATOR_EMITTER_KEY = 'uiautomator';
@@ -0,0 +1,59 @@
1
+ import {errors} from 'appium/driver';
2
+ import {memoize} from '../utils';
3
+ import {UI_AUTOMATOR_EMITTER_KEY, UI_AUTOMATOR_STRATEGY} from './constants';
4
+ import {ATTRIBUTE_SCHEMA} from './schema';
5
+ import {UiAutomatorEmitter} from './ui-automator-emitter';
6
+ import type {CssTransformer, NativeLocator, StrategyKey} from '@appium/css-locator-to-native';
7
+
8
+ export {UI_AUTOMATOR_STRATEGY} from './constants';
9
+
10
+ const emitters = {
11
+ [UI_AUTOMATOR_EMITTER_KEY]: new UiAutomatorEmitter(UI_AUTOMATOR_STRATEGY),
12
+ };
13
+
14
+ const getTransformCss = memoize(async function loadTransformCss(): Promise<CssTransformer> {
15
+ const mod = await import('@appium/css-locator-to-native');
16
+ return mod.createCssTransformer({
17
+ schema: ATTRIBUTE_SCHEMA,
18
+ emitters,
19
+ resolveStrategy(): StrategyKey<typeof emitters> {
20
+ return UI_AUTOMATOR_EMITTER_KEY;
21
+ },
22
+ });
23
+ });
24
+
25
+ /**
26
+ * Converts a CSS selector string into a native locator for the resolved strategy.
27
+ *
28
+ * @param css - CSS selector to transform
29
+ * @param appPackage - Optional application package used to qualify resource ids
30
+ * @returns Native locator strategy name and selector string
31
+ */
32
+ export async function cssToNativeLocator(
33
+ css: string,
34
+ appPackage?: string | null,
35
+ ): Promise<NativeLocator> {
36
+ try {
37
+ const transformCss = await getTransformCss();
38
+ return transformCss(css, {appPackage});
39
+ } catch (err) {
40
+ throw mapCssError(err, css);
41
+ }
42
+ }
43
+
44
+ function mapCssError(err: unknown, css: string): Error {
45
+ if (isPackageError(err, 'InvalidSelectorError')) {
46
+ return new errors.InvalidSelectorError(`Invalid CSS selector '${css}'`, err);
47
+ }
48
+ if (isPackageError(err, 'UnsupportedSelectorError')) {
49
+ return new errors.InvalidSelectorError(`Unsupported CSS selector '${css}'`, err);
50
+ }
51
+ if (err instanceof Error) {
52
+ return err;
53
+ }
54
+ return new Error(String(err));
55
+ }
56
+
57
+ function isPackageError(err: unknown, name: string): err is Error {
58
+ return err instanceof Error && err.name === name;
59
+ }
@@ -0,0 +1,26 @@
1
+ import type {AttributeSchema} from '@appium/css-locator-to-native';
2
+
3
+ export const ATTRIBUTE_SCHEMA: AttributeSchema = {
4
+ attributes: {
5
+ checkable: {type: 'boolean'},
6
+ checked: {type: 'boolean'},
7
+ clickable: {type: 'boolean'},
8
+ enabled: {type: 'boolean'},
9
+ focusable: {type: 'boolean'},
10
+ focused: {type: 'boolean'},
11
+ 'long-clickable': {type: 'boolean'},
12
+ scrollable: {type: 'boolean'},
13
+ selected: {type: 'boolean'},
14
+ index: {type: 'numeric', aliases: ['nth-child']},
15
+ instance: {type: 'numeric'},
16
+ description: {
17
+ type: 'string',
18
+ aliases: ['content-description', 'content-desc', 'desc', 'accessibility-id'],
19
+ },
20
+ 'resource-id': {type: 'string', aliases: ['id']},
21
+ text: {type: 'string'},
22
+ 'class-name': {type: 'string'},
23
+ 'package-name': {type: 'string'},
24
+ },
25
+ booleanFormat: 'true-false',
26
+ };
@@ -0,0 +1,148 @@
1
+ import {escapeRegExp} from '../utils';
2
+ import type {
3
+ ParsedAttribute,
4
+ ParsedRule,
5
+ ParsedSelector,
6
+ StrategyEmitter,
7
+ } from '@appium/css-locator-to-native';
8
+
9
+ const BOOLEAN_ATTRS = new Set([
10
+ 'checkable',
11
+ 'checked',
12
+ 'clickable',
13
+ 'enabled',
14
+ 'focusable',
15
+ 'focused',
16
+ 'long-clickable',
17
+ 'scrollable',
18
+ 'selected',
19
+ ]);
20
+
21
+ const NUMERIC_ATTRS = new Set(['index', 'instance']);
22
+
23
+ const STRING_ATTRS = new Set(['description', 'resource-id', 'text', 'class-name', 'package-name']);
24
+
25
+ const ID_LOCATOR_PATTERN = /^[a-zA-Z_][a-zA-Z0-9._]*:id\/[\S]+$/;
26
+
27
+ export interface UiAutomatorEmitterContext {
28
+ appPackage?: string | null;
29
+ }
30
+
31
+ /** Converts parsed CSS selectors into UiAutomator selector strings. */
32
+ export class UiAutomatorEmitter implements StrategyEmitter<UiAutomatorEmitterContext> {
33
+ readonly strategy: string;
34
+
35
+ constructor(strategy: string) {
36
+ this.strategy = strategy;
37
+ }
38
+
39
+ emit(parsed: ParsedSelector, ctx?: UiAutomatorEmitterContext): string {
40
+ return this.emitRule(parsed.rule, ctx);
41
+ }
42
+
43
+ private emitRule(rule: ParsedRule, ctx?: UiAutomatorEmitterContext): string {
44
+ const parts: string[] = ['new UiSelector()'];
45
+
46
+ const tagName = rule.tag;
47
+ const classNames = [...rule.classes];
48
+
49
+ if (tagName && tagName !== '*') {
50
+ if (classNames.length) {
51
+ parts.push(`.className("${[tagName, ...classNames].join('.')}")`);
52
+ } else {
53
+ parts.push(`.classNameMatches("${tagName}")`);
54
+ }
55
+ } else if (classNames.length) {
56
+ parts.push(`.classNameMatches("${classNames.join('\\.')}")`);
57
+ }
58
+
59
+ if (rule.id) {
60
+ parts.push(`.resourceId("${this.formatIdLocator(rule.id, ctx?.appPackage)}")`);
61
+ }
62
+
63
+ for (const attr of rule.attributes) {
64
+ parts.push(this.formatEntity(attr, ctx));
65
+ }
66
+
67
+ for (const pseudo of rule.pseudos) {
68
+ const formatted = this.formatEntity(pseudo, ctx);
69
+ if (formatted) {
70
+ parts.push(formatted);
71
+ }
72
+ }
73
+
74
+ if (rule.nested) {
75
+ parts.push(`.childSelector(${this.emitRule(rule.nested, ctx)})`);
76
+ }
77
+
78
+ return parts.join('');
79
+ }
80
+
81
+ private formatEntity(attr: ParsedAttribute, ctx?: UiAutomatorEmitterContext): string {
82
+ if (BOOLEAN_ATTRS.has(attr.name)) {
83
+ return this.formatBoolean(attr);
84
+ }
85
+ if (NUMERIC_ATTRS.has(attr.name)) {
86
+ return `.${attr.name}(${attr.value})`;
87
+ }
88
+ if (STRING_ATTRS.has(attr.name)) {
89
+ return this.formatStringAttr(attr, ctx);
90
+ }
91
+ return '';
92
+ }
93
+
94
+ private formatBoolean(attr: ParsedAttribute): string {
95
+ const value = attr.value ?? 'true';
96
+ return `.${toSnakeCase(attr.name)}(${value})`;
97
+ }
98
+
99
+ private formatStringAttr(attr: ParsedAttribute, ctx?: UiAutomatorEmitterContext): string {
100
+ const methodName = toSnakeCase(attr.name);
101
+ let value = attr.value ?? '';
102
+
103
+ if (attr.name === 'resource-id') {
104
+ value = this.formatIdLocator(value, ctx?.appPackage);
105
+ }
106
+
107
+ if (value === '') {
108
+ return `.${methodName}Matches("")`;
109
+ }
110
+
111
+ switch (attr.operator) {
112
+ case '=':
113
+ return `.${methodName}("${value}")`;
114
+ case '*=':
115
+ if (['description', 'text'].includes(attr.name)) {
116
+ return `.${methodName}Contains("${value}")`;
117
+ }
118
+ return `.${methodName}Matches("${escapeRegExp(value)}")`;
119
+ case '^=':
120
+ if (['description', 'text'].includes(attr.name)) {
121
+ return `.${methodName}StartsWith("${value}")`;
122
+ }
123
+ return `.${methodName}Matches("^${escapeRegExp(value)}")`;
124
+ case '$=':
125
+ return `.${methodName}Matches("${escapeRegExp(value)}$")`;
126
+ case '~=':
127
+ return `.${methodName}Matches("${getWordMatcherRegex(value)}")`;
128
+ default:
129
+ return `.${methodName}("${value}")`;
130
+ }
131
+ }
132
+
133
+ private formatIdLocator(locator: string, appPackage?: string | null): string {
134
+ return ID_LOCATOR_PATTERN.test(locator) ? locator : `${appPackage || 'android'}:id/${locator}`;
135
+ }
136
+ }
137
+
138
+ function toSnakeCase(str: string): string {
139
+ const tokens = str
140
+ .split('-')
141
+ .map((token) => token.charAt(0).toUpperCase() + token.slice(1).toLowerCase());
142
+ const out = tokens.join('');
143
+ return out.charAt(0).toLowerCase() + out.slice(1);
144
+ }
145
+
146
+ function getWordMatcherRegex(word: string): string {
147
+ return `\\b(\\w*${escapeRegExp(word)}\\w*)\\b`;
148
+ }