webdriverio 9.6.4 → 9.7.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.
- package/build/commands/browser.d.ts +3 -0
- package/build/commands/browser.d.ts.map +1 -1
- package/build/commands/mobile/getContext.d.ts +107 -0
- package/build/commands/mobile/getContext.d.ts.map +1 -0
- package/build/commands/mobile/getContexts.d.ts +158 -0
- package/build/commands/mobile/getContexts.d.ts.map +1 -0
- package/build/commands/mobile/switchContext.d.ts +112 -0
- package/build/commands/mobile/switchContext.d.ts.map +1 -0
- package/build/commands/mobile.d.ts +3 -0
- package/build/commands/mobile.d.ts.map +1 -1
- package/build/index.cjs +3 -3
- package/build/index.js +325 -65
- package/build/node.js +319 -65
- package/build/session/context.d.ts.map +1 -1
- package/build/types.d.ts +39 -0
- package/build/types.d.ts.map +1 -1
- package/package.json +4 -4
package/build/node.js
CHANGED
|
@@ -263,7 +263,7 @@ var ContextManager = class _ContextManager extends SessionManager {
|
|
|
263
263
|
isNativeContext: this.#isNativeContext
|
|
264
264
|
});
|
|
265
265
|
this.#browser.on("result", this.#onCommandResultBidiAndClassic.bind(this));
|
|
266
|
-
if (!this.isEnabled()) {
|
|
266
|
+
if (!this.isEnabled() && !this.#browser.isMobile) {
|
|
267
267
|
return;
|
|
268
268
|
}
|
|
269
269
|
this.#browser.on("command", this.#onCommand.bind(this));
|
|
@@ -308,15 +308,15 @@ var ContextManager = class _ContextManager extends SessionManager {
|
|
|
308
308
|
if (COMMANDS_REQUIRING_RESET.includes(event.command)) {
|
|
309
309
|
this.#currentContext = void 0;
|
|
310
310
|
}
|
|
311
|
-
if (this.#browser.isMobile && event.command === "
|
|
311
|
+
if (this.#browser.isMobile && event.command === "switchAppiumContext") {
|
|
312
312
|
this.#mobileContext = event.body.name;
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
#onCommandResultMobile(event) {
|
|
316
|
-
if (event.command === "
|
|
316
|
+
if (event.command === "getAppiumContext") {
|
|
317
317
|
this.setCurrentContext(event.result.value);
|
|
318
318
|
}
|
|
319
|
-
if (event.command === "
|
|
319
|
+
if (event.command === "switchAppiumContext" && event.result.value === null && this.#mobileContext) {
|
|
320
320
|
this.setCurrentContext(this.#mobileContext);
|
|
321
321
|
}
|
|
322
322
|
}
|
|
@@ -476,7 +476,7 @@ async function saveElementScreenshot(filepath) {
|
|
|
476
476
|
}
|
|
477
477
|
|
|
478
478
|
// src/index.ts
|
|
479
|
-
import
|
|
479
|
+
import logger28 from "@wdio/logger";
|
|
480
480
|
import WebDriver, { DEFAULTS } from "webdriver";
|
|
481
481
|
import { validateConfig } from "@wdio/config";
|
|
482
482
|
import { enableFileLogging, wrapCommand as wrapCommand3 } from "@wdio/utils";
|
|
@@ -549,7 +549,7 @@ async function refetchElement(currentElement, commandName) {
|
|
|
549
549
|
import cssValue from "css-value";
|
|
550
550
|
import rgb2hex from "rgb2hex";
|
|
551
551
|
import GraphemeSplitter from "grapheme-splitter";
|
|
552
|
-
import
|
|
552
|
+
import logger27 from "@wdio/logger";
|
|
553
553
|
import isPlainObject from "is-plain-obj";
|
|
554
554
|
import { ELEMENT_KEY as ELEMENT_KEY18 } from "webdriver";
|
|
555
555
|
import { UNICODE_CHARACTERS as UNICODE_CHARACTERS2, asyncIterators, getBrowserObject as getBrowserObject35 } from "@wdio/utils";
|
|
@@ -572,6 +572,8 @@ __export(browser_exports, {
|
|
|
572
572
|
emulate: () => emulate,
|
|
573
573
|
execute: () => execute,
|
|
574
574
|
executeAsync: () => executeAsync,
|
|
575
|
+
getContext: () => getContext,
|
|
576
|
+
getContexts: () => getContexts,
|
|
575
577
|
getCookies: () => getCookies,
|
|
576
578
|
getPuppeteer: () => getPuppeteer,
|
|
577
579
|
getWindowSize: () => getWindowSize,
|
|
@@ -594,6 +596,7 @@ __export(browser_exports, {
|
|
|
594
596
|
setViewport: () => setViewport,
|
|
595
597
|
setWindowSize: () => setWindowSize,
|
|
596
598
|
swipe: () => swipe,
|
|
599
|
+
switchContext: () => switchContext,
|
|
597
600
|
switchFrame: () => switchFrame,
|
|
598
601
|
switchWindow: () => switchWindow,
|
|
599
602
|
tap: () => tap,
|
|
@@ -4699,15 +4702,15 @@ var NetworkManager = class _NetworkManager extends SessionManager {
|
|
|
4699
4702
|
async initialize() {
|
|
4700
4703
|
return this.#initialize;
|
|
4701
4704
|
}
|
|
4702
|
-
#beforeRequestSent(
|
|
4703
|
-
if (
|
|
4705
|
+
#beforeRequestSent(log28) {
|
|
4706
|
+
if (log28.navigation) {
|
|
4704
4707
|
return;
|
|
4705
4708
|
}
|
|
4706
|
-
const request = this.#findRootRequest(
|
|
4709
|
+
const request = this.#findRootRequest(log28.navigation);
|
|
4707
4710
|
if (!request) {
|
|
4708
4711
|
return;
|
|
4709
4712
|
}
|
|
4710
|
-
const { request: id, headers, cookies, url: url2 } =
|
|
4713
|
+
const { request: id, headers, cookies, url: url2 } = log28.request;
|
|
4711
4714
|
request.children?.push({
|
|
4712
4715
|
id,
|
|
4713
4716
|
url: url2,
|
|
@@ -4723,51 +4726,51 @@ var NetworkManager = class _NetworkManager extends SessionManager {
|
|
|
4723
4726
|
sameSite: cookie.sameSite,
|
|
4724
4727
|
expiry: cookie.expiry
|
|
4725
4728
|
})),
|
|
4726
|
-
timestamp:
|
|
4729
|
+
timestamp: log28.timestamp
|
|
4727
4730
|
});
|
|
4728
4731
|
}
|
|
4729
|
-
#navigationStarted(
|
|
4732
|
+
#navigationStarted(log28) {
|
|
4730
4733
|
if (
|
|
4731
4734
|
/**
|
|
4732
4735
|
* we need a navigation id to identify the request
|
|
4733
4736
|
*/
|
|
4734
|
-
!
|
|
4737
|
+
!log28.navigation || /**
|
|
4735
4738
|
* ignore urls that users wouldn't navigate to
|
|
4736
4739
|
*/
|
|
4737
|
-
!SUPPORTED_NAVIGATION_PROTOCOLS.some((protocol) =>
|
|
4740
|
+
!SUPPORTED_NAVIGATION_PROTOCOLS.some((protocol) => log28.url.startsWith(protocol))
|
|
4738
4741
|
) {
|
|
4739
|
-
if (
|
|
4742
|
+
if (log28.navigation === null && log28.url === "") {
|
|
4740
4743
|
this.#lastNetworkId = UNKNOWN_NAVIGATION_ID;
|
|
4741
4744
|
return this.#requests.set(UNKNOWN_NAVIGATION_ID, {
|
|
4742
4745
|
url: "",
|
|
4743
4746
|
headers: {},
|
|
4744
|
-
timestamp:
|
|
4747
|
+
timestamp: log28.timestamp,
|
|
4745
4748
|
redirectChain: [],
|
|
4746
4749
|
children: []
|
|
4747
4750
|
});
|
|
4748
4751
|
}
|
|
4749
4752
|
return;
|
|
4750
4753
|
}
|
|
4751
|
-
this.#lastNetworkId =
|
|
4752
|
-
this.#requests.set(
|
|
4753
|
-
url:
|
|
4754
|
+
this.#lastNetworkId = log28.navigation;
|
|
4755
|
+
this.#requests.set(log28.navigation, {
|
|
4756
|
+
url: log28.url,
|
|
4754
4757
|
headers: {},
|
|
4755
|
-
timestamp:
|
|
4756
|
-
navigation:
|
|
4758
|
+
timestamp: log28.timestamp,
|
|
4759
|
+
navigation: log28.navigation,
|
|
4757
4760
|
redirectChain: [],
|
|
4758
4761
|
children: []
|
|
4759
4762
|
});
|
|
4760
4763
|
}
|
|
4761
|
-
#fetchError(
|
|
4762
|
-
const response = this.#findRootRequest(
|
|
4764
|
+
#fetchError(log28) {
|
|
4765
|
+
const response = this.#findRootRequest(log28.navigation);
|
|
4763
4766
|
if (!response) {
|
|
4764
4767
|
return;
|
|
4765
4768
|
}
|
|
4766
|
-
const request = response.children?.find((child) => child.id ===
|
|
4769
|
+
const request = response.children?.find((child) => child.id === log28.request.request);
|
|
4767
4770
|
if (!request) {
|
|
4768
4771
|
return;
|
|
4769
4772
|
}
|
|
4770
|
-
request.error =
|
|
4773
|
+
request.error = log28.errorText;
|
|
4771
4774
|
}
|
|
4772
4775
|
#findRootRequest(navigationId) {
|
|
4773
4776
|
const response = this.#requests.get(navigationId || UNKNOWN_NAVIGATION_ID);
|
|
@@ -4777,22 +4780,22 @@ var NetworkManager = class _NetworkManager extends SessionManager {
|
|
|
4777
4780
|
const firstRequest = this.#requests.values().next().value;
|
|
4778
4781
|
return this.#lastNetworkId ? this.#requests.get(this.#lastNetworkId) || firstRequest : firstRequest;
|
|
4779
4782
|
}
|
|
4780
|
-
#responseCompleted(
|
|
4781
|
-
const response = this.#findRootRequest(
|
|
4783
|
+
#responseCompleted(log28) {
|
|
4784
|
+
const response = this.#findRootRequest(log28.navigation);
|
|
4782
4785
|
if (!response) {
|
|
4783
4786
|
return;
|
|
4784
4787
|
}
|
|
4785
4788
|
if (!response.navigation && response.url === "") {
|
|
4786
|
-
response.url =
|
|
4787
|
-
response.navigation =
|
|
4789
|
+
response.url = log28.request.url;
|
|
4790
|
+
response.navigation = log28.navigation;
|
|
4788
4791
|
}
|
|
4789
|
-
if (
|
|
4790
|
-
if (response.url !==
|
|
4792
|
+
if (log28.navigation === response.navigation) {
|
|
4793
|
+
if (response.url !== log28.response.url) {
|
|
4791
4794
|
response.redirectChain?.push(response.url);
|
|
4792
4795
|
}
|
|
4793
|
-
response.url =
|
|
4794
|
-
const { headers: requestHeaders } =
|
|
4795
|
-
const { fromCache, headers: responseHeaders, mimeType, status } =
|
|
4796
|
+
response.url = log28.response.url;
|
|
4797
|
+
const { headers: requestHeaders } = log28.request;
|
|
4798
|
+
const { fromCache, headers: responseHeaders, mimeType, status } = log28.response;
|
|
4796
4799
|
response.headers = headerListToObject(requestHeaders), response.response = {
|
|
4797
4800
|
fromCache,
|
|
4798
4801
|
headers: headerListToObject(responseHeaders),
|
|
@@ -4801,15 +4804,15 @@ var NetworkManager = class _NetworkManager extends SessionManager {
|
|
|
4801
4804
|
};
|
|
4802
4805
|
return;
|
|
4803
4806
|
}
|
|
4804
|
-
const request = response.children?.find((child) => child.id ===
|
|
4807
|
+
const request = response.children?.find((child) => child.id === log28.request.request);
|
|
4805
4808
|
if (!request) {
|
|
4806
4809
|
return;
|
|
4807
4810
|
}
|
|
4808
4811
|
request.response = {
|
|
4809
|
-
fromCache:
|
|
4810
|
-
headers: headerListToObject(
|
|
4811
|
-
mimeType:
|
|
4812
|
-
status:
|
|
4812
|
+
fromCache: log28.response.fromCache,
|
|
4813
|
+
headers: headerListToObject(log28.response.headers),
|
|
4814
|
+
mimeType: log28.response.mimeType,
|
|
4815
|
+
status: log28.response.status
|
|
4813
4816
|
};
|
|
4814
4817
|
response.children?.push(request);
|
|
4815
4818
|
}
|
|
@@ -4879,14 +4882,14 @@ var DialogManager = class _DialogManager extends SessionManager {
|
|
|
4879
4882
|
/**
|
|
4880
4883
|
* capture shadow root elements propagated through console.debug
|
|
4881
4884
|
*/
|
|
4882
|
-
async #handleUserPrompt(
|
|
4885
|
+
async #handleUserPrompt(log28) {
|
|
4883
4886
|
if (this.#autoHandleDialog) {
|
|
4884
4887
|
return this.#browser.browsingContextHandleUserPrompt({
|
|
4885
4888
|
accept: false,
|
|
4886
|
-
context:
|
|
4889
|
+
context: log28.context
|
|
4887
4890
|
});
|
|
4888
4891
|
}
|
|
4889
|
-
const dialog = new Dialog(
|
|
4892
|
+
const dialog = new Dialog(log28, this.#browser);
|
|
4890
4893
|
this.#browser.emit("dialog", dialog);
|
|
4891
4894
|
}
|
|
4892
4895
|
/**
|
|
@@ -5836,6 +5839,257 @@ async function screenTap(browser, options) {
|
|
|
5836
5839
|
).move({ x, y }).down({ button: 0 }).pause(10).up({ button: 0 }).perform();
|
|
5837
5840
|
}
|
|
5838
5841
|
|
|
5842
|
+
// src/commands/mobile/getContext.ts
|
|
5843
|
+
import logger19 from "@wdio/logger";
|
|
5844
|
+
var log19 = logger19("webdriver");
|
|
5845
|
+
async function getContext(options) {
|
|
5846
|
+
const browser = this;
|
|
5847
|
+
if (!browser.isMobile) {
|
|
5848
|
+
throw new Error("The `getContext` command is only available for mobile platforms.");
|
|
5849
|
+
}
|
|
5850
|
+
const currentAppiumContext = await browser.getAppiumContext();
|
|
5851
|
+
if (!options || !options?.returnDetailedContext || currentAppiumContext === "NATIVE_APP") {
|
|
5852
|
+
return currentAppiumContext;
|
|
5853
|
+
}
|
|
5854
|
+
delete options.returnDetailedContext;
|
|
5855
|
+
return getDetailedContext(browser, currentAppiumContext, options);
|
|
5856
|
+
}
|
|
5857
|
+
async function getDetailedContext(browser, currentAppiumContext, options) {
|
|
5858
|
+
const detailedContexts = await browser.getContexts({
|
|
5859
|
+
...{ options },
|
|
5860
|
+
// Defaults
|
|
5861
|
+
returnDetailedContexts: true,
|
|
5862
|
+
// We want to get back the detailed context information
|
|
5863
|
+
isAndroidWebviewVisible: true,
|
|
5864
|
+
// We only want to get back the visible webviews
|
|
5865
|
+
filterByCurrentAndroidApp: true,
|
|
5866
|
+
// We only want to get back the webviews that are attached to the current app
|
|
5867
|
+
returnAndroidDescriptionData: false
|
|
5868
|
+
// We don't want to get back the Android Webview description data
|
|
5869
|
+
});
|
|
5870
|
+
const parsedContexts = detailedContexts.filter((context) => context.id === currentAppiumContext);
|
|
5871
|
+
if (parsedContexts.length > 1) {
|
|
5872
|
+
log19.warn(`We found more than 1 detailed context for the current context '${currentAppiumContext}'. We will return the first context.`);
|
|
5873
|
+
return parsedContexts[0];
|
|
5874
|
+
} else if (parsedContexts.length === 0) {
|
|
5875
|
+
log19.warn(`We did not get back any detailed context for the current context '${currentAppiumContext}'. We will return the current context as a string.`);
|
|
5876
|
+
return currentAppiumContext;
|
|
5877
|
+
}
|
|
5878
|
+
return parsedContexts[0];
|
|
5879
|
+
}
|
|
5880
|
+
|
|
5881
|
+
// src/commands/mobile/getContexts.ts
|
|
5882
|
+
import logger20 from "@wdio/logger";
|
|
5883
|
+
var log20 = logger20("webdriver");
|
|
5884
|
+
async function getContexts(options) {
|
|
5885
|
+
const browser = this;
|
|
5886
|
+
if (!browser.isMobile) {
|
|
5887
|
+
throw new Error("The `getContexts` command is only available for mobile platforms.");
|
|
5888
|
+
}
|
|
5889
|
+
if (!options || !options.returnDetailedContexts) {
|
|
5890
|
+
log20.info("The standard Appium `contexts` method is used. If you want to get more detailed data, you can set `returnDetailedContexts` to `true`.");
|
|
5891
|
+
return browser.getAppiumContexts();
|
|
5892
|
+
}
|
|
5893
|
+
const defaultOptions = {
|
|
5894
|
+
androidWebviewConnectionRetryTime: 500,
|
|
5895
|
+
androidWebviewConnectTimeout: 5e3,
|
|
5896
|
+
filterByCurrentAndroidApp: false,
|
|
5897
|
+
isAndroidWebviewVisible: true,
|
|
5898
|
+
returnAndroidDescriptionData: false
|
|
5899
|
+
};
|
|
5900
|
+
return getCurrentContexts({ browser, ...{ ...defaultOptions, ...options } });
|
|
5901
|
+
}
|
|
5902
|
+
var CHROME_PACKAGE_NAME = "com.android.chrome";
|
|
5903
|
+
async function parsedAndroidContexts({
|
|
5904
|
+
contexts,
|
|
5905
|
+
filterByCurrentAndroidApp,
|
|
5906
|
+
isAttachedAndVisible,
|
|
5907
|
+
packageName
|
|
5908
|
+
}) {
|
|
5909
|
+
const currentWebviewName = `WEBVIEW_${packageName}`;
|
|
5910
|
+
let parsedContexts = contexts;
|
|
5911
|
+
if (filterByCurrentAndroidApp) {
|
|
5912
|
+
parsedContexts = contexts.filter((context) => context.webviewName === currentWebviewName);
|
|
5913
|
+
}
|
|
5914
|
+
const result = [{ id: "NATIVE_APP" }];
|
|
5915
|
+
if (!parsedContexts || parsedContexts.length < 1) {
|
|
5916
|
+
return result;
|
|
5917
|
+
}
|
|
5918
|
+
parsedContexts.forEach(
|
|
5919
|
+
(context) => context.pages?.filter((page) => {
|
|
5920
|
+
if (packageName === CHROME_PACKAGE_NAME) {
|
|
5921
|
+
return true;
|
|
5922
|
+
}
|
|
5923
|
+
if (page.type === "page" && page.description) {
|
|
5924
|
+
let descriptionObj;
|
|
5925
|
+
try {
|
|
5926
|
+
descriptionObj = JSON.parse(page.description);
|
|
5927
|
+
} catch (e) {
|
|
5928
|
+
return false;
|
|
5929
|
+
}
|
|
5930
|
+
return isAttachedAndVisible ? descriptionObj.attached === true && descriptionObj.visible === true : true;
|
|
5931
|
+
}
|
|
5932
|
+
return !isAttachedAndVisible;
|
|
5933
|
+
}).forEach((page) => {
|
|
5934
|
+
const {
|
|
5935
|
+
attached = false,
|
|
5936
|
+
empty = false,
|
|
5937
|
+
height = 0,
|
|
5938
|
+
never_attached: neverAttached = false,
|
|
5939
|
+
screenX = 0,
|
|
5940
|
+
screenY = 0,
|
|
5941
|
+
visible = false,
|
|
5942
|
+
width = 0
|
|
5943
|
+
} = JSON.parse(page.description || "{}");
|
|
5944
|
+
const pageData = {
|
|
5945
|
+
androidWebviewData: {
|
|
5946
|
+
attached,
|
|
5947
|
+
empty,
|
|
5948
|
+
height,
|
|
5949
|
+
neverAttached,
|
|
5950
|
+
screenX,
|
|
5951
|
+
screenY,
|
|
5952
|
+
visible,
|
|
5953
|
+
width
|
|
5954
|
+
},
|
|
5955
|
+
id: context.webviewName,
|
|
5956
|
+
title: page.title,
|
|
5957
|
+
url: page.url,
|
|
5958
|
+
packageName: context.info["Android-Package"],
|
|
5959
|
+
webviewPageId: page.id
|
|
5960
|
+
};
|
|
5961
|
+
result.push(pageData);
|
|
5962
|
+
})
|
|
5963
|
+
);
|
|
5964
|
+
return result;
|
|
5965
|
+
}
|
|
5966
|
+
async function getCurrentContexts({
|
|
5967
|
+
browser,
|
|
5968
|
+
androidWebviewConnectionRetryTime,
|
|
5969
|
+
androidWebviewConnectTimeout,
|
|
5970
|
+
filterByCurrentAndroidApp,
|
|
5971
|
+
isAndroidWebviewVisible,
|
|
5972
|
+
returnAndroidDescriptionData
|
|
5973
|
+
}) {
|
|
5974
|
+
const contexts = await browser.execute("mobile: getContexts");
|
|
5975
|
+
if (browser.isIOS) {
|
|
5976
|
+
return contexts;
|
|
5977
|
+
}
|
|
5978
|
+
const packageName = await browser.getCurrentPackage();
|
|
5979
|
+
const startTime = Date.now();
|
|
5980
|
+
const retryInterval = androidWebviewConnectionRetryTime;
|
|
5981
|
+
let isPackageNameMissing = false;
|
|
5982
|
+
while (Date.now() - startTime < androidWebviewConnectTimeout) {
|
|
5983
|
+
const parsedContexts = await parsedAndroidContexts({
|
|
5984
|
+
contexts,
|
|
5985
|
+
filterByCurrentAndroidApp,
|
|
5986
|
+
isAttachedAndVisible: isAndroidWebviewVisible,
|
|
5987
|
+
packageName
|
|
5988
|
+
});
|
|
5989
|
+
const androidContext = parsedContexts.find((context) => context.packageName === packageName);
|
|
5990
|
+
isPackageNameMissing = !androidContext?.packageName;
|
|
5991
|
+
const isAndroidWebviewDataMissing = androidContext && !("androidWebviewData" in androidContext);
|
|
5992
|
+
const isAndroidWebviewDataEmpty = androidContext && androidContext.androidWebviewData?.empty;
|
|
5993
|
+
if (packageName === CHROME_PACKAGE_NAME) {
|
|
5994
|
+
return parsedContexts;
|
|
5995
|
+
}
|
|
5996
|
+
if (!isPackageNameMissing && !isAndroidWebviewDataMissing && !isAndroidWebviewDataEmpty) {
|
|
5997
|
+
if (!returnAndroidDescriptionData) {
|
|
5998
|
+
parsedContexts.forEach((context) => {
|
|
5999
|
+
if ("androidWebviewData" in context) {
|
|
6000
|
+
delete context.androidWebviewData;
|
|
6001
|
+
}
|
|
6002
|
+
});
|
|
6003
|
+
}
|
|
6004
|
+
return parsedContexts;
|
|
6005
|
+
}
|
|
6006
|
+
await new Promise((resolve2) => setTimeout(resolve2, retryInterval));
|
|
6007
|
+
}
|
|
6008
|
+
throw new Error(
|
|
6009
|
+
`The packageName '${packageName}' ${isPackageNameMissing ? "could not be found!" : "matches, but no webview with pages was loaded in this response: " + JSON.stringify(contexts) + "'"}`
|
|
6010
|
+
);
|
|
6011
|
+
}
|
|
6012
|
+
|
|
6013
|
+
// src/commands/mobile/switchContext.ts
|
|
6014
|
+
import logger21 from "@wdio/logger";
|
|
6015
|
+
var log21 = logger21("webdriver");
|
|
6016
|
+
async function switchContext(options) {
|
|
6017
|
+
const browser = this;
|
|
6018
|
+
if (!browser.isMobile) {
|
|
6019
|
+
throw new Error("The `switchContext` command is only available for mobile platforms.");
|
|
6020
|
+
}
|
|
6021
|
+
if (!options) {
|
|
6022
|
+
throw new Error("You need to provide at least a context name to switch to. See https://webdriver.io/docs/api/mobile/switchContext for more information.");
|
|
6023
|
+
}
|
|
6024
|
+
if (typeof options === "string") {
|
|
6025
|
+
log21.info("The standard Appium `context`-method is used. If you want to switch to a webview with a specific title or url, please provide an object with the `title` or `url` property. See https://webdriver.io/docs/api/mobile/switchContext for more information.");
|
|
6026
|
+
return browser.switchAppiumContext(options);
|
|
6027
|
+
}
|
|
6028
|
+
if (!options.title && !options.url) {
|
|
6029
|
+
throw new Error("You need to provide at least a `title` or `url` property to use full potential of the `switchContext` command. See https://webdriver.io/docs/api/mobile/switchContext for more information.");
|
|
6030
|
+
}
|
|
6031
|
+
return switchToContext({ browser, options });
|
|
6032
|
+
}
|
|
6033
|
+
async function switchToContext({ browser, options }) {
|
|
6034
|
+
const getContextsOptions = {
|
|
6035
|
+
returnDetailedContexts: true,
|
|
6036
|
+
filterByCurrentAndroidApp: false,
|
|
6037
|
+
isAndroidWebviewVisible: false,
|
|
6038
|
+
returnAndroidDescriptionData: true,
|
|
6039
|
+
...options?.androidWebviewConnectionRetryTime && { androidWebviewConnectionRetryTime: options.androidWebviewConnectionRetryTime },
|
|
6040
|
+
...options?.androidWebviewConnectTimeout && { androidWebviewConnectTimeout: options.androidWebviewConnectTimeout }
|
|
6041
|
+
};
|
|
6042
|
+
const contexts = await browser.getContexts(getContextsOptions);
|
|
6043
|
+
const identifier = browser.isIOS ? (await browser.execute("mobile: activeAppInfo"))?.bundleId : await browser.getCurrentPackage();
|
|
6044
|
+
const { matchingContext, reasons } = findMatchingContext({ browser, contexts, identifier, ...options?.title && { title: options.title }, ...options?.url && { url: options.url } });
|
|
6045
|
+
if (!matchingContext) {
|
|
6046
|
+
throw new Error(reasons.join("\n"));
|
|
6047
|
+
}
|
|
6048
|
+
log21.info("WebdriverIO found a matching context:", JSON.stringify(matchingContext, null, 2));
|
|
6049
|
+
if (!browser.isIOS) {
|
|
6050
|
+
const webviewName = `WEBVIEW_${identifier}`;
|
|
6051
|
+
await browser.switchAppiumContext(webviewName);
|
|
6052
|
+
}
|
|
6053
|
+
const switchFunction = browser.isIOS ? browser.switchAppiumContext.bind(browser) : browser.switchToWindow.bind(browser);
|
|
6054
|
+
const matchingContextId = browser.isIOS ? matchingContext.id : matchingContext.webviewPageId;
|
|
6055
|
+
return switchFunction(matchingContextId);
|
|
6056
|
+
}
|
|
6057
|
+
function findMatchingContext({
|
|
6058
|
+
browser: { isIOS },
|
|
6059
|
+
contexts,
|
|
6060
|
+
identifier,
|
|
6061
|
+
title,
|
|
6062
|
+
url: url2
|
|
6063
|
+
}) {
|
|
6064
|
+
const reasons = [];
|
|
6065
|
+
reasons.push(`We parsed a total of ${contexts.length} Webviews but did not find a matching context. The reasons are:`);
|
|
6066
|
+
const matchingContext = contexts.find((context, index) => {
|
|
6067
|
+
reasons.push(`- Webview ${index + 1}: '${context.id}'`);
|
|
6068
|
+
if (context.id === "NATIVE_APP") {
|
|
6069
|
+
reasons.push(" - Skipped context because it is NATIVE_APP");
|
|
6070
|
+
return false;
|
|
6071
|
+
}
|
|
6072
|
+
const idMatch = isIOS ? context.bundleId === identifier : context.packageName === identifier;
|
|
6073
|
+
const titleMatches = title ? title instanceof RegExp ? title.test(context.title || "") : context.title?.includes(title) : true;
|
|
6074
|
+
const urlMatches = url2 ? url2 instanceof RegExp ? url2.test(context.url || "") : context.url?.includes(url2) : true;
|
|
6075
|
+
const additionalAndroidChecks = isIOS ? true : context.androidWebviewData?.attached && context.androidWebviewData?.visible;
|
|
6076
|
+
if (!idMatch) {
|
|
6077
|
+
reasons.push(` - App ${isIOS ? "bundleId" : "packageName"} '${identifier}' did not match: '${context.id}'`);
|
|
6078
|
+
}
|
|
6079
|
+
if (!titleMatches) {
|
|
6080
|
+
reasons.push(` - Title '${title}' did not match: '${context.title}'`);
|
|
6081
|
+
}
|
|
6082
|
+
if (!urlMatches) {
|
|
6083
|
+
reasons.push(` - URL '${url2}' did not match: '${context.url}'`);
|
|
6084
|
+
}
|
|
6085
|
+
if (!additionalAndroidChecks) {
|
|
6086
|
+
reasons.push(" - Additional Android checks failed");
|
|
6087
|
+
}
|
|
6088
|
+
return idMatch && titleMatches && urlMatches && additionalAndroidChecks;
|
|
6089
|
+
});
|
|
6090
|
+
return { matchingContext, reasons };
|
|
6091
|
+
}
|
|
6092
|
+
|
|
5839
6093
|
// src/commands/element.ts
|
|
5840
6094
|
var element_exports = {};
|
|
5841
6095
|
__export(element_exports, {
|
|
@@ -5920,9 +6174,9 @@ function clearValue() {
|
|
|
5920
6174
|
}
|
|
5921
6175
|
|
|
5922
6176
|
// src/commands/element/click.ts
|
|
5923
|
-
import
|
|
6177
|
+
import logger22 from "@wdio/logger";
|
|
5924
6178
|
import { getBrowserObject as getBrowserObject12 } from "@wdio/utils";
|
|
5925
|
-
var
|
|
6179
|
+
var log22 = logger22("webdriver");
|
|
5926
6180
|
function click(options) {
|
|
5927
6181
|
if (typeof options !== "undefined") {
|
|
5928
6182
|
if (typeof options !== "object" || Array.isArray(options)) {
|
|
@@ -5969,10 +6223,10 @@ async function actionClick(element, options) {
|
|
|
5969
6223
|
if (x || y) {
|
|
5970
6224
|
const { width, height } = await browser.getElementRect(element.elementId);
|
|
5971
6225
|
if (x && x < -Math.floor(width / 2) || x && x > Math.floor(width / 2)) {
|
|
5972
|
-
|
|
6226
|
+
log22.warn("x would cause a out of bounds error as it goes outside of element");
|
|
5973
6227
|
}
|
|
5974
6228
|
if (y && y < -Math.floor(height / 2) || y && y > Math.floor(height / 2)) {
|
|
5975
|
-
|
|
6229
|
+
log22.warn("y would cause a out of bounds error as it goes outside of element");
|
|
5976
6230
|
}
|
|
5977
6231
|
}
|
|
5978
6232
|
const clickNested = async () => {
|
|
@@ -6347,7 +6601,7 @@ async function isClickable() {
|
|
|
6347
6601
|
if (!await this.isDisplayed()) {
|
|
6348
6602
|
return false;
|
|
6349
6603
|
}
|
|
6350
|
-
if (this.isMobile &&
|
|
6604
|
+
if (this.isMobile && this.isNativeContext) {
|
|
6351
6605
|
throw new Error("Method not supported in mobile native environment. It is unlikely that you need to use this command.");
|
|
6352
6606
|
}
|
|
6353
6607
|
const browser = getBrowserObject21(this);
|
|
@@ -6490,18 +6744,18 @@ async function isStable() {
|
|
|
6490
6744
|
}
|
|
6491
6745
|
|
|
6492
6746
|
// src/commands/element/moveTo.ts
|
|
6493
|
-
import
|
|
6747
|
+
import logger23 from "@wdio/logger";
|
|
6494
6748
|
import { getBrowserObject as getBrowserObject26 } from "@wdio/utils";
|
|
6495
|
-
var
|
|
6749
|
+
var log23 = logger23("webdriver");
|
|
6496
6750
|
async function moveTo({ xOffset, yOffset } = {}) {
|
|
6497
6751
|
const browser = getBrowserObject26(this);
|
|
6498
6752
|
if (xOffset || yOffset) {
|
|
6499
6753
|
const { width, height } = await browser.getElementRect(this.elementId);
|
|
6500
6754
|
if (xOffset && xOffset < -Math.floor(width / 2) || xOffset && xOffset > Math.floor(width / 2)) {
|
|
6501
|
-
|
|
6755
|
+
log23.warn("xOffset would cause a out of bounds error as it goes outside of element");
|
|
6502
6756
|
}
|
|
6503
6757
|
if (yOffset && yOffset < -Math.floor(height / 2) || yOffset && yOffset > Math.floor(height / 2)) {
|
|
6504
|
-
|
|
6758
|
+
log23.warn("yOffset would cause a out of bounds error as it goes outside of element");
|
|
6505
6759
|
}
|
|
6506
6760
|
}
|
|
6507
6761
|
const moveToNested = async () => {
|
|
@@ -6587,10 +6841,10 @@ async function saveScreenshot3(filepath) {
|
|
|
6587
6841
|
}
|
|
6588
6842
|
|
|
6589
6843
|
// src/commands/element/scrollIntoView.ts
|
|
6590
|
-
import
|
|
6844
|
+
import logger24 from "@wdio/logger";
|
|
6591
6845
|
import { ELEMENT_KEY as ELEMENT_KEY17 } from "webdriver";
|
|
6592
6846
|
import { getBrowserObject as getBrowserObject29 } from "@wdio/utils";
|
|
6593
|
-
var
|
|
6847
|
+
var log24 = logger24("webdriverio");
|
|
6594
6848
|
async function scrollIntoView(options = { block: "start", inline: "nearest" }) {
|
|
6595
6849
|
const browser = getBrowserObject29(this);
|
|
6596
6850
|
if (browser.isMobile) {
|
|
@@ -6643,7 +6897,7 @@ async function scrollIntoView(options = { block: "start", inline: "nearest" }) {
|
|
|
6643
6897
|
deltaY = Math.round(deltaY - scrollY);
|
|
6644
6898
|
await browser.action("wheel").scroll({ duration: 0, x: deltaX, y: deltaY, origin: this }).perform();
|
|
6645
6899
|
} catch (err) {
|
|
6646
|
-
|
|
6900
|
+
log24.warn(
|
|
6647
6901
|
`Failed to execute "scrollIntoView" using WebDriver Actions API: ${err.message}!
|
|
6648
6902
|
Re-attempting using \`Element.scrollIntoView\` via Web API.`
|
|
6649
6903
|
);
|
|
@@ -6789,7 +7043,7 @@ async function setValue(value) {
|
|
|
6789
7043
|
}
|
|
6790
7044
|
|
|
6791
7045
|
// src/commands/element/shadow$$.ts
|
|
6792
|
-
import
|
|
7046
|
+
import logger25 from "@wdio/logger";
|
|
6793
7047
|
import { getBrowserObject as getBrowserObject30 } from "@wdio/utils";
|
|
6794
7048
|
import { SHADOW_ELEMENT_KEY } from "webdriver";
|
|
6795
7049
|
import { shadowFnFactory } from "./scripts/shadowFnFactory.js";
|
|
@@ -7116,7 +7370,7 @@ var createRoleBaseXpathSelector = (role) => {
|
|
|
7116
7370
|
};
|
|
7117
7371
|
|
|
7118
7372
|
// src/commands/element/shadow$$.ts
|
|
7119
|
-
var
|
|
7373
|
+
var log25 = logger25("webdriverio");
|
|
7120
7374
|
async function shadow$$(selector) {
|
|
7121
7375
|
const browser = getBrowserObject30(this);
|
|
7122
7376
|
try {
|
|
@@ -7126,7 +7380,7 @@ async function shadow$$(selector) {
|
|
|
7126
7380
|
const elements = await getElements.call(this, selector, res, { isShadowElement: true });
|
|
7127
7381
|
return enhanceElementsArray(elements, this, selector);
|
|
7128
7382
|
} catch (err) {
|
|
7129
|
-
|
|
7383
|
+
log25.warn(
|
|
7130
7384
|
`Failed to fetch element within shadow DOM using WebDriver command: ${err.message}!
|
|
7131
7385
|
Falling back to JavaScript shim.`
|
|
7132
7386
|
);
|
|
@@ -7135,11 +7389,11 @@ Falling back to JavaScript shim.`
|
|
|
7135
7389
|
}
|
|
7136
7390
|
|
|
7137
7391
|
// src/commands/element/shadow$.ts
|
|
7138
|
-
import
|
|
7392
|
+
import logger26 from "@wdio/logger";
|
|
7139
7393
|
import { SHADOW_ELEMENT_KEY as SHADOW_ELEMENT_KEY2 } from "webdriver";
|
|
7140
7394
|
import { shadowFnFactory as shadowFnFactory2 } from "./scripts/shadowFnFactory.js";
|
|
7141
7395
|
import { getBrowserObject as getBrowserObject31 } from "@wdio/utils";
|
|
7142
|
-
var
|
|
7396
|
+
var log26 = logger26("webdriverio");
|
|
7143
7397
|
async function shadow$(selector) {
|
|
7144
7398
|
const browser = getBrowserObject31(this);
|
|
7145
7399
|
try {
|
|
@@ -7148,7 +7402,7 @@ async function shadow$(selector) {
|
|
|
7148
7402
|
const res = await browser.findElementFromShadowRoot(shadowRoot[SHADOW_ELEMENT_KEY2], using, value);
|
|
7149
7403
|
return getElement.call(this, selector, res, { isShadowElement: true });
|
|
7150
7404
|
} catch (err) {
|
|
7151
|
-
|
|
7405
|
+
log26.warn(
|
|
7152
7406
|
`Failed to fetch element within shadow DOM using WebDriver command: ${err.message}!
|
|
7153
7407
|
Falling back to JavaScript shim.`
|
|
7154
7408
|
);
|
|
@@ -7508,7 +7762,7 @@ function querySelectorAllDeep(findMany, s, r) {
|
|
|
7508
7762
|
}
|
|
7509
7763
|
|
|
7510
7764
|
// src/utils/index.ts
|
|
7511
|
-
var
|
|
7765
|
+
var log27 = logger27("webdriverio");
|
|
7512
7766
|
var INVALID_SELECTOR_ERROR = "selector needs to be typeof `string` or `function`";
|
|
7513
7767
|
var IGNORED_COMMAND_FILE_EXPORTS = ["SESSION_MOCKS", "CDP_SESSIONS"];
|
|
7514
7768
|
var scopes = {
|
|
@@ -7676,7 +7930,7 @@ async function findDeepElement(selector) {
|
|
|
7676
7930
|
})).then((elems) => elems.filter(([isIn]) => isIn).map(([, elem]) => elem));
|
|
7677
7931
|
return scopedNodes[0];
|
|
7678
7932
|
}, (err) => {
|
|
7679
|
-
|
|
7933
|
+
log27.warn(`Failed to execute browser.browsingContextLocateNodes({ ... }) due to ${err}, falling back to regular WebDriver Classic command`);
|
|
7680
7934
|
return this && "elementId" in this && this.elementId ? this.findElementFromElement(this.elementId, using, value) : browser.findElement(using, value);
|
|
7681
7935
|
});
|
|
7682
7936
|
if (!deepElementResult) {
|
|
@@ -7714,7 +7968,7 @@ async function findDeepElements(selector) {
|
|
|
7714
7968
|
})).then((elems) => elems.filter(([isIn]) => isIn).map(([, elem]) => elem));
|
|
7715
7969
|
return scopedNodes;
|
|
7716
7970
|
}, (err) => {
|
|
7717
|
-
|
|
7971
|
+
log27.warn(`Failed to execute browser.browsingContextLocateNodes({ ... }) due to ${err}, falling back to regular WebDriver Classic command`);
|
|
7718
7972
|
return this && "elementId" in this && this.elementId ? this.findElementsFromElement(this.elementId, using, value) : browser.findElements(using, value);
|
|
7719
7973
|
});
|
|
7720
7974
|
return deepElementResult;
|
|
@@ -7842,7 +8096,7 @@ async function getElementRect(scope) {
|
|
|
7842
8096
|
if (rectJs && typeof rectJs[key] === "number") {
|
|
7843
8097
|
rect[key] = Math.floor(rectJs[key]);
|
|
7844
8098
|
} else {
|
|
7845
|
-
|
|
8099
|
+
log27.error("getElementRect", { rect, rectJs, key });
|
|
7846
8100
|
throw new Error("Failed to receive element rects via execute command");
|
|
7847
8101
|
}
|
|
7848
8102
|
});
|
|
@@ -8286,7 +8540,7 @@ var remote = async function(params, remoteModifier) {
|
|
|
8286
8540
|
const keysToKeep = Object.keys(process.env.WDIO_WORKER_ID ? params : DEFAULTS);
|
|
8287
8541
|
const config = validateConfig(WDIO_DEFAULTS, params, keysToKeep);
|
|
8288
8542
|
await enableFileLogging(config.outputDir);
|
|
8289
|
-
|
|
8543
|
+
logger28.setLogLevelsConfig(config.logLevels, config.logLevel);
|
|
8290
8544
|
const modifier = (client, options2) => {
|
|
8291
8545
|
Object.assign(options2, Object.entries(config).reduce((a, [k, v]) => typeof v === "undefined" ? a : { ...a, [k]: v }, {}));
|
|
8292
8546
|
if (typeof remoteModifier === "function") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/session/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAGtC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAM7C,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,kBAE7D;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,UAAU,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAElG;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,cAAc;;gBAMlC,OAAO,EAAE,WAAW,CAAC,OAAO;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/session/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAGtC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAM7C,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,kBAE7D;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,UAAU,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAElG;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,cAAc;;gBAMlC,OAAO,EAAE,WAAW,CAAC,OAAO;IAsCxC,eAAe,IAAI,IAAI;IAiFvB;;OAEG;IACG,UAAU;IAoChB,iBAAiB,CAAE,OAAO,EAAE,MAAM;IAQ5B,iBAAiB;IAOvB,IAAI,eAAe,YAElB;IAED,IAAI,aAAa,uBAEhB;IAED;;;OAGG;IACG,kBAAkB,IAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAqBrE;;;;;OAKG;IACH,iBAAiB,CAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,uBAAuB,GAAG,KAAK,CAAC,mBAAmB,GAAG,SAAS;IAiBrH;;;;;;OAMG;IACH,WAAW,CACP,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,KAAK,CAAC,uBAAuB,GAAG,IAAI,EAC9C,WAAW,EAAE,OAAO,GAAG,iBAAiB,GAAG,aAAa,GACzD,KAAK,CAAC,mBAAmB,GAAG,SAAS;CAqB3C"}
|