@appium/universal-xml-plugin 2.0.3 → 2.1.0

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 (50) hide show
  1. package/build/lib/attr-map.d.ts +3 -57
  2. package/build/lib/attr-map.d.ts.map +1 -1
  3. package/build/lib/attr-map.js +2 -5
  4. package/build/lib/attr-map.js.map +1 -1
  5. package/build/lib/index.d.ts +4 -3
  6. package/build/lib/index.d.ts.map +1 -1
  7. package/build/lib/index.js +20 -5
  8. package/build/lib/index.js.map +1 -1
  9. package/build/lib/node-map.d.ts +3 -199
  10. package/build/lib/node-map.d.ts.map +1 -1
  11. package/build/lib/node-map.js +2 -9
  12. package/build/lib/node-map.js.map +1 -1
  13. package/build/lib/plugin.d.ts +7 -6
  14. package/build/lib/plugin.d.ts.map +1 -1
  15. package/build/lib/plugin.js +16 -18
  16. package/build/lib/plugin.js.map +1 -1
  17. package/build/lib/source.d.ts +41 -47
  18. package/build/lib/source.d.ts.map +1 -1
  19. package/build/lib/source.js +104 -47
  20. package/build/lib/source.js.map +1 -1
  21. package/build/lib/transformers.d.ts +3 -7
  22. package/build/lib/transformers.d.ts.map +1 -1
  23. package/build/lib/transformers.js +9 -10
  24. package/build/lib/transformers.js.map +1 -1
  25. package/build/lib/types.d.ts +29 -0
  26. package/build/lib/types.d.ts.map +1 -0
  27. package/build/lib/types.js +3 -0
  28. package/build/lib/types.js.map +1 -0
  29. package/build/lib/xpath.d.ts +16 -7
  30. package/build/lib/xpath.d.ts.map +1 -1
  31. package/build/lib/xpath.js +19 -16
  32. package/build/lib/xpath.js.map +1 -1
  33. package/lib/{attr-map.js → attr-map.ts} +5 -5
  34. package/lib/{index.js → index.ts} +22 -4
  35. package/lib/{node-map.js → node-map.ts} +5 -1
  36. package/lib/plugin.ts +119 -0
  37. package/lib/source.ts +265 -0
  38. package/lib/{transformers.js → transformers.ts} +9 -12
  39. package/lib/types.ts +33 -0
  40. package/lib/{xpath.js → xpath.ts} +24 -20
  41. package/package.json +10 -10
  42. package/tsconfig.json +3 -2
  43. package/build/lib/logger.d.ts +0 -3
  44. package/build/lib/logger.d.ts.map +0 -1
  45. package/build/lib/logger.js +0 -6
  46. package/build/lib/logger.js.map +0 -1
  47. package/index.js +0 -7
  48. package/lib/logger.js +0 -3
  49. package/lib/plugin.js +0 -81
  50. package/lib/source.js +0 -226
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const support_1 = require("appium/support");
4
- const log = support_1.logger.getLogger('UniversalXMLPlugin');
5
- exports.default = log;
6
- //# sourceMappingURL=logger.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../lib/logger.js"],"names":[],"mappings":";;AAAA,4CAAsC;AACtC,MAAM,GAAG,GAAG,gBAAM,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;AACnD,kBAAe,GAAG,CAAC"}
package/index.js DELETED
@@ -1,7 +0,0 @@
1
- const {main, UniversalXMLPlugin} = require('./build/lib');
2
-
3
- if (require.main === module) {
4
- (async () => await main())();
5
- }
6
-
7
- module.exports = {UniversalXMLPlugin};
package/lib/logger.js DELETED
@@ -1,3 +0,0 @@
1
- import {logger} from 'appium/support';
2
- const log = logger.getLogger('UniversalXMLPlugin');
3
- export default log;
package/lib/plugin.js DELETED
@@ -1,81 +0,0 @@
1
- import {BasePlugin} from 'appium/plugin';
2
- import {errors} from 'appium/driver';
3
- import {transformSourceXml} from './source';
4
- import {transformQuery} from './xpath';
5
- import log from './logger';
6
-
7
- export default class UniversalXMLPlugin extends BasePlugin {
8
- async getPageSource(next, driver, sessId, addIndexPath = false) {
9
- const source = next ? await next() : await driver.getPageSource();
10
- const metadata = {};
11
- const {platformName} = driver.caps;
12
- if (platformName.toLowerCase() === 'android') {
13
- metadata.appPackage = driver.opts.appPackage;
14
- }
15
- const {xml, unknowns} = await transformSourceXml(source, platformName.toLowerCase(), {
16
- metadata,
17
- addIndexPath,
18
- });
19
- if (unknowns.nodes.length) {
20
- log.warn(
21
- `The XML mapper found ${unknowns.nodes.length} node(s) / ` +
22
- `tag name(s) that it didn't know about. These should be ` +
23
- `reported to improve the quality of the plugin: ` +
24
- unknowns.nodes.join(', '),
25
- );
26
- }
27
- if (unknowns.attrs.length) {
28
- log.warn(
29
- `The XML mapper found ${unknowns.attrs.length} attributes ` +
30
- `that it didn't know about. These should be reported to ` +
31
- `improve the quality of the plugin: ` +
32
- unknowns.attrs.join(', '),
33
- );
34
- }
35
- return xml;
36
- }
37
-
38
- async findElement(...args) {
39
- return await this._find(false, ...args);
40
- }
41
-
42
- async findElements(...args) {
43
- return await this._find(true, ...args);
44
- }
45
-
46
- async _find(multiple, next, driver, strategy, selector) {
47
- const {platformName} = driver.caps;
48
- if (strategy.toLowerCase() !== 'xpath' || (await driver.getCurrentContext()) !== 'NATIVE_APP') {
49
- return await next();
50
- }
51
- const xml = await this.getPageSource(null, driver, null, true);
52
- let newSelector = transformQuery(selector, xml, multiple);
53
-
54
- // if the selector was not able to be transformed, that means no elements were found that
55
- // matched, so do the appropriate thing based on element vs elements
56
- if (newSelector === null) {
57
- log.warn(
58
- `Selector was not able to be translated to underlying XML. Either the requested ` +
59
- `element does not exist or there was an error in translation`,
60
- );
61
- if (multiple) {
62
- return [];
63
- }
64
- throw new errors.NoSuchElementError();
65
- }
66
-
67
- if (platformName.toLowerCase() === 'ios') {
68
- // with the XCUITest driver, the <AppiumAUT> wrapper element is present in the source but is
69
- // not present in the source considered by WDA, so our index path based xpath queries will
70
- // not work with WDA as-is. We need to remove the first path segment.
71
- newSelector = newSelector.replace(/^\/\*\[1\]/, '');
72
- }
73
- log.info(`Selector was translated to: ${newSelector}`);
74
-
75
- // otherwise just run the transformed query!
76
- const finder = multiple ? 'findElements' : 'findElement';
77
- return await driver[finder](strategy, newSelector);
78
- }
79
- }
80
-
81
- export {UniversalXMLPlugin};
package/lib/source.js DELETED
@@ -1,226 +0,0 @@
1
- import _ from 'lodash';
2
- import NODE_MAP from './node-map';
3
- import {ATTR_MAP, REMOVE_ATTRS} from './attr-map';
4
- import TRANSFORMS from './transformers';
5
-
6
- export const ATTR_PREFIX = '@_';
7
- export const IDX_PATH_PREFIX = `${ATTR_PREFIX}indexPath`;
8
- export const IDX_PREFIX = `${ATTR_PREFIX}index`;
9
-
10
- const isAttr = (/** @type {string} */ k) => k.startsWith(ATTR_PREFIX);
11
- const isNode = (/** @type {string} */ k) => !isAttr(k);
12
-
13
- /**
14
- *
15
- * @param {string} xmlStr
16
- * @param {string} platform
17
- * @param {{metadata?: Object, addIndexPath?: boolean}} opts
18
- * @returns {Promise<{xml: string, unknowns: NodesAndAttributes}>}
19
- */
20
- export async function transformSourceXml(xmlStr, platform, {metadata = {}, addIndexPath = false} = {}) {
21
- // first thing we want to do is modify the ios source root node, because it doesn't include the
22
- // necessary index attribute, so we add it if it's not there
23
- xmlStr = xmlStr.replace('<AppiumAUT>', '<AppiumAUT index="0">');
24
- const xmlObj = (await singletonXmlParser()).parse(xmlStr);
25
- const unknowns = transformNode(xmlObj, platform, {
26
- metadata,
27
- addIndexPath,
28
- parentPath: '',
29
- });
30
- let transformedXml = (await singletonXmlBuilder()).build(xmlObj).trim();
31
- transformedXml = `<?xml version="1.0" encoding="UTF-8"?>\n${transformedXml}`;
32
- return {xml: transformedXml, unknowns};
33
- }
34
-
35
- /**
36
- *
37
- * @param {Object} nameMap
38
- * @param {string} name
39
- * @param {string} platform
40
- * @returns {string | null}
41
- */
42
- function getUniversalName(nameMap, name, platform) {
43
- for (const translatedName of Object.keys(nameMap)) {
44
- const sourceNodes = nameMap[translatedName]?.[platform];
45
- if (_.isArray(sourceNodes) && sourceNodes.includes(name)) {
46
- return translatedName;
47
- }
48
- if (sourceNodes === name) {
49
- return translatedName;
50
- }
51
- }
52
- return null;
53
- }
54
-
55
- /**
56
- *
57
- * @param {any} nodeName
58
- * @param {string} platform
59
- * @returns {string?}
60
- */
61
- export function getUniversalNodeName(nodeName, platform) {
62
- return getUniversalName(NODE_MAP, nodeName, platform);
63
- }
64
-
65
- /**
66
- *
67
- * @param {string} attrName
68
- * @param {string} platform
69
- * @returns {string?}
70
- */
71
- export function getUniversalAttrName(attrName, platform) {
72
- return getUniversalName(ATTR_MAP, attrName, platform);
73
- }
74
-
75
- /**
76
- *
77
- * @param {any} nodeObj
78
- * @param {string} platform
79
- * @param {{metadata?: Object, addIndexPath?: boolean, parentPath?: string}} opts
80
- * @returns {NodesAndAttributes}
81
- */
82
- export function transformNode(nodeObj, platform, {metadata, addIndexPath, parentPath}) {
83
- const unknownNodes = [];
84
- const unknownAttrs = [];
85
- if (_.isPlainObject(nodeObj)) {
86
- const keys = Object.keys(nodeObj);
87
- const childNodeNames = keys.filter(isNode);
88
- const attrs = keys.filter(isAttr);
89
- let thisIndexPath = parentPath;
90
-
91
- if (attrs.length && addIndexPath) {
92
- if (!attrs.includes(IDX_PREFIX)) {
93
- throw new Error(`Index path is required but node found with no 'index' attribute`);
94
- }
95
-
96
- thisIndexPath = `${parentPath}/${nodeObj[IDX_PREFIX]}`;
97
- nodeObj[IDX_PATH_PREFIX] = thisIndexPath;
98
- }
99
-
100
- TRANSFORMS[platform]?.(nodeObj, metadata);
101
- unknownAttrs.push(...transformAttrs(nodeObj, attrs, platform));
102
- const unknowns = transformChildNodes(nodeObj, childNodeNames, platform, {
103
- metadata,
104
- addIndexPath,
105
- parentPath: thisIndexPath,
106
- });
107
- unknownAttrs.push(...unknowns.attrs);
108
- unknownNodes.push(...unknowns.nodes);
109
- } else if (_.isArray(nodeObj)) {
110
- for (const childObj of nodeObj) {
111
- const {nodes, attrs} = transformNode(childObj, platform, {
112
- metadata,
113
- addIndexPath,
114
- parentPath,
115
- });
116
- unknownNodes.push(...nodes);
117
- unknownAttrs.push(...attrs);
118
- }
119
- }
120
- return {
121
- nodes: _.uniq(unknownNodes),
122
- attrs: _.uniq(unknownAttrs),
123
- };
124
- }
125
-
126
- /**
127
- *
128
- * @param {any} nodeObj
129
- * @param {string[]} childNodeNames
130
- * @param {string} platform
131
- * @param {{metadata?: Object, addIndexPath?: boolean, parentPath?: string}} opts
132
- * @returns {NodesAndAttributes}
133
- */
134
- export function transformChildNodes(
135
- nodeObj,
136
- childNodeNames,
137
- platform,
138
- {metadata, addIndexPath, parentPath}
139
- ) {
140
- const unknownNodes = [];
141
- const unknownAttrs = [];
142
- for (const nodeName of childNodeNames) {
143
- // before modifying the name of this child node, recurse down and modify the subtree
144
- const {nodes, attrs} = transformNode(nodeObj[nodeName], platform, {
145
- metadata,
146
- addIndexPath,
147
- parentPath,
148
- });
149
- unknownNodes.push(...nodes);
150
- unknownAttrs.push(...attrs);
151
-
152
- // now translate the node name and replace the subtree with this node
153
- const universalName = getUniversalNodeName(nodeName, platform);
154
- if (universalName === null) {
155
- unknownNodes.push(nodeName);
156
- continue;
157
- }
158
-
159
- // since multiple child node names could map to the same new transformed node name, we can't
160
- // simply assign nodeObj[universalName] = nodeObj[nodeName]; we need to be sensitive to the
161
- // situation where the end result is an array of children having the same node name
162
- if (nodeObj[universalName]) {
163
- // if we already have a node with the universal name, that means we are mapping a second
164
- // original node name to the same universal node name, so we just push all its children into
165
- // the list
166
- nodeObj[universalName].push(...nodeObj[nodeName]);
167
- } else {
168
- nodeObj[universalName] = nodeObj[nodeName];
169
- }
170
- delete nodeObj[nodeName];
171
- }
172
- return {nodes: unknownNodes, attrs: unknownAttrs};
173
- }
174
-
175
- /**
176
- *
177
- * @param {any} nodeObj
178
- * @param {string[]} attrs
179
- * @param {string} platform
180
- * @returns {string[]}
181
- */
182
- export function transformAttrs(nodeObj, attrs, platform) {
183
- const unknownAttrs = [];
184
- for (const attr of attrs) {
185
- const cleanAttr = attr.substring(2);
186
- if (REMOVE_ATTRS.includes(cleanAttr)) {
187
- delete nodeObj[attr];
188
- continue;
189
- }
190
- const universalAttr = getUniversalAttrName(cleanAttr, platform);
191
- if (universalAttr === null) {
192
- unknownAttrs.push(cleanAttr);
193
- continue;
194
- }
195
- const newAttr = `${ATTR_PREFIX}${universalAttr}`;
196
- if (newAttr !== attr) {
197
- nodeObj[newAttr] = nodeObj[attr];
198
- delete nodeObj[attr];
199
- }
200
- }
201
- return unknownAttrs;
202
- }
203
-
204
- const singletonXmlBuilder = _.memoize(async function makeXmlBuilder() {
205
- const { XMLBuilder } = await import('fast-xml-parser');
206
- return new XMLBuilder({
207
- ignoreAttributes: false,
208
- attributeNamePrefix: ATTR_PREFIX,
209
- suppressBooleanAttributes: false,
210
- format: true,
211
- });
212
- });
213
-
214
- const singletonXmlParser = _.memoize(async function makeXmlParser() {
215
- const { XMLParser } = await import('fast-xml-parser');
216
- return new XMLParser({
217
- ignoreAttributes: false,
218
- ignoreDeclaration: true,
219
- attributeNamePrefix: ATTR_PREFIX,
220
- isArray: (name, jPath, isLeafNode, isAttribute) => !isAttribute,
221
- });
222
- });
223
-
224
- /**
225
- * @typedef {{nodes: string[], attrs: string[]}} NodesAndAttributes
226
- */