@testim/testim-cli 3.289.0 → 3.290.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/cli.js +22390 -122
- package/cli.js.map +1 -0
- package/npm-shrinkwrap.json +1951 -203
- package/package.json +9 -5
- package/OverrideTestDataBuilder.js +0 -117
- package/agent/routers/cliJsCode/index.js +0 -13
- package/agent/routers/cliJsCode/router.js +0 -63
- package/agent/routers/cliJsCode/service.js +0 -705
- package/agent/routers/codim/router.js +0 -69
- package/agent/routers/codim/router.test.js +0 -60
- package/agent/routers/codim/service.js +0 -193
- package/agent/routers/general/index.js +0 -36
- package/agent/routers/hybrid/registerRoutes.js +0 -81
- package/agent/routers/index.js +0 -56
- package/agent/routers/playground/router.js +0 -77
- package/agent/routers/playground/service.js +0 -96
- package/agent/routers/standalone-browser/registerRoutes.js +0 -47
- package/agent/server.js +0 -150
- package/cdpTestRunner.js +0 -86
- package/chromiumInstaller.js +0 -91
- package/cli/isCiRun.js +0 -10
- package/cli/onExit.js +0 -65
- package/cli/writeStackTrace.js +0 -27
- package/cliAgentMode.js +0 -384
- package/codim/codim-cli.js +0 -91
- package/codim/codim-npm-package/index.ts +0 -427
- package/codim/codim-npm-package/package-lock.json +0 -14
- package/codim/codim-npm-package/package.json +0 -14
- package/codim/hybrid-utils.js +0 -28
- package/codim/measure-perf.js +0 -41
- package/codim/template.js/.idea/workspace.xml +0 -57
- package/codim/template.js/.vscode/launch.json +0 -53
- package/codim/template.ts/.idea/workspace.xml +0 -57
- package/codim/template.ts/.vscode/launch.json +0 -55
- package/commons/AbortError.js +0 -12
- package/commons/SeleniumPerfStats.js +0 -58
- package/commons/chrome-launcher.js +0 -15
- package/commons/chromedriverWrapper.js +0 -70
- package/commons/config.js +0 -39
- package/commons/constants.js +0 -67
- package/commons/detectDebugger.js +0 -19
- package/commons/featureAvailabilityService.js +0 -26
- package/commons/featureFlags.js +0 -132
- package/commons/getSessionPlayerRequire.js +0 -28
- package/commons/httpRequest.js +0 -261
- package/commons/httpRequestCounters.js +0 -98
- package/commons/httpRequestCounters.test.js +0 -38
- package/commons/initializeUserWithAuth.js +0 -55
- package/commons/lazyRequire.js +0 -105
- package/commons/logUtils.js +0 -15
- package/commons/logUtils.test.js +0 -21
- package/commons/logger.js +0 -178
- package/commons/mockNetworkRuleFileSchema.json +0 -140
- package/commons/npmWrapper.js +0 -174
- package/commons/npmWrapper.test.js +0 -374
- package/commons/performance-logger.js +0 -71
- package/commons/preloadTests.js +0 -29
- package/commons/prepareRunner.js +0 -85
- package/commons/prepareRunner.test.js +0 -144
- package/commons/prepareRunnerAndTestimStartUtils.js +0 -198
- package/commons/prepareRunnerAndTestimStartUtils.test.js +0 -73
- package/commons/requireWithFallback.js +0 -25
- package/commons/runnerFileCache.js +0 -204
- package/commons/socket/baseSocketServiceSocketIO.js +0 -197
- package/commons/socket/realDataService.js +0 -59
- package/commons/socket/realDataServiceSocketIO.js +0 -33
- package/commons/socket/remoteStepService.js +0 -55
- package/commons/socket/remoteStepServiceSocketIO.js +0 -61
- package/commons/socket/socketService.js +0 -175
- package/commons/socket/testResultService.js +0 -62
- package/commons/socket/testResultServiceSocketIO.js +0 -64
- package/commons/testimAnalytics.js +0 -40
- package/commons/testimCloudflare.js +0 -83
- package/commons/testimCloudflare.test.js +0 -185
- package/commons/testimCustomToken.js +0 -124
- package/commons/testimDesiredCapabilitiesBuilder.js +0 -647
- package/commons/testimNgrok.js +0 -90
- package/commons/testimNgrok.test.js +0 -140
- package/commons/testimServicesApi.js +0 -631
- package/commons/testimTunnel.js +0 -73
- package/commons/testimTunnel.test.js +0 -172
- package/commons/xhr2.js +0 -897
- package/coverage/SummaryToObjectReport.js +0 -19
- package/coverage/jsCoverage.js +0 -252
- package/credentialsManager.js +0 -142
- package/errors.js +0 -161
- package/executionQueue.js +0 -37
- package/fixLocalBuild.js +0 -24
- package/inputFileUtils.js +0 -103
- package/lib/coralogix-winston.transport.js +0 -99
- package/player/SeleniumProtocolError.js +0 -100
- package/player/WebDriverHttpRequest.js +0 -177
- package/player/WebdriverioWebDriverApi.js +0 -671
- package/player/appiumTestPlayer.js +0 -90
- package/player/chromeLauncherTestPlayer.js +0 -67
- package/player/constants.js +0 -332
- package/player/extensionTestPlayer.js +0 -32
- package/player/findElementStrategy.js +0 -154
- package/player/scripts/isElementDisplayed.js +0 -252
- package/player/seleniumTestPlayer.js +0 -140
- package/player/services/frameLocator.js +0 -170
- package/player/services/mobileFrameLocatorMock.js +0 -32
- package/player/services/playbackTimeoutCalculator.js +0 -175
- package/player/services/portSelector.js +0 -19
- package/player/services/tabService.js +0 -551
- package/player/services/tabServiceMock.js +0 -167
- package/player/services/windowCreationListener.js +0 -8
- package/player/stepActions/RefreshStepAction.js +0 -16
- package/player/stepActions/apiStepAction.js +0 -89
- package/player/stepActions/baseCliJsStepAction.js +0 -51
- package/player/stepActions/baseJsStepAction.js +0 -277
- package/player/stepActions/cliConditionStepAction.js +0 -11
- package/player/stepActions/cliJsStepAction.js +0 -11
- package/player/stepActions/dropFileStepAction.js +0 -34
- package/player/stepActions/evaluateExpressionStepAction.js +0 -52
- package/player/stepActions/extensionOnlyStepAction.js +0 -12
- package/player/stepActions/extractTextStepAction.js +0 -19
- package/player/stepActions/hoverStepAction.js +0 -55
- package/player/stepActions/inputFileStepAction.js +0 -199
- package/player/stepActions/jsCodeStepAction.js +0 -11
- package/player/stepActions/jsConditionStepAction.js +0 -11
- package/player/stepActions/locateStepAction.js +0 -159
- package/player/stepActions/mouseStepAction.js +0 -370
- package/player/stepActions/navigationStepAction.js +0 -29
- package/player/stepActions/nodePackageStepAction.js +0 -47
- package/player/stepActions/pixelValidationStepAction.js +0 -39
- package/player/stepActions/scripts/dispatchEvents.js +0 -282
- package/player/stepActions/scripts/doClick.js +0 -221
- package/player/stepActions/scripts/doDragPath.js +0 -225
- package/player/stepActions/scripts/doubleClick.js +0 -119
- package/player/stepActions/scripts/dropEvent.js +0 -63
- package/player/stepActions/scripts/focusElement.js +0 -46
- package/player/stepActions/scripts/html5dragAction.js +0 -56
- package/player/stepActions/scripts/html5dragActionV2.js +0 -312
- package/player/stepActions/scripts/runCode.js +0 -147
- package/player/stepActions/scripts/scroll.js +0 -90
- package/player/stepActions/scripts/selectOption.js +0 -51
- package/player/stepActions/scripts/setText.js +0 -415
- package/player/stepActions/scripts/wheel.js +0 -61
- package/player/stepActions/scrollStepAction.js +0 -96
- package/player/stepActions/selectOptionStepAction.js +0 -49
- package/player/stepActions/sfdcRecordedStepAction.js +0 -24
- package/player/stepActions/sfdcStepAction.js +0 -28
- package/player/stepActions/sleepStepAction.js +0 -12
- package/player/stepActions/specialKeyStepAction.js +0 -52
- package/player/stepActions/stepAction.js +0 -73
- package/player/stepActions/stepActionRegistrar.js +0 -111
- package/player/stepActions/submitStepAction.js +0 -12
- package/player/stepActions/tdkHybridStepAction.js +0 -18
- package/player/stepActions/textStepAction.js +0 -110
- package/player/stepActions/textValidationStepAction.js +0 -64
- package/player/stepActions/wheelStepAction.js +0 -41
- package/player/utils/cookieUtils.js +0 -39
- package/player/utils/eyeSdkService.js +0 -250
- package/player/utils/imageCaptureUtils.js +0 -267
- package/player/utils/screenshotUtils.js +0 -68
- package/player/utils/stepActionUtils.js +0 -90
- package/player/utils/windowUtils.js +0 -195
- package/player/webDriverUtils.js +0 -40
- package/player/webDriverUtils.test.js +0 -116
- package/player/webdriver.js +0 -976
- package/polyfills/Array.prototype.at.js +0 -13
- package/polyfills/index.js +0 -13
- package/processHandler.js +0 -79
- package/processHandler.test.js +0 -55
- package/reports/chromeReporter.js +0 -17
- package/reports/consoleReporter.js +0 -190
- package/reports/debugReporter.js +0 -82
- package/reports/jsonReporter.js +0 -55
- package/reports/junitReporter.js +0 -183
- package/reports/reporter.js +0 -166
- package/reports/reporterUtils.js +0 -54
- package/reports/teamCityReporter.js +0 -73
- package/runOptions.d.ts +0 -305
- package/runOptions.js +0 -1288
- package/runOptionsAgentFlow.js +0 -87
- package/runOptionsUtils.js +0 -60
- package/runner.js +0 -355
- package/runners/ParallelWorkerManager.js +0 -284
- package/runners/TestPlanRunner.js +0 -419
- package/runners/buildCodeTests.js +0 -159
- package/runners/runnerUtils.js +0 -81
- package/services/analyticsService.js +0 -96
- package/services/branchService.js +0 -29
- package/services/gridService.js +0 -357
- package/services/gridService.test.js +0 -357
- package/services/labFeaturesService.js +0 -64
- package/services/lambdatestService.js +0 -227
- package/services/lambdatestService.test.js +0 -353
- package/services/localRCASaver.js +0 -124
- package/stepPlayers/cliJsStepPlayback.js +0 -40
- package/stepPlayers/hybridStepPlayback.js +0 -140
- package/stepPlayers/nodePackageStepPlayback.js +0 -28
- package/stepPlayers/playwrightHybridStepPlayback.js +0 -61
- package/stepPlayers/puppeteerHybridStepPlayback.js +0 -76
- package/stepPlayers/remoteStepPlayback.js +0 -80
- package/stepPlayers/seleniumHybridStepPlayback.js +0 -84
- package/stepPlayers/tdkHybridStepPlayback.js +0 -112
- package/testRunHandler.js +0 -603
- package/testRunStatus.js +0 -567
- package/testimNpmDriver.js +0 -52
- package/utils/argsUtils.js +0 -91
- package/utils/argsUtils.test.js +0 -32
- package/utils/fsUtils.js +0 -174
- package/utils/index.js +0 -197
- package/utils/promiseUtils.js +0 -85
- package/utils/stringUtils.js +0 -98
- package/utils/stringUtils.test.js +0 -22
- package/utils/timeUtils.js +0 -25
- package/utils/utils.test.js +0 -27
- package/workers/BaseWorker.js +0 -498
- package/workers/BaseWorker.test.js +0 -186
- package/workers/WorkerAppium.js +0 -180
- package/workers/WorkerExtension.js +0 -192
- package/workers/WorkerExtensionSingleBrowser.js +0 -77
- package/workers/WorkerSelenium.js +0 -253
- package/workers/workerUtils.js +0 -20
|
@@ -1,551 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const semver = require('semver');
|
|
7
|
-
const utils = require('../../utils');
|
|
8
|
-
const WindowUtils = require('../utils/windowUtils');
|
|
9
|
-
const ScreenshotUtils = require('../utils/screenshotUtils');
|
|
10
|
-
const ImageCaptureUtils = require('../utils/imageCaptureUtils');
|
|
11
|
-
const { getLogger } = require('../../commons/logger');
|
|
12
|
-
const { getSessionPlayer } = require('../../commons/getSessionPlayerRequire');
|
|
13
|
-
|
|
14
|
-
const logger = getLogger('tab-service');
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @typedef {{
|
|
18
|
-
* attachDebugger(): Promise<any>,
|
|
19
|
-
* detachDebugger(): Promise<any>,
|
|
20
|
-
* onDebuggerDetached(): {},
|
|
21
|
-
* tabId: string,
|
|
22
|
-
* domUtils: { getDOM(): Promise<any> },
|
|
23
|
-
* windowUtils: WindowUtils,
|
|
24
|
-
* imageCaptureUtils: ImageCaptureUtils
|
|
25
|
-
* }} TabUtil
|
|
26
|
-
* */
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {{
|
|
29
|
-
* infoId: any;
|
|
30
|
-
* url: any;
|
|
31
|
-
* title: any;
|
|
32
|
-
* favIconUrl: any;
|
|
33
|
-
* order: any;
|
|
34
|
-
* from: any;
|
|
35
|
-
* isMain: any;
|
|
36
|
-
* openerStepId: any;
|
|
37
|
-
* openerOriginalStepId?: any;
|
|
38
|
-
* isClosed?: boolean;
|
|
39
|
-
* currentUrl?: any;
|
|
40
|
-
* lastUpdatedUrl?: any;
|
|
41
|
-
* }} TabInfo
|
|
42
|
-
*/
|
|
43
|
-
/**
|
|
44
|
-
* @typedef {{
|
|
45
|
-
* tabCount: number;
|
|
46
|
-
* tabInfos: Record<string, TabInfo>;
|
|
47
|
-
* lastActiveTabInfo?: any;
|
|
48
|
-
* currentTab?: any;
|
|
49
|
-
* }} SessionTab
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
class TabService {
|
|
53
|
-
/** @param {import('../webdriver')} driver */
|
|
54
|
-
constructor(driver) {
|
|
55
|
-
this.driver = driver;
|
|
56
|
-
/** @type {Record<string, TabUtil>} */
|
|
57
|
-
this._utils = {};
|
|
58
|
-
/** @type {Record<string, SessionTab>} */
|
|
59
|
-
this.sessionTabs = {};
|
|
60
|
-
/** @type {Record<string, string>} */
|
|
61
|
-
this.pendingTabs = {};
|
|
62
|
-
/** @type {Record<string, Set<any>>} */
|
|
63
|
-
this.addedTabs = {};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
on() {}
|
|
67
|
-
|
|
68
|
-
tabCount(sessionId) {
|
|
69
|
-
if (this.sessionTabs[sessionId]) {
|
|
70
|
-
return this.sessionTabs[sessionId].tabCount;
|
|
71
|
-
}
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
getAllOpenTabIds(sessionId) {
|
|
76
|
-
const allTabInfos = this.getAllTabInfos(sessionId);
|
|
77
|
-
return Object.keys(allTabInfos)
|
|
78
|
-
.filter(tabId => !allTabInfos[tabId].isClosed);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Get last tab info for pixel validation
|
|
83
|
-
*
|
|
84
|
-
* @returns last tab info
|
|
85
|
-
*/
|
|
86
|
-
getActiveTabInfo(sessionId) {
|
|
87
|
-
return this.sessionTabs[sessionId].lastActiveTabInfo;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
getAllTabIds(sessionId) {
|
|
91
|
-
return Object.keys(this.getAllTabInfos(sessionId)).map(id => id);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
isSessionTab(sessionId, tabId) {
|
|
95
|
-
return this.getAllTabIds(sessionId).includes(tabId);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
createSesion(sessionId) {
|
|
99
|
-
if (this.sessionTabs[sessionId]) { // joining an existing session
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
this.addedTabs[sessionId] = new Set();
|
|
103
|
-
this.sessionTabs[sessionId] = {
|
|
104
|
-
tabCount: 0,
|
|
105
|
-
tabInfos: {},
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
setAddFrameHandlerCallBack(addFrameHandlerCb) {
|
|
110
|
-
this.addFrameHandler = addFrameHandlerCb;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
getAllTabInfoStrings(sessionId) {
|
|
114
|
-
const allIds = this.getAllTabIds(sessionId);
|
|
115
|
-
return allIds.map(tabId => {
|
|
116
|
-
const tabInfo = this.getTabInfo(sessionId, tabId);
|
|
117
|
-
return `tabId=${tabId}, url=${tabInfo.url}, order=${tabInfo.order}, isMain=${tabInfo.isMain}, openerStepId=${tabInfo.openerStepId}, isClosed=${tabInfo.isClosed}, currentUrl: ${tabInfo.currentUrl}, lastUpdatedUrl: ${tabInfo.lastUpdatedUrl}`;
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
getAllTabInfos(sessionId) {
|
|
122
|
-
return this.sessionTabs[sessionId].tabInfos;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
addNewTab(sessionId, tabId, openerStepId, options = {}) {
|
|
126
|
-
if (this.addedTabs[sessionId].has(tabId)) {
|
|
127
|
-
return Promise.resolve();
|
|
128
|
-
}
|
|
129
|
-
this.addedTabs[sessionId].add(tabId);
|
|
130
|
-
logger.info(`Adding a new tab sessionId: ${sessionId}, tabId: ${tabId}, openerId: ${openerStepId}`);
|
|
131
|
-
return this.addTab(sessionId, tabId, this.sessionTabs[sessionId].tabCount++, openerStepId, options);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
addOpenerStepId(sessionId, tabId, openerStepId) {
|
|
135
|
-
this.sessionTabs[sessionId].tabInfos[tabId].openerStepId = openerStepId;
|
|
136
|
-
}
|
|
137
|
-
addOpenerStep(sessionId, tabId, openerStep) {
|
|
138
|
-
this.sessionTabs[sessionId].tabInfos[tabId].openerStepId = openerStep.id;
|
|
139
|
-
this.sessionTabs[sessionId].tabInfos[tabId].openerOriginalStepId = openerStep.originalStepId;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
fixMissingMainTab(sessionId) {
|
|
143
|
-
const mainTab = this.getMainTabInfo(sessionId);
|
|
144
|
-
if (mainTab) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const allTabInfos = this.getAllTabInfos(sessionId);
|
|
148
|
-
if (Object.keys(allTabInfos).length === 0) {
|
|
149
|
-
// no tab infos 🤷
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
Object.values(this.getAllTabInfos(sessionId))[0].isMain = true;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async buildTabInfo(sessionId, tabId, order, openerStepId, options = {}) {
|
|
156
|
-
/** @type {{ title: any; url: any; isMainTab: string; favIconUrl?: any, openerTabId?: any }} */
|
|
157
|
-
const tab = await this.getTabDetails(tabId, sessionId, options);
|
|
158
|
-
const infoId = utils.guid();
|
|
159
|
-
function isMainTab(tabService) {
|
|
160
|
-
if (options.checkForMainTab) {
|
|
161
|
-
return tab.isMainTab;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (!tab.isMainTab || tab.isMainTab === 'unknown') {
|
|
165
|
-
const missingMainTab = !tabService.getMainTabInfo(sessionId);
|
|
166
|
-
return missingMainTab;
|
|
167
|
-
}
|
|
168
|
-
return tab.isMainTab;
|
|
169
|
-
}
|
|
170
|
-
this.sessionTabs[sessionId].tabInfos[tabId] = {
|
|
171
|
-
infoId,
|
|
172
|
-
url: tab.url,
|
|
173
|
-
title: tab.title,
|
|
174
|
-
favIconUrl: tab.favIconUrl,
|
|
175
|
-
order,
|
|
176
|
-
from: this.getTabInfo(sessionId, tab.openerTabId),
|
|
177
|
-
isMain: isMainTab(this),
|
|
178
|
-
openerStepId,
|
|
179
|
-
};
|
|
180
|
-
return infoId;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async addTab(sessionId, id, order, openerStepId, options = {}) {
|
|
184
|
-
const infoId = await this.buildTabInfo(sessionId, id, order, openerStepId, options);
|
|
185
|
-
const _windowUtils = new WindowUtils(id, this.driver);
|
|
186
|
-
this._utils[infoId] = {
|
|
187
|
-
attachDebugger: () => Promise.resolve(),
|
|
188
|
-
detachDebugger: () => Promise.resolve(),
|
|
189
|
-
onDebuggerDetached: () => {},
|
|
190
|
-
tabId: id,
|
|
191
|
-
domUtils: { getDOM: () => Promise.resolve() },
|
|
192
|
-
windowUtils: _windowUtils,
|
|
193
|
-
imageCaptureUtils: new ImageCaptureUtils(
|
|
194
|
-
id,
|
|
195
|
-
_windowUtils,
|
|
196
|
-
new ScreenshotUtils(id, this.driver, { takeScreenshots: options.takeScreenshots })
|
|
197
|
-
),
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
getTabUtilsByTabIdAndSessionId(sessionId, tabId) {
|
|
202
|
-
const tabInfo = this.sessionTabs[sessionId].tabInfos[tabId];
|
|
203
|
-
return this._utils[tabInfo.infoId];
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
getTabUtilsByTabId(tabId) {
|
|
207
|
-
const infoId = Object.keys(this._utils).find(uId => this._utils[uId].tabId === tabId);
|
|
208
|
-
return this._utils[infoId];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
getTabInfo(sessionId, id) {
|
|
212
|
-
return this.sessionTabs[sessionId].tabInfos[id];
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
getTabUtils(sessionId, tabInfo) {
|
|
216
|
-
if (!tabInfo) {
|
|
217
|
-
return this.getMainTabUtils(sessionId);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (this._utils[tabInfo.infoId]) {
|
|
221
|
-
return this._utils[tabInfo.infoId];
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (tabInfo.isMain) {
|
|
225
|
-
return this.getMainTabUtils(sessionId);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const infos = this.getAllTabInfos(sessionId);
|
|
229
|
-
const nonMainTabs = Object.keys(infos)
|
|
230
|
-
.map(tabId => infos[tabId])
|
|
231
|
-
.filter(info => !info.isMain);
|
|
232
|
-
if (nonMainTabs.length === 1) {
|
|
233
|
-
return this._utils[nonMainTabs[0].infoId];
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const sameTabs = Object.keys(sessionId)
|
|
237
|
-
.map(key => sessionId[key])
|
|
238
|
-
.filter(info => this.isSameTab(sessionId, tabInfo, info));
|
|
239
|
-
if (sameTabs.length > 0) {
|
|
240
|
-
return this._utils[sameTabs[0].infoId];
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// if nothing else
|
|
244
|
-
return this.getMainTabUtils(sessionId);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
exactUrlMatch(first, second, allUrls) {
|
|
248
|
-
const exactUrlMatch = allUrls
|
|
249
|
-
.filter(url => url === second.url);
|
|
250
|
-
|
|
251
|
-
if ((first.url === second.url || first.currentUrl === second.url || (first.currentUrl && (first.currentUrl === second.currentUrl))) && (exactUrlMatch.length === 1)) {
|
|
252
|
-
return true;
|
|
253
|
-
}
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
singleExactMatchForParts(first, second, allUrls, combinePartsFunction) {
|
|
258
|
-
const { urlUtils } = getSessionPlayer();
|
|
259
|
-
const firstUrlParts = urlUtils.urlBreaker(first.url || first.currentUrl);
|
|
260
|
-
const secondUrlParts = urlUtils.urlBreaker(second.url || second.currentUrl);
|
|
261
|
-
const firstPartsCombined = combinePartsFunction(firstUrlParts);
|
|
262
|
-
const secondPartsCombined = combinePartsFunction(secondUrlParts);
|
|
263
|
-
const allDomainAndPathMatches = allUrls.map(url => urlUtils.urlBreaker(url))
|
|
264
|
-
.map(urlParts => combinePartsFunction(urlParts))
|
|
265
|
-
.filter(combinedParts => combinedParts === firstPartsCombined);
|
|
266
|
-
if ((firstPartsCombined === secondPartsCombined) && (allDomainAndPathMatches.length === 1)) {
|
|
267
|
-
return true;
|
|
268
|
-
}
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
isSameTab(sessionId, first, second) {
|
|
273
|
-
const { tabMatcher } = getSessionPlayer();
|
|
274
|
-
if (tabMatcher) {
|
|
275
|
-
const infos = this.getAllTabInfos(sessionId);
|
|
276
|
-
const allTabInfos = Object.keys(infos).map(tabId => infos[tabId]);
|
|
277
|
-
return tabMatcher.isSameTab(allTabInfos, first, second);
|
|
278
|
-
}
|
|
279
|
-
// flow:
|
|
280
|
-
// both main => exact match => exact match for domain and path => exact match for domain path and hash
|
|
281
|
-
// => order (legacy data)
|
|
282
|
-
if (first.isMain && second.isMain) {
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (first.openerStepId && second.openerStepId && first.openerStepId === second.openerStepId) {
|
|
287
|
-
return true;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const infos = this.getAllTabInfos(sessionId);
|
|
291
|
-
const allTopFrameUrls = Object.keys(infos)
|
|
292
|
-
.map(tabId => infos[tabId].url);
|
|
293
|
-
|
|
294
|
-
if (this.exactUrlMatch(first, second, allTopFrameUrls)) {
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const combineDomainAndPath = urlParts => (`${urlParts.domain}/${urlParts.path.join('/')}`);
|
|
299
|
-
if (this.singleExactMatchForParts(first, second, allTopFrameUrls, combineDomainAndPath)) {
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const combineDomainPathAndHash = urlParts => (`${urlParts.domain}/${urlParts.path.join('/')}#${urlParts.hash}`);
|
|
304
|
-
if (this.singleExactMatchForParts(first, second, allTopFrameUrls, combineDomainPathAndHash)) {
|
|
305
|
-
return true;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (first.order === second.order) {
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
getMainTabInfo(sessionId) {
|
|
316
|
-
const infos = this.getAllTabInfos(sessionId);
|
|
317
|
-
return Object.keys(infos)
|
|
318
|
-
.map(id => infos[id])
|
|
319
|
-
.find(tabInfo => tabInfo.isMain);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
getMainTabUtils(sessionId) {
|
|
323
|
-
const mainTabInfo = this.getMainTabInfo(sessionId);
|
|
324
|
-
if (!mainTabInfo) {
|
|
325
|
-
return {};
|
|
326
|
-
}
|
|
327
|
-
return this.getTabUtils(sessionId, mainTabInfo);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
removeTabInfo(sessionId, tabId) {
|
|
331
|
-
const infos = this.getAllTabInfos(sessionId);
|
|
332
|
-
const info = infos[tabId];
|
|
333
|
-
delete this.sessionTabs[sessionId].tabInfos[tabId];
|
|
334
|
-
delete this._utils[info.infoId];
|
|
335
|
-
this.sessionTabs[sessionId].tabCount--;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
getMainTabId(sessionId) {
|
|
339
|
-
const infos = this.getAllTabInfos(sessionId);
|
|
340
|
-
return Object.keys(infos).find(id => infos[id].isMain);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
isMainTabExists(sessionId) {
|
|
344
|
-
const mainTabId = this.getMainTabId(sessionId);
|
|
345
|
-
if (!mainTabId) {
|
|
346
|
-
return Promise.resolve(false);
|
|
347
|
-
}
|
|
348
|
-
return Promise.resolve(true);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
clearAllTabs(sessionId) {
|
|
352
|
-
const infos = this.getAllTabInfos(sessionId);
|
|
353
|
-
|
|
354
|
-
this.sessionTabs[sessionId].tabCount = 0;
|
|
355
|
-
Object.keys(infos)
|
|
356
|
-
.forEach(tabId => this.removeTabInfo(sessionId, tabId));
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
clearNonMainTabs(sessionId) {
|
|
360
|
-
const infos = this.getAllTabInfos(sessionId);
|
|
361
|
-
Object.keys(infos)
|
|
362
|
-
.filter(tabId => !infos[tabId].isMain)
|
|
363
|
-
.forEach(tabId => this.removeTabInfo(sessionId, tabId));
|
|
364
|
-
|
|
365
|
-
this.sessionTabs[sessionId].tabCount = 1;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
switchTab(tabId, sessionId, { forceSwitch = false } = {}) {
|
|
369
|
-
const tabCount = this.sessionTabs[sessionId] ? this.sessionTabs[sessionId].tabCount : 1;
|
|
370
|
-
// looking at session tabs here and tabCount only works if we already "know" all the tabs, so we
|
|
371
|
-
// opt out of it in hybrid steps and other cases.
|
|
372
|
-
if ((typeof tabCount === 'number' && tabCount > 1) || forceSwitch) {
|
|
373
|
-
return this.driver.switchTab(tabId);
|
|
374
|
-
}
|
|
375
|
-
return Promise.resolve();
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
getTabDetails(tabId, sessionId, options = {}) {
|
|
379
|
-
return this.switchTab(tabId, sessionId, options)
|
|
380
|
-
.then(() => {
|
|
381
|
-
if (options.skipLoadInfo) { // the tab title and url are not interesting yet
|
|
382
|
-
return { title: '', url: '' };
|
|
383
|
-
}
|
|
384
|
-
let mainTabPromise = Promise.resolve('unknown');
|
|
385
|
-
if (options.checkForMainTab) {
|
|
386
|
-
mainTabPromise = this.driver.executeJS('return window.__isMainTestimTab').then(r => r.value);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return Promise.all([this.driver.getTitle(), this.driver.getUrl(), mainTabPromise]).then(
|
|
390
|
-
([title, url, isMainTab]) => ({ title, url, isMainTab }),
|
|
391
|
-
err => {
|
|
392
|
-
logger.error('failed to get url or title', { err });
|
|
393
|
-
return {
|
|
394
|
-
title: '',
|
|
395
|
-
url: '',
|
|
396
|
-
};
|
|
397
|
-
},
|
|
398
|
-
);
|
|
399
|
-
})
|
|
400
|
-
.catch(err => {
|
|
401
|
-
logger.error('failed to switch to tab', { tabId, err });
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
async getUnregisteredTabId(sessionId) {
|
|
406
|
-
const ids = await this.driver.getTabIds();
|
|
407
|
-
return ids.find(tabId => !this.getAllTabIds(sessionId).includes(tabId));
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
async waitForTabToOpen(sessionId) {
|
|
411
|
-
const newId = await this.getUnregisteredTabId(sessionId);
|
|
412
|
-
if (newId) {
|
|
413
|
-
return newId;
|
|
414
|
-
}
|
|
415
|
-
await utils.delay(500);
|
|
416
|
-
return await this.waitForTabToOpen(sessionId);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
async tryToAddTab(sessionId, openerStepId) {
|
|
420
|
-
if (this.pendingTabs[sessionId]) {
|
|
421
|
-
// don't mess with the main flow
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
const newId = await this.getUnregisteredTabId(sessionId);
|
|
425
|
-
if (!newId) {
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
await this.addNewTab(sessionId, newId);
|
|
429
|
-
await this.addFrameHandler(newId);
|
|
430
|
-
this.sessionTabs[sessionId].currentTab = null;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
addNewPopup(id, openerStepId) {
|
|
434
|
-
const tabInfos = this.getAllTabInfos(id);
|
|
435
|
-
const stepWasAdded = Object.keys(tabInfos).find(tabId => tabInfos[tabId].openerStepId === openerStepId);
|
|
436
|
-
if (stepWasAdded) {
|
|
437
|
-
return Promise.resolve();
|
|
438
|
-
}
|
|
439
|
-
if (this.pendingTabs[id]) {
|
|
440
|
-
logger.info(`overriding opener step id from ${this.pendingTabs[id]} to ${openerStepId}`);
|
|
441
|
-
this.pendingTabs[id] = openerStepId;
|
|
442
|
-
return Promise.resolve();
|
|
443
|
-
}
|
|
444
|
-
this.pendingTabs[id] = openerStepId;
|
|
445
|
-
return this.waitForTabToOpen(id)
|
|
446
|
-
.then(newTabId => this.addNewTab(id, newTabId, this.pendingTabs[id])
|
|
447
|
-
.then(() => this.addFrameHandler(newTabId))
|
|
448
|
-
.then(() => delete this.pendingTabs[id])
|
|
449
|
-
.then(() => (this.sessionTabs[id].currentTab = null)));
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
waitToPendingTabs(id, openerStepId) {
|
|
453
|
-
const retryInterval = 500;
|
|
454
|
-
let timeToWait = 3000;
|
|
455
|
-
const that = this;
|
|
456
|
-
|
|
457
|
-
if (!openerStepId) {
|
|
458
|
-
return Promise.resolve();
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
return new Promise(resolve => {
|
|
462
|
-
function checkPendingTabs() {
|
|
463
|
-
if (that.pendingTabs[id] === openerStepId) {
|
|
464
|
-
if (timeToWait - retryInterval > 0) {
|
|
465
|
-
timeToWait -= retryInterval;
|
|
466
|
-
setTimeout(checkPendingTabs, retryInterval);
|
|
467
|
-
} else {
|
|
468
|
-
resolve();
|
|
469
|
-
}
|
|
470
|
-
} else {
|
|
471
|
-
resolve();
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
checkPendingTabs();
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
isMainTabIncognito() {
|
|
480
|
-
return Promise.resolve(false);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
isInvalidStepVersion(step) {
|
|
484
|
-
const isOldVersion = semver.lt(step._version || step.version, '1.2.0');
|
|
485
|
-
const hasParams = !!step.parameterValues;
|
|
486
|
-
const hasBadLocateParams = hasParams &&
|
|
487
|
-
step.parameterValues
|
|
488
|
-
.filter(param => param.type === 'locate')
|
|
489
|
-
.filter(param => !param.frameLocators)
|
|
490
|
-
.length > 0;
|
|
491
|
-
|
|
492
|
-
return isOldVersion && (!hasParams || hasBadLocateParams);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
getTabIdByTabInfo(sessionId, step) {
|
|
496
|
-
const { tabMatcher, commonConstants } = getSessionPlayer();
|
|
497
|
-
if (this.isInvalidStepVersion(step)) {
|
|
498
|
-
return Promise.reject({
|
|
499
|
-
success: false,
|
|
500
|
-
shouldRetry: false,
|
|
501
|
-
errorType: commonConstants.stepResult.INVALID_TEST_VERSION,
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
const openerStepId = step.tabInfo?.openerStepId;
|
|
505
|
-
return this.waitToPendingTabs(sessionId, openerStepId)
|
|
506
|
-
.then(() => {
|
|
507
|
-
let tabId;
|
|
508
|
-
if (tabMatcher) {
|
|
509
|
-
const allTabInfos = this.getAllTabIds(sessionId)
|
|
510
|
-
.map((tabId) => Object.assign({}, this.getTabInfo(sessionId, tabId), { tabId }))
|
|
511
|
-
.filter((tabInfo) => !tabInfo.isClosed);
|
|
512
|
-
tabId = tabMatcher.matchTabs(step, allTabInfos);
|
|
513
|
-
} else {
|
|
514
|
-
// old session player (clickim) version - remove once enough time passed
|
|
515
|
-
const stepTabInfo = step.tabInfo;
|
|
516
|
-
if (!stepTabInfo) {
|
|
517
|
-
tabId = this.getMainTabId(sessionId);
|
|
518
|
-
} else {
|
|
519
|
-
tabId = this.getAllTabIds(sessionId).find(tabId => {
|
|
520
|
-
const tabInfo = this.getTabInfo(sessionId, tabId);
|
|
521
|
-
return this.isSameTab(sessionId, tabInfo, stepTabInfo);
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if (!tabId) {
|
|
527
|
-
return this.tryToAddTab(sessionId, openerStepId)
|
|
528
|
-
.then(() => { throw new Error('No tab ID found'); });
|
|
529
|
-
}
|
|
530
|
-
if (this.sessionTabs[sessionId].currentTab === tabId) {
|
|
531
|
-
return tabId;
|
|
532
|
-
}
|
|
533
|
-
return this.switchTab(tabId, sessionId)
|
|
534
|
-
.then(() => {
|
|
535
|
-
this.sessionTabs[sessionId].currentTab = tabId;
|
|
536
|
-
return tabId;
|
|
537
|
-
})
|
|
538
|
-
.catch(err => {
|
|
539
|
-
const windowClosedErrors = ['no such window', 'no window found', 'the window could not be found'];
|
|
540
|
-
if (err.message && windowClosedErrors.some(errorString => err.message.toLowerCase().includes(errorString))) {
|
|
541
|
-
this.sessionTabs[sessionId].tabCount--;
|
|
542
|
-
this.sessionTabs[sessionId].tabInfos[tabId].isClosed = true;
|
|
543
|
-
return this.getTabIdByTabInfo(sessionId, step);
|
|
544
|
-
}
|
|
545
|
-
throw err;
|
|
546
|
-
});
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
module.exports = TabService;
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {{
|
|
5
|
-
* attachDebugger(): Promise<any>,
|
|
6
|
-
* detachDebugger(): Promise<any>,
|
|
7
|
-
* onDebuggerDetached(): {},
|
|
8
|
-
* tabId: string,
|
|
9
|
-
* domUtils: { getDOM(): Promise<any> },
|
|
10
|
-
* windowUtils: WindowUtils,
|
|
11
|
-
* imageCaptureUtils: ImageCaptureUtils
|
|
12
|
-
* }} TabUtil
|
|
13
|
-
* */
|
|
14
|
-
/**
|
|
15
|
-
* @typedef {{
|
|
16
|
-
* infoId: any;
|
|
17
|
-
* url: any;
|
|
18
|
-
* title: any;
|
|
19
|
-
* favIconUrl: any;
|
|
20
|
-
* order: any;
|
|
21
|
-
* from: any;
|
|
22
|
-
* isMain: any;
|
|
23
|
-
* openerStepId: any;
|
|
24
|
-
* }} TabInfo
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
class TabService {
|
|
28
|
-
/** @param {import('../webdriver')} driver */
|
|
29
|
-
constructor(driver) {
|
|
30
|
-
this.driver = driver;
|
|
31
|
-
/** @type {Record<string, TabUtil>} */
|
|
32
|
-
this._utils = {};
|
|
33
|
-
/** @type {Record<string, { tabCount: number; tabInfos: Record<string, TabInfo> }>} */
|
|
34
|
-
this.sessionTabs = {};
|
|
35
|
-
/** @type {Record<string, string>} */
|
|
36
|
-
this.pendingTabs = {};
|
|
37
|
-
/** @type {Record<string, Set<any>>} */
|
|
38
|
-
this.addedTabs = {};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
on() {}
|
|
42
|
-
|
|
43
|
-
tabCount(sessionId) {
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
getAllOpenTabIds(sessionId) {
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Get last tab info for pixel validation
|
|
51
|
-
*
|
|
52
|
-
* @returns last tab info
|
|
53
|
-
*/
|
|
54
|
-
getActiveTabInfo(sessionId) {
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
getAllTabIds(sessionId) {
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
isSessionTab(sessionId, tabId) {
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
createSesion(sessionId) {
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
setAddFrameHandlerCallBack(addFrameHandlerCb) {
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
getAllTabInfoStrings(sessionId) {
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
getAllTabInfos(sessionId) {
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
addNewTab(sessionId, tabId, openerStepId, options = {}) {
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
addOpenerStepId(sessionId, tabId, openerStepId) {
|
|
79
|
-
}
|
|
80
|
-
addOpenerStep(sessionId, tabId, openerStep) {
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
fixMissingMainTab(sessionId) {
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
buildTabInfo(sessionId, tabId, order, openerStepId, options = {}) {
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
addTab(sessionId, id, order, openerStepId, options = {}) {
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
getTabUtilsByTabIdAndSessionId(sessionId, tabId) {
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
getTabUtilsByTabId(tabId) {
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
getTabInfo(sessionId, id) {
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
getTabUtils(sessionId, tabInfo) {
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
exactUrlMatch(first, second, allUrls) {
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
singleExactMatchForParts(first, second, allUrls, combinePartsFunction) {
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
isSameTab(sessionId, first, second) {
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
getMainTabInfo(sessionId) {
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
getMainTabUtils(sessionId) {
|
|
117
|
-
return {};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
removeTabInfo(sessionId, tabId) {
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
getMainTabId(sessionId) {
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
isMainTabExists(sessionId) {
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
clearAllTabs(sessionId) {
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
clearNonMainTabs(sessionId) {
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
switchTab(tabId, sessionId, { forceSwitch } = { forceSwitch: false }) {
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
getTabDetails(tabId, sessionId, options = {}) {
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
getUnregisteredTabId(sessionId) {
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
waitForTabToOpen(sessionId) {
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
tryToAddTab(sessionId, openerStepId) {
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
addNewPopup(id, openerStepId) {
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
waitToPendingTabs(id, openerStepId) {
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
isMainTabIncognito() {
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
isInvalidStepVersion(step) {
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
getTabIdByTabInfo(sessionId, step) {
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
module.exports = TabService;
|