appium-cdp-driver 1.0.6 → 1.0.7

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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "appium",
6
6
  "chrome-dev-tools"
7
7
  ],
8
- "version": "1.0.6",
8
+ "version": "1.0.7",
9
9
  "author": "",
10
10
  "license": "Apache-2.0",
11
11
  "repository": {
@@ -66,6 +66,7 @@
66
66
  "chai": "^4.3.6",
67
67
  "chrome-remote-interface": "^0.31.3",
68
68
  "get-port": "^5.1.1",
69
+ "node-fetch": "^2.6.7",
69
70
  "taiko": "https://github.com/getgauge/taiko.git"
70
71
  },
71
72
  "peerDependencies": {
package/src/adb.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ADB, getSdkRootFromEnv } from 'appium-adb';
1
+ import { ADB, DEFAULT_ADB_PORT, getSdkRootFromEnv } from 'appium-adb';
2
2
  import { fs } from '@appium/support';
3
3
  import getPort from 'get-port';
4
4
  import log from './logger';
@@ -16,14 +16,28 @@ const DEVTOOLS_SOCKET_MAP = {
16
16
  terrance: 'com.sec.android.app.sbrowser_devtools_remote',
17
17
  };
18
18
 
19
- export async function getAdb() {
19
+ /**
20
+ * Resolve the adb server port: explicit value wins, then CDP_ADB_PORT (used by
21
+ * the skip-welcome scripts, which run as separate processes and receive no
22
+ * capabilities), then adb's default.
23
+ */
24
+ export function resolveAdbPort(adbPort) {
25
+ const candidate = adbPort ?? process.env.CDP_ADB_PORT;
26
+ const port = parseInt(candidate, 10);
27
+ return Number.isInteger(port) && port > 0 ? port : DEFAULT_ADB_PORT;
28
+ }
29
+
30
+ export async function getAdb(adbPort) {
20
31
  try {
21
32
  if (!adb) {
22
- adb = await ADB.createADB();
33
+ const port = resolveAdbPort(adbPort);
34
+ log.info(`Using adb server port ${port}`);
35
+ adb = await ADB.createADB({ adbPort: port });
23
36
  }
24
37
  } catch (e) {
25
38
  console.log(e);
26
39
  }
40
+ return adb;
27
41
  }
28
42
 
29
43
  export async function requireSdkRoot() {
@@ -76,7 +90,10 @@ async function getDuckDuckGoPid(browser = 'duckduckgo') {
76
90
  const packageName = 'com.duckduckgo.mobile.android';
77
91
  try {
78
92
  const output = await adb.adbExec(['shell', 'pidof', packageName]);
79
- const pid = output.trim();
93
+ // pidof lists every process of the package; the socket belongs to the first
94
+ // (browser) process, and passing the whole list builds an unusable socket
95
+ // name like webview_devtools_remote_1234 5678
96
+ const pid = String(output).trim().split(/\s+/)[0];
80
97
  return pid || null;
81
98
  } catch (err) {
82
99
  console.error(`Unable to get PID for ${browser}:`, err.message);
@@ -84,66 +101,47 @@ async function getDuckDuckGoPid(browser = 'duckduckgo') {
84
101
  }
85
102
  }
86
103
 
87
- export async function startApplication(browser = 'chrome') {
88
- const common = {
89
- waitDuration: START_APP_WAIT_DURATION,
90
- optionalIntentArguments: '-d www.appium.io',
91
- };
92
- const chrome = {
93
- pkg: 'com.android.chrome',
94
- activity: 'com.google.android.apps.chrome.Main',
95
- };
96
-
97
- const terrance = {
98
- pkg: 'com.sec.android.app.sbrowser',
99
- activity: 'com.sec.android.app.sbrowser.SBrowserMainActivity',
100
- };
101
-
102
- const brave = {
103
- pkg: 'com.brave.browser',
104
- activity: 'com.google.android.apps.chrome.Main',
105
- };
106
-
107
- const opera = {
108
- pkg: 'com.opera.browser',
109
- activity: 'com.opera.android.BrowserActivity',
110
- };
111
-
112
- const duckduckgo = {
113
- pkg: 'com.duckduckgo.mobile.android',
114
- activity: 'com.duckduckgo.app.browser.BrowserActivity',
115
- };
104
+ const BROWSERS = {
105
+ chrome: { pkg: 'com.android.chrome', activity: 'com.google.android.apps.chrome.Main' },
106
+ brave: { pkg: 'com.brave.browser', activity: 'com.google.android.apps.chrome.Main' },
107
+ opera: { pkg: 'com.opera.browser', activity: 'com.opera.android.BrowserActivity' },
108
+ duckduckgo: { pkg: 'com.duckduckgo.mobile.android', activity: 'com.duckduckgo.app.browser.BrowserActivity' },
109
+ samsung: { pkg: 'com.sec.android.app.sbrowser', activity: 'com.sec.android.app.sbrowser.SBrowserMainActivity' },
110
+ terrance: { pkg: 'com.sec.android.app.sbrowser', activity: 'com.sec.android.app.sbrowser.SBrowserMainActivity' },
111
+ edge: { pkg: 'com.microsoft.emmx', activity: 'com.microsoft.ruby.Main' },
112
+ };
116
113
 
117
- const samsung = {
118
- pkg: 'com.sec.android.app.sbrowser',
119
- activity: 'com.sec.android.app.sbrowser.SBrowserMainActivity',
120
- };
121
-
122
- const edge = {
123
- pkg: 'com.microsoft.emmx',
124
- activity: 'com.microsoft.ruby.Main',
125
- };
114
+ /**
115
+ * Open the start URL in an already-running browser.
116
+ *
117
+ * Deliberately a plain VIEW intent rather than startApplication: the latter
118
+ * passes -S, which force-stops the process. That changes the pid, and for
119
+ * WebView-based browsers the devtools socket name carries the pid, so an
120
+ * existing port forward is left pointing at a socket that no longer exists.
121
+ */
122
+ export async function openStartUrl(browser, url = 'https://www.appium.io') {
123
+ const pkg = BROWSERS[browser]?.pkg;
124
+ if (!pkg) {
125
+ return;
126
+ }
127
+ // -n <component>: a bare trailing package is not honoured by am, so the intent
128
+ // would go to whichever browser handles VIEW by default rather than this one
129
+ await adb.adbExec([
130
+ 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', url,
131
+ '-n', `${pkg}/${BROWSERS[browser].activity}`,
132
+ ]);
133
+ }
126
134
 
127
- if (browser === 'chrome') {
128
- log.info(`Starting Chrome`);
129
- await adb.startApp(Object.assign({}, chrome, common));
130
- } else if (browser === 'terrance') {
131
- log.info(`Starting Terrance`);
132
- await adb.startApp(Object.assign({}, terrance, common));
133
- } else if (browser === 'brave') {
134
- log.info(`Starting Brave`);
135
- await adb.startApp(Object.assign({}, brave, common));
136
- } else if (browser === 'opera') {
137
- log.info(`Starting Opera`);
138
- await adb.startApp(Object.assign({}, opera, common));
139
- } else if (browser === 'duckduckgo') {
140
- log.info(`Starting DuckDuckGo`);
141
- await adb.startApp(Object.assign({}, duckduckgo, common));
142
- } else if (browser === 'samsung') {
143
- log.info(`Starting Samsung Browser`);
144
- await adb.startApp(Object.assign({}, samsung, common));
145
- } else if (browser === 'edge') {
146
- log.info(`Starting MS Edge`);
147
- await adb.startApp(Object.assign({}, edge, common));
135
+ export async function startApplication(browser = 'chrome') {
136
+ const target = BROWSERS[browser];
137
+ if (!target) {
138
+ return;
148
139
  }
149
- }
140
+ log.info(`Starting ${browser}`);
141
+ await adb.startApp({
142
+ pkg: target.pkg,
143
+ activity: target.activity,
144
+ waitDuration: START_APP_WAIT_DURATION,
145
+ optionalIntentArguments: '-d www.appium.io',
146
+ });
147
+ }
package/src/driver.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BaseDriver, errors } from '@appium/base-driver';
2
- import { getAdb, adbExec, startApplication } from './adb';
2
+ import { getAdb, adbExec, startApplication, openStartUrl } from './adb';
3
3
  import { openBrowser, getConfig, goto } from 'taiko';
4
4
  import commands from './commands';
5
5
  import log from './logger';
@@ -19,6 +19,10 @@ class AppiumCDPDriver extends BaseDriver {
19
19
  presence: true,
20
20
  isString: true,
21
21
  },
22
+ adbPort: {
23
+ isNumber: false,
24
+ presence: false,
25
+ },
22
26
  };
23
27
  }
24
28
 
@@ -26,7 +30,7 @@ class AppiumCDPDriver extends BaseDriver {
26
30
  console.log(await getConfig('local'));
27
31
  const res = await super.createSession(w3cCaps);
28
32
  const browser = w3cCaps.alwaysMatch['browserName'];
29
- await getAdb();
33
+ await getAdb(w3cCaps.alwaysMatch['appium:adbPort']);
30
34
  let port;
31
35
  if (browser === 'duckduckgo') {
32
36
  await startApplication(browser);
@@ -55,6 +59,15 @@ class AppiumCDPDriver extends BaseDriver {
55
59
  log.info(`CDP response: ${JSON.stringify(data)}`);
56
60
 
57
61
  if (!Array.isArray(data) || data.length === 0) {
62
+ // The socket is up but the browser holds no debuggable page — common
63
+ // for WebView-based browsers just after launch. Retrying the fetch
64
+ // alone changes nothing, so nudge the browser into opening one.
65
+ log.info('Debug list is empty, opening the start URL to create a page');
66
+ try {
67
+ await openStartUrl(browser);
68
+ } catch (launchError) {
69
+ log.info(`Could not open the start URL: ${launchError.message}`);
70
+ }
58
71
  throw new Error('Debug list is empty or invalid');
59
72
  }
60
73
 
@@ -71,9 +84,21 @@ class AppiumCDPDriver extends BaseDriver {
71
84
  maxTimeout: 10000,
72
85
  }
73
86
  );
74
- const target = data.find((target) => {
75
- return target.url.includes('appium.io');
76
- });
87
+ // Prefer the page we launched, but do not require it: after a redirect, a
88
+ // new tab, or an onboarding page the list holds other targets, and reading
89
+ // webSocketDebuggerUrl off undefined fails the session with an opaque error.
90
+ let target = data.find((t) => t.url && t.url.includes('appium.io'));
91
+ if (!target) {
92
+ target =
93
+ data.find((t) => t.type === 'page' && t.webSocketDebuggerUrl) ||
94
+ data.find((t) => t.webSocketDebuggerUrl);
95
+ log.info(`No appium.io target; falling back to ${target ? target.url || target.type : 'none'}`);
96
+ }
97
+ if (!target) {
98
+ throw new Error(
99
+ `No CDP target with a webSocketDebuggerUrl in ${JSON.stringify(data)}`
100
+ );
101
+ }
77
102
  log.info(`Target found: ${target.webSocketDebuggerUrl}`);
78
103
  await openBrowser({
79
104
  port: port,
@@ -1,98 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- const appium_uiautomator2_driver_1 = require("appium-uiautomator2-driver");
17
- const appium_adb_1 = require("appium-adb");
18
- const logger_js_1 = __importDefault(require("../src/logger.js"));
19
- const asyncbox_1 = require("asyncbox");
20
- const START_APP_WAIT_DURATION = 60000;
21
- const brave = {
22
- pkg: 'com.brave.browser',
23
- activity: 'com.google.android.apps.chrome.Main',
24
- };
25
- const common = {
26
- waitDuration: START_APP_WAIT_DURATION,
27
- optionalIntentArguments: '-d www.appium.io',
28
- };
29
- function skipWelcomeBrave() {
30
- return __awaiter(this, void 0, void 0, function* () {
31
- const adb = yield appium_adb_1.ADB.createADB();
32
- yield adb.adbExec(['shell', 'pm', 'clear', 'com.brave.browser']);
33
- const driver = new appium_uiautomator2_driver_1.AndroidUiautomator2Driver();
34
- const caps = {
35
- platformName: "Android",
36
- "appium:automationName": "UiAutomator2",
37
- "appium:deviceName": "Android Device",
38
- "appium:appPackage": "com.brave.browser",
39
- "appium:appActivity": "com.google.android.apps.chrome.Main",
40
- };
41
- try {
42
- yield driver.createSession(null, {
43
- alwaysMatch: caps,
44
- firstMatch: [{}],
45
- });
46
- const activity = yield driver.getCurrentActivity();
47
- logger_js_1.default.info(`Activity is ${activity}`);
48
- if (activity.includes('Welcome')) {
49
- const findElementWithWaitForCondition = (strategy_1, selector_1, ...args_1) => __awaiter(this, [strategy_1, selector_1, ...args_1], void 0, function* (strategy, selector, timeout = 6000) {
50
- try {
51
- return yield (0, asyncbox_1.waitForCondition)(() => __awaiter(this, void 0, void 0, function* () {
52
- try {
53
- logger_js_1.default.info(`Attempting to find element (${strategy}: ${selector})`);
54
- const element = yield driver.findElement(strategy, selector);
55
- logger_js_1.default.info(`Successfully found element (${strategy}: ${selector})`);
56
- return element;
57
- }
58
- catch (error) {
59
- // Return false to continue waiting
60
- return false;
61
- }
62
- }), {
63
- waitMs: timeout,
64
- intervalMs: 1000,
65
- });
66
- }
67
- catch (error) {
68
- throw new Error(`Failed to find element (${strategy}: ${selector}) within ${timeout}ms timeout: ${error.message}`);
69
- }
70
- });
71
- const cancelButton = yield findElementWithWaitForCondition('id', 'android:id/button2');
72
- logger_js_1.default.info(`Cancel button is ${JSON.stringify(cancelButton, null, 2)}`);
73
- yield driver.click(cancelButton.ELEMENT);
74
- try {
75
- yield findElementWithWaitForCondition('xpath', '//android.widget.TextView[@text="Allow Brave to send you notifications?"]');
76
- const allowButton = yield findElementWithWaitForCondition('id', 'com.android.permissioncontroller:id/permission_allow_button');
77
- yield driver.click(allowButton.ELEMENT);
78
- }
79
- catch (error) {
80
- logger_js_1.default.info(`Help make Brave better text not found, skipping`);
81
- }
82
- const laterButton = yield findElementWithWaitForCondition('id', 'com.brave.browser:id/btn_negative');
83
- yield driver.click(laterButton.ELEMENT);
84
- yield findElementWithWaitForCondition('xpath', '//android.widget.TextView[@text="Help make Brave better."]');
85
- const continueButton = yield findElementWithWaitForCondition('id', 'com.brave.browser:id/btn_positive');
86
- yield driver.click(continueButton.ELEMENT);
87
- }
88
- }
89
- finally {
90
- yield driver.deleteSession();
91
- }
92
- });
93
- }
94
- (() => __awaiter(void 0, void 0, void 0, function* () {
95
- yield skipWelcomeBrave();
96
- process.exit(0);
97
- }))();
98
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJhdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zY3JpcHRzL2JyYXZlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUVBLDJFQUF1RTtBQUN2RSwyQ0FBaUM7QUFDakMsaUVBQW1DO0FBQ25DLHVDQUE0QztBQUU1QyxNQUFNLHVCQUF1QixHQUFHLEtBQUssQ0FBQztBQUV0QyxNQUFNLEtBQUssR0FBRztJQUNaLEdBQUcsRUFBRSxtQkFBbUI7SUFDeEIsUUFBUSxFQUFFLHFDQUFxQztDQUNoRCxDQUFDO0FBRUYsTUFBTSxNQUFNLEdBQUc7SUFDYixZQUFZLEVBQUUsdUJBQXVCO0lBQ3JDLHVCQUF1QixFQUFFLGtCQUFrQjtDQUM1QyxDQUFDO0FBRUYsU0FBZSxnQkFBZ0I7O1FBQzdCLE1BQU0sR0FBRyxHQUFHLE1BQU0sZ0JBQUcsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNsQyxNQUFNLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxtQkFBbUIsQ0FBQyxDQUFDLENBQUM7UUFDakUsTUFBTSxNQUFNLEdBQUcsSUFBSSxzREFBeUIsRUFBRSxDQUFDO1FBQy9DLE1BQU0sSUFBSSxHQUFHO1lBQ1gsWUFBWSxFQUFFLFNBQVM7WUFDdkIsdUJBQXVCLEVBQUUsY0FBYztZQUN2QyxtQkFBbUIsRUFBRSxnQkFBZ0I7WUFDckMsbUJBQW1CLEVBQUUsbUJBQW1CO1lBQ3hDLG9CQUFvQixFQUFFLHFDQUFxQztTQUM1RCxDQUFDO1FBQ0YsSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRTtnQkFDL0IsV0FBVyxFQUFFLElBQUk7Z0JBQ2pCLFVBQVUsRUFBRSxDQUFDLEVBQUUsQ0FBQzthQUNqQixDQUFDLENBQUM7WUFDSCxNQUFNLFFBQVEsR0FBRyxNQUFNLE1BQU0sQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQ25ELG1CQUFH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
@@ -1,100 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- const appium_uiautomator2_driver_1 = require("appium-uiautomator2-driver");
17
- const appium_adb_1 = require("appium-adb");
18
- const logger_js_1 = __importDefault(require("../src/logger.js"));
19
- const asyncbox_1 = require("asyncbox");
20
- const START_APP_WAIT_DURATION = 60000;
21
- const duckduckgo = {
22
- pkg: 'com.duckduckgo.mobile.android',
23
- activity: 'com.duckduckgo.app.browser.BrowserActivity',
24
- };
25
- const common = {
26
- waitDuration: START_APP_WAIT_DURATION,
27
- optionalIntentArguments: '-d www.appium.io',
28
- };
29
- function skipWelcomeDuckDuckGo() {
30
- return __awaiter(this, void 0, void 0, function* () {
31
- const adb = yield appium_adb_1.ADB.createADB();
32
- yield adb.adbExec(['shell', 'pm', 'clear', 'com.duckduckgo.mobile.android']);
33
- const driver = new appium_uiautomator2_driver_1.AndroidUiautomator2Driver();
34
- const caps = {
35
- platformName: "Android",
36
- "appium:automationName": "UiAutomator2",
37
- "appium:deviceName": "Android Device",
38
- "appium:appPackage": "com.duckduckgo.mobile.android",
39
- "appium:appActivity": "com.duckduckgo.app.browser.BrowserActivity",
40
- };
41
- try {
42
- yield driver.createSession(null, {
43
- alwaysMatch: caps,
44
- firstMatch: [{}],
45
- });
46
- const activity = yield driver.getCurrentActivity();
47
- logger_js_1.default.info(`Activity is ${activity}`);
48
- if (activity.includes('BrowserActivity')) {
49
- const findElementWithWaitForCondition = (strategy_1, selector_1, ...args_1) => __awaiter(this, [strategy_1, selector_1, ...args_1], void 0, function* (strategy, selector, timeout = 6000) {
50
- try {
51
- return yield (0, asyncbox_1.waitForCondition)(() => __awaiter(this, void 0, void 0, function* () {
52
- try {
53
- logger_js_1.default.info(`Attempting to find element (${strategy}: ${selector})`);
54
- const element = yield driver.findElement(strategy, selector);
55
- logger_js_1.default.info(`Successfully found element (${strategy}: ${selector})`);
56
- return element;
57
- }
58
- catch (error) {
59
- // Return false to continue waiting
60
- return false;
61
- }
62
- }), {
63
- waitMs: timeout,
64
- intervalMs: 1000,
65
- });
66
- }
67
- catch (error) {
68
- throw new Error(`Failed to find element (${strategy}: ${selector}) within ${timeout}ms timeout: ${error.message}`);
69
- }
70
- });
71
- let attempt = 0;
72
- let found = false;
73
- const MAX_RETRIES = 3;
74
- while (attempt < MAX_RETRIES && !found) {
75
- try {
76
- const dismissButton = yield findElementWithWaitForCondition('id', 'com.duckduckgo.mobile.android:id/bottomSheetPromoSecondaryButton');
77
- logger_js_1.default.info(`Dismiss button found: ${JSON.stringify(dismissButton, null, 2)} in attempt ${attempt}`);
78
- yield driver.click(dismissButton.ELEMENT);
79
- found = true;
80
- }
81
- catch (error) {
82
- logger_js_1.default.info(`Dismiss button not found, retrying after opening a url...`);
83
- }
84
- finally {
85
- yield adb.shell(['am', 'start', '-a', 'android.intent.action.VIEW', '-d', 'https://www.appium.io', duckduckgo.pkg]);
86
- attempt++;
87
- }
88
- }
89
- }
90
- }
91
- finally {
92
- yield driver.deleteSession();
93
- }
94
- });
95
- }
96
- (() => __awaiter(void 0, void 0, void 0, function* () {
97
- yield skipWelcomeDuckDuckGo();
98
- process.exit(0);
99
- }))();
100
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHVja2R1Y2tnby5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NjcmlwdHMvZHVja2R1Y2tnby5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7QUFFQSwyRUFBdUU7QUFDdkUsMkNBQWlDO0FBQ2pDLGlFQUFtQztBQUNuQyx1Q0FBNEM7QUFFNUMsTUFBTSx1QkFBdUIsR0FBRyxLQUFLLENBQUM7QUFFdEMsTUFBTSxVQUFVLEdBQUc7SUFDakIsR0FBRyxFQUFFLCtCQUErQjtJQUNwQyxRQUFRLEVBQUUsNENBQTRDO0NBQ3ZELENBQUM7QUFFRixNQUFNLE1BQU0sR0FBRztJQUNiLFlBQVksRUFBRSx1QkFBdUI7SUFDckMsdUJBQXVCLEVBQUUsa0JBQWtCO0NBQzVDLENBQUM7QUFFRixTQUFlLHFCQUFxQjs7UUFDbEMsTUFBTSxHQUFHLEdBQUcsTUFBTSxnQkFBRyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ2xDLE1BQU0sR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLCtCQUErQixDQUFDLENBQUMsQ0FBQztRQUM3RSxNQUFNLE1BQU0sR0FBRyxJQUFJLHNEQUF5QixFQUFFLENBQUM7UUFDL0MsTUFBTSxJQUFJLEdBQUc7WUFDWCxZQUFZLEVBQUUsU0FBUztZQUN2Qix1QkFBdUIsRUFBRSxjQUFjO1lBQ3ZDLG1CQUFtQixFQUFFLGdCQUFnQjtZQUNyQyxtQkFBbUIsRUFBRSwrQkFBK0I7WUFDcEQsb0JBQW9CLEVBQUUsNENBQTRDO1NBQ25FLENBQUM7UUFDRixJQUFJLENBQUM7WUFDSCxNQUFNLE1BQU0sQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFO2dCQUMvQixXQUFXLEVBQUUsSUFBSTtnQkFDakIsVUFBVSxFQUFFLENBQUMsRUFBRSxDQUFDO2FBQ2pCLENBQUMsQ0FBQztZQUNILE1BQU0sUUFBUSxHQUFHLE1BQU0sTUFBTSxDQUFDLGtCQUFrQixFQUFFLENBQUM7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
@@ -1,194 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- const appium_uiautomator2_driver_1 = require("appium-uiautomator2-driver");
17
- const appium_adb_1 = require("appium-adb");
18
- const logger_js_1 = __importDefault(require("../src/logger.js"));
19
- const asyncbox_1 = require("asyncbox");
20
- const START_APP_WAIT_DURATION = 60000;
21
- const edge = {
22
- pkg: 'com.microsoft.emmx',
23
- activity: 'com.microsoft.ruby.Main',
24
- };
25
- const common = {
26
- waitDuration: START_APP_WAIT_DURATION,
27
- optionalIntentArguments: '-d www.appium.io',
28
- };
29
- function skipWelcomeEdge() {
30
- return __awaiter(this, void 0, void 0, function* () {
31
- const adb = yield appium_adb_1.ADB.createADB();
32
- yield adb.adbExec(['shell', 'pm', 'clear', edge.pkg]);
33
- const driver = new appium_uiautomator2_driver_1.AndroidUiautomator2Driver();
34
- const caps = {
35
- platformName: "Android",
36
- "appium:automationName": "UiAutomator2",
37
- "appium:deviceName": "Android Device",
38
- "appium:appPackage": edge.pkg,
39
- "appium:appActivity": edge.activity,
40
- };
41
- const findElementWithWaitForCondition = (strategy_1, selector_1, ...args_1) => __awaiter(this, [strategy_1, selector_1, ...args_1], void 0, function* (strategy, selector, timeout = 6000) {
42
- try {
43
- return yield (0, asyncbox_1.waitForCondition)(() => __awaiter(this, void 0, void 0, function* () {
44
- try {
45
- logger_js_1.default.info(`Attempting to find element (${strategy}: ${selector})`);
46
- const element = yield driver.findElement(strategy, selector);
47
- logger_js_1.default.info(`Successfully found element (${strategy}: ${selector})`);
48
- return element;
49
- }
50
- catch (error) {
51
- // Return false to continue waiting
52
- return false;
53
- }
54
- }), {
55
- waitMs: timeout,
56
- intervalMs: 1000,
57
- });
58
- }
59
- catch (error) {
60
- throw new Error(`Failed to find element (${strategy}: ${selector}) within ${timeout}ms timeout: ${error.message}`);
61
- }
62
- });
63
- try {
64
- yield driver.createSession(null, {
65
- alwaysMatch: caps,
66
- firstMatch: [{}],
67
- });
68
- const activity = yield driver.getCurrentActivity();
69
- logger_js_1.default.info(`Activity is ${activity}`);
70
- if (activity.includes('MicrosoftFirstRunActivity')) {
71
- // wait for the page to load
72
- yield new Promise(resolve => setTimeout(resolve, 5000));
73
- const MAX_RETRIES = 3;
74
- let attempt = 0;
75
- let clicked = false;
76
- let found = false;
77
- var notNowButton;
78
- while (attempt < MAX_RETRIES && !clicked) {
79
- try {
80
- notNowButton = yield findElementWithWaitForCondition('xpath', '//android.widget.Button[@text="Not now"]');
81
- logger_js_1.default.info(`Not Now button is ${JSON.stringify(notNowButton, null, 2)}`);
82
- found = true;
83
- }
84
- catch (error) {
85
- logger_js_1.default.info(`Not Now button not found, retrying ...`);
86
- }
87
- finally {
88
- if (found) {
89
- yield driver.click(notNowButton.ELEMENT);
90
- clicked = true;
91
- }
92
- attempt++;
93
- }
94
- }
95
- attempt = 0;
96
- clicked = false;
97
- found = false;
98
- var checkBox;
99
- while (attempt < MAX_RETRIES && !clicked) {
100
- try {
101
- checkBox = yield findElementWithWaitForCondition('xpath', '//android.widget.Image');
102
- logger_js_1.default.info(`Check box is ${JSON.stringify(checkBox, null, 2)}`);
103
- found = true;
104
- }
105
- catch (error) {
106
- logger_js_1.default.info(`Check box not found, retrying the other xpath...`);
107
- try {
108
- checkBox = yield findElementWithWaitForCondition('xpath', '//android.widget.CheckBox[@text="Help improve Microsoft products by sending optional diagnostic data about how you use the browser, websites you visit, and crash reports."]');
109
- logger_js_1.default.info(`Check box is ${JSON.stringify(checkBox, null, 2)}`);
110
- found = true;
111
- }
112
- catch (error) {
113
- logger_js_1.default.info(`Check box not found, retrying...`);
114
- }
115
- }
116
- finally {
117
- if (found) {
118
- yield driver.click(checkBox.ELEMENT);
119
- clicked = true;
120
- }
121
- attempt++;
122
- }
123
- }
124
- attempt = 0;
125
- clicked = false;
126
- found = false;
127
- var confirmButton;
128
- while (attempt < MAX_RETRIES && !clicked) {
129
- try {
130
- confirmButton = yield findElementWithWaitForCondition('xpath', '//android.widget.Button[@text="Confirm"]');
131
- logger_js_1.default.info(`Confirm button is ${JSON.stringify(confirmButton, null, 2)}`);
132
- found = true;
133
- }
134
- catch (error) {
135
- logger_js_1.default.info(`Confirm button not found, retrying...`);
136
- }
137
- finally {
138
- if (found) {
139
- yield driver.click(confirmButton.ELEMENT);
140
- clicked = true;
141
- }
142
- attempt++;
143
- }
144
- }
145
- }
146
- }
147
- finally {
148
- yield driver.deleteSession();
149
- }
150
- try {
151
- caps["appium:noReset"] = true;
152
- yield driver.createSession(null, {
153
- alwaysMatch: caps,
154
- firstMatch: [{}],
155
- });
156
- const activity = yield driver.getCurrentActivity();
157
- logger_js_1.default.info(`Activity is ${activity}`);
158
- if (activity.includes('GrantPermissionsActivity')) {
159
- // wait for the page to load
160
- yield new Promise(resolve => setTimeout(resolve, 5000));
161
- const MAX_RETRIES = 3;
162
- let attempt = 0;
163
- let clicked = false;
164
- let found = false;
165
- var denyButton;
166
- while (attempt < MAX_RETRIES && !clicked) {
167
- try {
168
- denyButton = yield findElementWithWaitForCondition('id', 'com.android.permissioncontroller:id/permission_allow_button');
169
- logger_js_1.default.info(`Deny button is ${JSON.stringify(denyButton, null, 2)}`);
170
- found = true;
171
- }
172
- catch (error) {
173
- logger_js_1.default.info(`Deny button not found, retrying ...`);
174
- }
175
- finally {
176
- if (found) {
177
- yield driver.click(denyButton.ELEMENT);
178
- clicked = true;
179
- }
180
- attempt++;
181
- }
182
- }
183
- }
184
- }
185
- finally {
186
- yield driver.deleteSession();
187
- }
188
- });
189
- }
190
- (() => __awaiter(void 0, void 0, void 0, function* () {
191
- yield skipWelcomeEdge();
192
- process.exit(0);
193
- }))();
194
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NjcmlwdHMvZWRnZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7QUFFQSwyRUFBdUU7QUFDdkUsMkNBQWlDO0FBQ2pDLGlFQUFtQztBQUNuQyx1Q0FBNEM7QUFFNUMsTUFBTSx1QkFBdUIsR0FBRyxLQUFLLENBQUM7QUFFdEMsTUFBTSxJQUFJLEdBQUc7SUFDWCxHQUFHLEVBQUUsb0JBQW9CO0lBQ3pCLFFBQVEsRUFBRSx5QkFBeUI7Q0FDcEMsQ0FBQztBQUVGLE1BQU0sTUFBTSxHQUFHO0lBQ2IsWUFBWSxFQUFFLHVCQUF1QjtJQUNyQyx1QkFBdUIsRUFBRSxrQkFBa0I7Q0FDNUMsQ0FBQztBQUVGLFNBQWUsZUFBZTs7UUFDNUIsTUFBTSxHQUFHLEdBQUcsTUFBTSxnQkFBRyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ2xDLE1BQU0sR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sTUFBTSxHQUFHLElBQUksc0RBQXlCLEVBQUUsQ0FBQztRQUMvQyxNQUFNLElBQUksR0FBRztZQUNYLFlBQVksRUFBRSxTQUFTO1lBQ3ZCLHVCQUF1QixFQUFFLGNBQWM7WUFDdkMsbUJBQW1CLEVBQUUsZ0JBQWdCO1lBQ3JDLG1CQUFtQixFQUFFLElBQUksQ0FBQyxHQUFHO1lBQzdCLG9CQUFvQixFQUFFLElBQUksQ0FBQyxRQUFRO1NBQ3BDLENBQUM7UUFFRixNQUFNLCtCQUErQixHQUFHLG9DQUl0QyxFQUFFLHlFQUhGLFFBQVEsRUFDUixRQUFRLEVBQ1IsT0FBTyxHQUFHLElBQUk7WUFFZCxJQUFJLENBQUM7Z0JBQ0gsT0FBTyxNQUFNLElBQUEsMkJBQWdCLEVBQzNCLEdBQVMsRUFBRTtvQkFDVCxJQUFJLENBQUM7d0JBQ0gsbUJBQUcsQ0FBQyxJ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