@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,357 +0,0 @@
|
|
|
1
|
-
const { expect, sinon } = require('../../test/utils/testUtils');
|
|
2
|
-
const gridService = require('./gridService');
|
|
3
|
-
const servicesApi = require('../commons/testimServicesApi');
|
|
4
|
-
|
|
5
|
-
describe('gridService', () => {
|
|
6
|
-
describe('handleHybridOrVendorIfNeeded', () => {
|
|
7
|
-
let getHybridGridProviderStub;
|
|
8
|
-
let ltServiceMock;
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
ltServiceMock = { enableIfNeeded: sinon.stub().resolves(), disable: sinon.stub().resolves() };
|
|
12
|
-
getHybridGridProviderStub = sinon.stub(servicesApi, 'getHybridGridProvider');
|
|
13
|
-
});
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
getHybridGridProviderStub.restore();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('should return grid if no grid id or grid type', async () => {
|
|
19
|
-
const grid = { name: 'grid' };
|
|
20
|
-
const result = await gridService.handleHybridOrVendorIfNeeded();
|
|
21
|
-
expect(result).to.eql({});
|
|
22
|
-
sinon.assert.notCalled(getHybridGridProviderStub);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should return grid if it is not a hybrid grid', async () => {
|
|
26
|
-
const grid = { type: 'not hybrid', gridId: 'gridId' };
|
|
27
|
-
const result = await gridService.handleHybridOrVendorIfNeeded({ company: {} }, grid);
|
|
28
|
-
expect(result).to.equal(grid);
|
|
29
|
-
sinon.assert.notCalled(getHybridGridProviderStub);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should return grid if it is hybrid with insufficient data to get its provider', async () => {
|
|
33
|
-
const grid = { type: 'testimHybrid', gridId: 'gridId' };
|
|
34
|
-
const result = await gridService.handleHybridOrVendorIfNeeded({ company: { companyId: 'companyId' }, browser: 'chrome' }, grid);
|
|
35
|
-
expect(result).to.equal(grid);
|
|
36
|
-
sinon.assert.notCalled(getHybridGridProviderStub);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should get grid provider for hybrid grids', async () => {
|
|
40
|
-
getHybridGridProviderStub.resolves({ provider: 'provider' });
|
|
41
|
-
const grid = { type: 'testimHybrid', gridId: 'gridId' };
|
|
42
|
-
const result = await gridService.handleHybridOrVendorIfNeeded({ company: { companyId: 'companyId' }, browser: 'chrome' }, grid, {}, ltServiceMock, { currentRetry: 1, maxRetries: 1 });
|
|
43
|
-
expect(result).to.shallowDeepEqual({ ...grid, provider: 'provider' });
|
|
44
|
-
sinon.assert.calledOnce(getHybridGridProviderStub);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should enable lambda test service if lambda test grid', async () => {
|
|
48
|
-
const grid = { type: 'testimLambdaTest', gridId: 'gridId' };
|
|
49
|
-
await gridService.handleHybridOrVendorIfNeeded({ company: {} }, grid, {}, ltServiceMock);
|
|
50
|
-
sinon.assert.calledOnce(ltServiceMock.enableIfNeeded);
|
|
51
|
-
sinon.assert.notCalled(getHybridGridProviderStub);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should enable lambda test service if hybrid grid and lambda test provider', async () => {
|
|
55
|
-
getHybridGridProviderStub.resolves({ provider: 'lambdatest' });
|
|
56
|
-
const grid = { type: 'testimHybrid', gridId: 'gridId' };
|
|
57
|
-
await gridService.handleHybridOrVendorIfNeeded({ company: { companyId: 'companyId' }, browser: 'chrome' }, grid, {}, ltServiceMock, { currentRetry: 1, maxRetries: 1 });
|
|
58
|
-
sinon.assert.calledOnce(ltServiceMock.enableIfNeeded);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should disable lambda test service if hybrid grid and not lambda test provider', async () => {
|
|
62
|
-
getHybridGridProviderStub.resolves({ provider: 'provider' });
|
|
63
|
-
const grid = { type: 'testimHybrid', gridId: 'gridId' };
|
|
64
|
-
await gridService.handleHybridOrVendorIfNeeded({ company: { companyId: 'companyId' }, browser: 'chrome' }, grid, {}, ltServiceMock, { currentRetry: 1, maxRetries: 1 });
|
|
65
|
-
sinon.assert.calledOnce(ltServiceMock.disable);
|
|
66
|
-
sinon.assert.notCalled(ltServiceMock.enableIfNeeded);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should return hybrid grid tunnel credentials if set', async () => {
|
|
70
|
-
getHybridGridProviderStub.resolves({ provider: 'provider', connectionDetails: { hybrid: { tunnel: 'provider', external: { provider: { user: 'username', key: 'password' } } } } });
|
|
71
|
-
const grid = { type: 'testimHybrid', gridId: 'gridId' };
|
|
72
|
-
const result = await gridService.handleHybridOrVendorIfNeeded({ company: { companyId: 'companyId' }, browser: 'chrome' }, grid, {}, ltServiceMock, { currentRetry: 1, maxRetries: 1 });
|
|
73
|
-
expect(result).to.shallowDeepEqual({ ...grid, provider: 'provider', key: undefined, user: undefined, tunnelUser: 'username', tunnelKey: 'password' });
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should ignore hybrid grid tunnel credentials if tunnel set to a different provider', async () => {
|
|
77
|
-
getHybridGridProviderStub.resolves({ provider: 'provider', connectionDetails: { hybrid: { tunnel: 'otherprovider', external: { provider: { user: 'username', key: 'password' } } } } });
|
|
78
|
-
const grid = { type: 'testimHybrid', gridId: 'gridId' };
|
|
79
|
-
const result = await gridService.handleHybridOrVendorIfNeeded({ company: { companyId: 'companyId' }, browser: 'chrome' }, grid, {}, ltServiceMock, { currentRetry: 1, maxRetries: 1 });
|
|
80
|
-
expect(result).to.shallowDeepEqual({ ...grid, provider: 'provider', key: undefined, user: undefined, tunnelUser: undefined, tunnelKey: undefined });
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('getGridData', () => {
|
|
85
|
-
let getAllGridsStub;
|
|
86
|
-
let getTestPlanStub;
|
|
87
|
-
|
|
88
|
-
beforeEach(() => {
|
|
89
|
-
getAllGridsStub = sinon.stub(servicesApi, 'getAllGrids');
|
|
90
|
-
getTestPlanStub = sinon.stub(servicesApi, 'getTestPlan').resolves([{ gridId: 'gridId' }]);
|
|
91
|
-
});
|
|
92
|
-
afterEach(() => {
|
|
93
|
-
getAllGridsStub.restore();
|
|
94
|
-
getTestPlanStub.restore();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should not access server when using useLocalChromeDriver flag', async () => {
|
|
98
|
-
const grid = await gridService.getGridData({ useLocalChromeDriver: true });
|
|
99
|
-
expect(grid).to.eql({ mode: 'local' });
|
|
100
|
-
sinon.assert.notCalled(getAllGridsStub);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should not access server when using useChromeLauncher flag', async () => {
|
|
104
|
-
const grid = await gridService.getGridData({ useChromeLauncher: true });
|
|
105
|
-
expect(grid).to.eql({ mode: 'local' });
|
|
106
|
-
sinon.assert.notCalled(getAllGridsStub);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should return fixed grid when passing host and port', async () => {
|
|
110
|
-
const grid = await gridService.getGridData({ host: 'localhost', port: 4444 });
|
|
111
|
-
expect(grid).to.shallowDeepEqual({ type: 'hostAndPort', host: 'localhost', port: 4444 });
|
|
112
|
-
sinon.assert.notCalled(getAllGridsStub);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should get grid from server when passing grid id', async () => {
|
|
116
|
-
getAllGridsStub.resolves([{ _id: 'gridId', type: 'gridId' }]);
|
|
117
|
-
const grid = await gridService.getGridData({ gridId: 'gridId', company: { companyId: 'companyId' } });
|
|
118
|
-
expect(grid).to.shallowDeepEqual({ type: 'gridId', gridId: 'gridId' });
|
|
119
|
-
sinon.assert.calledOnce(getAllGridsStub);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should use existing grid list if passed when passing grid id', async () => {
|
|
123
|
-
const grid = await gridService.getGridData({ gridId: 'gridId', company: { companyId: 'companyId' }, allGrids: [{ _id: 'gridId', type: 'gridId' }] });
|
|
124
|
-
expect(grid).to.shallowDeepEqual({ type: 'gridId', gridId: 'gridId' });
|
|
125
|
-
sinon.assert.notCalled(getAllGridsStub);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should handle grid not found error when passing grid id', async () => {
|
|
129
|
-
getAllGridsStub.resolves([{ _id: 'gridId', type: 'gridId' }]);
|
|
130
|
-
await expect(gridService.getGridData({ gridId: 'gridId1', company: { companyId: 'companyId' } }))
|
|
131
|
-
.to.eventually.be.rejectedWith('Failed to find grid id: gridId1');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should get grid from server when passing grid name', async () => {
|
|
135
|
-
getAllGridsStub.resolves([{ name: 'GRIDNAME', type: 'gridName' }]);
|
|
136
|
-
const grid = await gridService.getGridData({ grid: 'gridName', company: { companyId: 'companyId' } });
|
|
137
|
-
expect(grid).to.shallowDeepEqual({ type: 'gridName', name: 'GRIDNAME' });
|
|
138
|
-
sinon.assert.calledOnce(getAllGridsStub);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should use existing grid list if passed when passing grid name', async () => {
|
|
142
|
-
const grid = await gridService.getGridData({ grid: 'gridName', company: { companyId: 'companyId' }, allGrids: [{ name: 'GRIDNAME', type: 'gridName' }] });
|
|
143
|
-
expect(grid).to.shallowDeepEqual({ type: 'gridName', name: 'GRIDNAME' });
|
|
144
|
-
sinon.assert.notCalled(getAllGridsStub);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should handle grid not found error when passing grid name', async () => {
|
|
148
|
-
getAllGridsStub.resolves([{ type: 'gridName' }]);
|
|
149
|
-
await expect(gridService.getGridData({ grid: 'gridName', company: { companyId: 'companyId' } }))
|
|
150
|
-
.to.eventually.be.rejectedWith('Failed to find grid name: gridName');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should not assign a grid when using a test plan', async () => {
|
|
154
|
-
const grid = await gridService.getGridData({ testPlan: ['testPlan'], company: { companyId: 'companyId' }, allGrids: [{ _id: 'gridId', type: 'gridId' }] });
|
|
155
|
-
expect(grid).to.be.undefined;
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should not assign a grid when using a test plan id', async () => {
|
|
159
|
-
const grid = await gridService.getGridData({ testPlanIds: ['testPlan'], company: { companyId: 'companyId' }, allGrids: [{ _id: 'gridId', type: 'gridId' }] });
|
|
160
|
-
expect(grid).to.be.undefined;
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should throw when no grid selected', async () => {
|
|
164
|
-
await expect(gridService.getGridData({ company: { companyId: 'companyId' } }))
|
|
165
|
-
.to.eventually.be.rejectedWith('Missing host or grid configuration');
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe('getGridSlot', () => {
|
|
170
|
-
let getGridByIdStub;
|
|
171
|
-
let getGridByNameStub;
|
|
172
|
-
let addItemToGridCacheStub;
|
|
173
|
-
|
|
174
|
-
beforeEach(() => {
|
|
175
|
-
getGridByIdStub = sinon.stub(servicesApi, 'getGridById').resolves({ grid: { gridId: 'gridId', type: 'gridId' }, status: 'success' });
|
|
176
|
-
getGridByNameStub = sinon.stub(servicesApi, 'getGridByName').resolves({ grid: { gridId: 'gridId', type: 'gridName' }, status: 'success' });
|
|
177
|
-
addItemToGridCacheStub = sinon.stub(gridService, 'addItemToGridCache').callThrough();
|
|
178
|
-
});
|
|
179
|
-
afterEach(() => {
|
|
180
|
-
getGridByIdStub.restore();
|
|
181
|
-
getGridByNameStub.restore();
|
|
182
|
-
addItemToGridCacheStub.restore();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('should not access server when using useLocalChromeDriver flag', async () => {
|
|
186
|
-
const slot = await gridService.getGridSlot('browser', 'executionId', { useLocalChromeDriver: true }, 'workerId');
|
|
187
|
-
expect(slot).to.eql({ mode: 'local' });
|
|
188
|
-
sinon.assert.notCalled(getGridByIdStub);
|
|
189
|
-
sinon.assert.notCalled(getGridByNameStub);
|
|
190
|
-
sinon.assert.notCalled(addItemToGridCacheStub);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should not access server when using useChromeLauncher flag', async () => {
|
|
194
|
-
const slot = await gridService.getGridSlot('browser', 'executionId', { useChromeLauncher: true }, 'workerId');
|
|
195
|
-
expect(slot).to.eql({ mode: 'local' });
|
|
196
|
-
sinon.assert.notCalled(getGridByIdStub);
|
|
197
|
-
sinon.assert.notCalled(getGridByNameStub);
|
|
198
|
-
sinon.assert.notCalled(addItemToGridCacheStub);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('should return fixed grid when passing host and port', async () => {
|
|
202
|
-
const slot = await gridService.getGridSlot('browser', 'executionId', { host: 'localhost', port: 4444 }, 'workerId');
|
|
203
|
-
expect(slot).to.shallowDeepEqual({ type: 'hostAndPort', host: 'localhost', port: 4444 });
|
|
204
|
-
sinon.assert.notCalled(getGridByIdStub);
|
|
205
|
-
sinon.assert.notCalled(getGridByNameStub);
|
|
206
|
-
sinon.assert.notCalled(addItemToGridCacheStub);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('should get grid from server when passing grid id', async () => {
|
|
210
|
-
const slot = await gridService.getGridSlot('browser', 'executionId', { gridId: 'gridId' }, 'workerId');
|
|
211
|
-
expect(slot).to.shallowDeepEqual({ type: 'gridId', gridId: 'gridId' });
|
|
212
|
-
sinon.assert.calledOnce(getGridByIdStub);
|
|
213
|
-
sinon.assert.notCalled(getGridByNameStub);
|
|
214
|
-
sinon.assert.calledOnce(addItemToGridCacheStub);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should get grid from server when passing grid name', async () => {
|
|
218
|
-
const slot = await gridService.getGridSlot('browser', 'executionId', { grid: 'gridName' }, 'workerId');
|
|
219
|
-
expect(slot).to.shallowDeepEqual({ type: 'gridName', gridId: 'gridId' });
|
|
220
|
-
sinon.assert.calledOnce(getGridByNameStub);
|
|
221
|
-
sinon.assert.notCalled(getGridByIdStub);
|
|
222
|
-
sinon.assert.calledOnce(addItemToGridCacheStub);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should handle grid not found error', async () => {
|
|
226
|
-
getGridByIdStub.resolves({ status: 'error', code: 'not-found' });
|
|
227
|
-
await expect(gridService.getGridSlot('browser', 'executionId', { gridId: 'gridId' }, 'workerId'))
|
|
228
|
-
.to.eventually.be.rejectedWith('The specified grid is not available');
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it('should handle no available slot error', async () => {
|
|
232
|
-
getGridByIdStub.resolves({ status: 'error', code: 'no-available-slot' });
|
|
233
|
-
await expect(gridService.getGridSlot('browser', 'executionId', { gridId: 'gridId' }, 'workerId'))
|
|
234
|
-
.to.eventually.be.rejectedWith('Failed to run test on browser - concurrency limit reached');
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it('should handle getGridSlot request error', async () => {
|
|
238
|
-
getGridByIdStub.rejects({ status: 'error', code: 'not-found' });
|
|
239
|
-
await expect(gridService.getGridSlot('browser', 'executionId', { gridId: 'gridId' }, 'workerId'))
|
|
240
|
-
.to.eventually.be.rejectedWith('Test couldn\'t get browser - unknown error');
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('should throw when no grid selected', async () => {
|
|
244
|
-
await expect(gridService.getGridSlot('browser', 'executionId', {}, 'workerId'))
|
|
245
|
-
.to.eventually.be.rejectedWith('Missing host or grid configuration');
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('should handle unkonwn errors', async () => {
|
|
249
|
-
getGridByIdStub.resolves({ status: 'error', code: 'bla bla' });
|
|
250
|
-
await expect(gridService.getGridSlot('browser', 'executionId', { gridId: 'gridId' }, 'workerId'))
|
|
251
|
-
.to.eventually.be.rejectedWith('Test couldn\'t get browser - unknown error');
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it('should handle no status', async () => {
|
|
255
|
-
getGridByIdStub.resolves({ code: 'bla bla' });
|
|
256
|
-
await expect(gridService.getGridSlot('browser', 'executionId', { gridId: 'gridId' }, 'workerId'))
|
|
257
|
-
.to.eventually.be.rejectedWith('Test couldn\'t get browser - unknown error');
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
describe('releaseGridSlot', () => {
|
|
262
|
-
let releaseGridSlotStub;
|
|
263
|
-
beforeEach(() => {
|
|
264
|
-
releaseGridSlotStub = sinon.stub(servicesApi, 'releaseGridSlot').resolves();
|
|
265
|
-
});
|
|
266
|
-
afterEach(() => {
|
|
267
|
-
releaseGridSlotStub.restore();
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should call releaseGridSlot with the correct parameters', async () => {
|
|
271
|
-
gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
|
|
272
|
-
await gridService.releaseGridSlot('workerId', 'projectId');
|
|
273
|
-
sinon.assert.calledWith(releaseGridSlotStub, 'companyId', 'projectId', 'slotId', 'gridId', 'chrome');
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it('should not call releaseGridSlot if the workerId is not in the cache', async () => {
|
|
277
|
-
await gridService.releaseGridSlot('workerId', 'projectId');
|
|
278
|
-
sinon.assert.notCalled(releaseGridSlotStub);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('should not call releaseGridSlot if no slotId', async () => {
|
|
282
|
-
gridService.addItemToGridCache('workerId', 'companyId', 'gridId');
|
|
283
|
-
await gridService.releaseGridSlot('workerId', 'projectId');
|
|
284
|
-
sinon.assert.notCalled(releaseGridSlotStub);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('should handle releaseGridSlot request error', async () => {
|
|
288
|
-
releaseGridSlotStub.rejects({ });
|
|
289
|
-
gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
|
|
290
|
-
await gridService.releaseGridSlot('workerId', 'projectId');
|
|
291
|
-
sinon.assert.calledWith(releaseGridSlotStub, 'companyId', 'projectId', 'slotId', 'gridId', 'chrome');
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
describe('keepAlive', () => {
|
|
296
|
-
let clock;
|
|
297
|
-
let keepAliveStub;
|
|
298
|
-
let releaseGridSlotStub;
|
|
299
|
-
beforeEach(() => {
|
|
300
|
-
clock = sinon.useFakeTimers();
|
|
301
|
-
keepAliveStub = sinon.stub(servicesApi, 'keepAliveGrid').resolves();
|
|
302
|
-
releaseGridSlotStub = sinon.stub(servicesApi, 'releaseGridSlot').resolves();
|
|
303
|
-
});
|
|
304
|
-
afterEach(() => {
|
|
305
|
-
clock.restore();
|
|
306
|
-
keepAliveStub.restore();
|
|
307
|
-
releaseGridSlotStub.restore();
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('should send keepAlive to server on an interval', async () => {
|
|
311
|
-
gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
|
|
312
|
-
await gridService.keepAlive.start('projectId');
|
|
313
|
-
clock.tick(10010);
|
|
314
|
-
sinon.assert.calledOnceWithExactly(keepAliveStub, 'projectId', [{ gridId: 'gridId', companyId: 'companyId', slotId: 'slotId', browser: 'chrome' }]);
|
|
315
|
-
await gridService.releaseGridSlot('workerId', 'projectId');
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it('should send keepAlive to server every 10 seconds', async () => {
|
|
319
|
-
gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
|
|
320
|
-
await gridService.keepAlive.start('projectId');
|
|
321
|
-
clock.tick(30010);
|
|
322
|
-
sinon.assert.calledThrice(keepAliveStub);
|
|
323
|
-
await gridService.releaseGridSlot('workerId', 'projectId');
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
it('should not send keepAlive if there is no grid', async () => {
|
|
327
|
-
await gridService.keepAlive.start('projectId');
|
|
328
|
-
clock.tick(10010);
|
|
329
|
-
sinon.assert.notCalled(keepAliveStub);
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
it('should handle keepAlive request error', async () => {
|
|
333
|
-
keepAliveStub.rejects({ });
|
|
334
|
-
gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
|
|
335
|
-
await gridService.keepAlive.start('projectId');
|
|
336
|
-
clock.tick(10010);
|
|
337
|
-
sinon.assert.calledOnce(keepAliveStub);
|
|
338
|
-
await gridService.releaseGridSlot('workerId', 'projectId');
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
it('should release all slots when ending', async () => {
|
|
342
|
-
gridService.addItemToGridCache('workerId', 'companyId', 'gridId', 'slotId', 'chrome');
|
|
343
|
-
gridService.addItemToGridCache('workerId1', 'companyId', 'gridId', 'slotId1', 'firefox');
|
|
344
|
-
await gridService.keepAlive.start('projectId');
|
|
345
|
-
await gridService.keepAlive.end('projectId');
|
|
346
|
-
sinon.assert.calledTwice(releaseGridSlotStub);
|
|
347
|
-
sinon.assert.calledWith(releaseGridSlotStub, 'companyId', 'projectId', 'slotId', 'gridId', 'chrome');
|
|
348
|
-
sinon.assert.calledWith(releaseGridSlotStub, 'companyId', 'projectId', 'slotId1', 'gridId', 'firefox');
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('should not release slots if there is no slots in use', async () => {
|
|
352
|
-
await gridService.keepAlive.start('projectId');
|
|
353
|
-
await gridService.keepAlive.end('projectId');
|
|
354
|
-
sinon.assert.notCalled(releaseGridSlotStub);
|
|
355
|
-
});
|
|
356
|
-
});
|
|
357
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const featureFlagService = require('../commons/featureFlags');
|
|
4
|
-
const { getLabFeaturesByProjectId } = require('../commons/testimServicesApi');
|
|
5
|
-
const _ = require('lodash');
|
|
6
|
-
|
|
7
|
-
const logger = require('../commons/logger').getLogger('lab-features-service');
|
|
8
|
-
|
|
9
|
-
class LabFeaturesService {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.featuresForProject = [];
|
|
12
|
-
this.labBatman = false;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async loadLabFeatures(projectId, companyPlan) {
|
|
16
|
-
if (!companyPlan || !projectId) {
|
|
17
|
-
logger.error('missing companyPlan or projectId when loading lab features', { companyPlan, projectId });
|
|
18
|
-
this.featuresForProject = [];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const labBatman = this.isLabsEnabledForCompany(companyPlan);
|
|
23
|
-
const features = labBatman ? (await getLabFeaturesByProjectId(projectId)) : [];
|
|
24
|
-
this.featuresForProject = features;
|
|
25
|
-
this.labBatman = labBatman;
|
|
26
|
-
} catch (err) {
|
|
27
|
-
logger.error('failed loading lab features', { err, companyPlan, projectId });
|
|
28
|
-
this.featuresForProject = [];
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
isFeatureAvailableForProject(featureFlagName) {
|
|
33
|
-
const featureFlag = featureFlagService.flags[featureFlagName];
|
|
34
|
-
this.validateAsLabFeatureFlag(featureFlag);
|
|
35
|
-
const ffValue = featureFlag.getValue();
|
|
36
|
-
if (ffValue === 'disabled') {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
if (ffValue === 'enabled') {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const { featuresForProject: features, labBatman } = this;
|
|
44
|
-
const labFeature = features.find(f => f.featureFlagName === featureFlagName);
|
|
45
|
-
const featureEnabled = labFeature?.enabled;
|
|
46
|
-
|
|
47
|
-
return Boolean(labBatman && featureEnabled);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
isLabsEnabledForCompany(companyPlan) {
|
|
51
|
-
return Boolean(companyPlan?.premiumFeatures?.enableLabFeatures);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
validateAsLabFeatureFlag(featureFlag) {
|
|
55
|
-
if ('getValue' in featureFlag) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const msg = `Attempted querying a lab feature flag which isn't a variant. This means that a wrong configuration is being used in FeatureFlagsService (for feature flag: ${featureFlag.name}`;
|
|
60
|
-
logger.error(msg, { featureFlagName: featureFlag.name });
|
|
61
|
-
throw new Error(msg);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
module.exports = new LabFeaturesService();
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
const os = require('os');
|
|
2
|
-
const childProcess = require('child_process');
|
|
3
|
-
const pRetry = require('p-retry');
|
|
4
|
-
const fse = require('fs-extra');
|
|
5
|
-
const portfinder = require('portfinder');
|
|
6
|
-
const ms = require('ms');
|
|
7
|
-
const utils = require('../utils');
|
|
8
|
-
const { gridTypes, CLI_MODE } = require('../commons/constants');
|
|
9
|
-
const httpRequest = require('../commons/httpRequest');
|
|
10
|
-
const { ArgError } = require('../errors');
|
|
11
|
-
const servicesApi = require('../commons/testimServicesApi');
|
|
12
|
-
const { getExtensionsUrl } = require('../runOptionsUtils');
|
|
13
|
-
const featureFlagService = require('../commons/featureFlags');
|
|
14
|
-
|
|
15
|
-
const logger = require('../commons/logger').getLogger('lambdatestService');
|
|
16
|
-
|
|
17
|
-
const LT_TUNNEL_BINARY_ORIGIN = 'https://downloads.lambdatest.com/tunnel/v3';
|
|
18
|
-
const LT_TUNNEL_BINARY_PATHNAME = {
|
|
19
|
-
win32ia32: 'windows/32bit/LT_Windows.zip',
|
|
20
|
-
win32x64: 'windows/64bit/LT_Windows.zip',
|
|
21
|
-
darwinia32: 'mac/32bit/LT_Mac.zip',
|
|
22
|
-
darwinx64: 'mac/64bit/LT_Mac.zip',
|
|
23
|
-
darwinarm64: 'mac/64bit/LT_Mac.zip',
|
|
24
|
-
linuxia32: 'linux/32bit/LT_Linux.zip',
|
|
25
|
-
linuxx64: 'linux/64bit/LT_Linux.zip',
|
|
26
|
-
freebsdia32: 'freebsd/32bit/LT_Freebsd.zip',
|
|
27
|
-
freebsdx64: 'freebsd/64bit/LT_Freebsd.zip',
|
|
28
|
-
};
|
|
29
|
-
const LT_TUNNEL_BINARY_DIRECTORY = `${os.tmpdir()}/LT`;
|
|
30
|
-
const LT_TUNNEL_BINARY_LOCATION = `${LT_TUNNEL_BINARY_DIRECTORY}/LT`;
|
|
31
|
-
|
|
32
|
-
const LT_MINIMUM_CONNECTION_RETRY_TIMEOUT = ms('15m');
|
|
33
|
-
|
|
34
|
-
class LambdatestService {
|
|
35
|
-
constructor() {
|
|
36
|
-
this.isActive = false;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
static isLambdatestGrid(gridData) {
|
|
40
|
-
return gridData.type === gridTypes.LAMBDATEST || (gridData.type === gridTypes.HYBRID && gridData.provider === 'lambdatest');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
isLambdatestRun() {
|
|
44
|
-
return this.isActive;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async enableIfNeeded(gridData) {
|
|
48
|
-
if (!LambdatestService.isLambdatestGrid(gridData)) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
LambdatestService.lambdatestConfigPromise = LambdatestService.lambdatestConfigPromise || servicesApi.fetchLambdatestConfig();
|
|
53
|
-
LambdatestService.lambdatestConfig = await LambdatestService.lambdatestConfigPromise;
|
|
54
|
-
this.isActive = true;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
disable() {
|
|
58
|
-
this.isActive = false;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
get getSessionTimeout() {
|
|
62
|
-
if (!this.isActive) {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
return LT_MINIMUM_CONNECTION_RETRY_TIMEOUT;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
get getSessionRetries() {
|
|
69
|
-
if (!this.isActive) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
return 1;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// https://www.lambdatest.com/support/docs/beta-lambda-tunnel-for-corporate-firewalls/
|
|
76
|
-
static async prepareTunnel() {
|
|
77
|
-
const isBinaryExist = await fse.pathExists(LT_TUNNEL_BINARY_LOCATION);
|
|
78
|
-
if (isBinaryExist) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const downloadUrl = LT_TUNNEL_BINARY_PATHNAME[os.platform() + os.arch()];
|
|
83
|
-
if (!downloadUrl) {
|
|
84
|
-
throw new Error(`tunnel on ${os.platform() + os.arch()} platform is not supported.`);
|
|
85
|
-
}
|
|
86
|
-
const zipLocation = `${LT_TUNNEL_BINARY_DIRECTORY}.zip`;
|
|
87
|
-
await utils.downloadAndSave(`${LT_TUNNEL_BINARY_ORIGIN}/${downloadUrl}`, zipLocation);
|
|
88
|
-
await utils.unzipFile(zipLocation, LT_TUNNEL_BINARY_DIRECTORY);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
static async connectTunnel(runnerOptions) {
|
|
92
|
-
if (runnerOptions.externalLambdatestTunnelId) {
|
|
93
|
-
LambdatestService.tunnelName = runnerOptions.externalLambdatestTunnelId;
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
await this.prepareTunnel();
|
|
97
|
-
const infoAPIPort = await portfinder.getPortPromise();
|
|
98
|
-
const { gridData = {}, gridUsername, gridPassword } = runnerOptions;
|
|
99
|
-
const proxyUri = global.proxyUri;
|
|
100
|
-
LambdatestService.tunnelName = utils.guid();
|
|
101
|
-
|
|
102
|
-
let tunnelArgs = [
|
|
103
|
-
'--tunnelName', LambdatestService.tunnelName,
|
|
104
|
-
'--infoAPIPort', infoAPIPort,
|
|
105
|
-
];
|
|
106
|
-
|
|
107
|
-
if (runnerOptions.externalLambdatestUseWss) {
|
|
108
|
-
tunnelArgs = [...tunnelArgs, '--mode', 'ws'];
|
|
109
|
-
}
|
|
110
|
-
if (runnerOptions.externalLambdatestDisableAutomationTunneling) {
|
|
111
|
-
tunnelArgs = [...tunnelArgs, '--bypassHosts', 'run.testim.io,services.testim.io,api.coralogix.com,conf.rollout.io,statestore.rollout.io,push.rollout.io,analytic.rollout.io,res.cloudinary.com'];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (gridData.tunnelUser && gridData.tunnelKey) {
|
|
115
|
-
tunnelArgs = [...tunnelArgs, '--user', gridData.tunnelUser, '--key', gridData.tunnelKey];
|
|
116
|
-
} else if (gridUsername && gridPassword) {
|
|
117
|
-
tunnelArgs = [...tunnelArgs, '--user', gridUsername, '--key', gridPassword];
|
|
118
|
-
} else {
|
|
119
|
-
throw new ArgError('tunnel requires username and password');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (proxyUri) {
|
|
123
|
-
try {
|
|
124
|
-
const proxyUrl = new URL(proxyUri);
|
|
125
|
-
tunnelArgs = [...tunnelArgs, '--proxy-host', proxyUrl.hostname];
|
|
126
|
-
if (proxyUrl.port) {
|
|
127
|
-
tunnelArgs = [...tunnelArgs, '--proxy-port', proxyUrl.port];
|
|
128
|
-
}
|
|
129
|
-
if (proxyUrl.username && proxyUrl.password) {
|
|
130
|
-
tunnelArgs = [...tunnelArgs, '--proxy-user', proxyUrl.username, '--proxy-pass', proxyUrl.password];
|
|
131
|
-
}
|
|
132
|
-
} catch (e) {
|
|
133
|
-
throw new ArgError('proxy url is invalid');
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (runnerOptions.externalLambdatestMitm) {
|
|
138
|
-
tunnelArgs = [...tunnelArgs, '--mitm'];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
LambdatestService.tunnel = childProcess.spawn('./LT', tunnelArgs, { cwd: LT_TUNNEL_BINARY_DIRECTORY });
|
|
142
|
-
|
|
143
|
-
let stdoutResult = '';
|
|
144
|
-
let stderrResult = '';
|
|
145
|
-
|
|
146
|
-
LambdatestService.tunnel.stdout.on('data', (data) => {
|
|
147
|
-
stdoutResult += data.toString();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
LambdatestService.tunnel.stderr.on('data', (data) => {
|
|
151
|
-
stderrResult += data.toString();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// verify that LT tunnel strated successfully
|
|
155
|
-
try {
|
|
156
|
-
const ltInfo = await pRetry(() => httpRequest.get(`http://127.0.0.1:${infoAPIPort}/api/v1.0/info`, {}, {}, undefined, { skipProxy: true }), { retries: 30, minTimeout: 2000 });
|
|
157
|
-
logger.info('LT tunnel info', ltInfo);
|
|
158
|
-
} catch (err) {
|
|
159
|
-
logger.error('Failed to start LT tunnel', { err, stdoutResult, stderrResult });
|
|
160
|
-
throw err;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
static async disconnectTunnel(runnerOptions) {
|
|
165
|
-
if (runnerOptions.externalLambdatestTunnelId || !LambdatestService.tunnel) {
|
|
166
|
-
return undefined;
|
|
167
|
-
}
|
|
168
|
-
return new Promise((resolve, reject) => {
|
|
169
|
-
LambdatestService.tunnel.on('close', (code) => {
|
|
170
|
-
if (code) {
|
|
171
|
-
reject(new Error(`tunnel process exited with code ${code}`));
|
|
172
|
-
}
|
|
173
|
-
resolve();
|
|
174
|
-
});
|
|
175
|
-
LambdatestService.tunnel.kill();
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
getCapabilities(runnerOptions, browser, executionId, testResultId, testName) {
|
|
180
|
-
if (!this.isActive) {
|
|
181
|
-
return {};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const defaultBrowserCaps = LambdatestService.lambdatestConfig.CAPABILITIES[browser] || {};
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const tunnelCaps = {};
|
|
188
|
-
if (LambdatestService.tunnelName) {
|
|
189
|
-
tunnelCaps.tunnel = true;
|
|
190
|
-
tunnelCaps.tunnelName = LambdatestService.tunnelName;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
let loadExtension = [];
|
|
194
|
-
const { mode, canary, ext, extensionPath, installCustomExtension } = runnerOptions;
|
|
195
|
-
if (mode === CLI_MODE.EXTENSION && !ext) {
|
|
196
|
-
const extUrls = getExtensionsUrl({ canary }, true);
|
|
197
|
-
if (!extensionPath && extUrls[browser]) {
|
|
198
|
-
loadExtension = [...loadExtension, extUrls[browser]];
|
|
199
|
-
}
|
|
200
|
-
if (extensionPath && utils.isURL(extensionPath)) {
|
|
201
|
-
loadExtension = [...loadExtension, extensionPath];
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (installCustomExtension && utils.isURL(installCustomExtension)) {
|
|
205
|
-
loadExtension = [...loadExtension, installCustomExtension];
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
build: executionId,
|
|
210
|
-
name: `${testResultId} - ${testName}`,
|
|
211
|
-
platform: LambdatestService.lambdatestConfig.PLATFORM,
|
|
212
|
-
// eslint-disable-next-line camelcase
|
|
213
|
-
selenium_version: LambdatestService.lambdatestConfig.SELENIUM_VERSION,
|
|
214
|
-
resolution: LambdatestService.lambdatestConfig.RESOLUTION,
|
|
215
|
-
timezone: LambdatestService.lambdatestConfig.TIMEZONE,
|
|
216
|
-
...defaultBrowserCaps,
|
|
217
|
-
loadExtension,
|
|
218
|
-
...tunnelCaps,
|
|
219
|
-
console: true,
|
|
220
|
-
queueTimeout: 300, // time a session spends in the LT queue, in seconds (apparently 300 is the minimum)
|
|
221
|
-
// visual: true, // [NOTE]: activate LT screenshots feature (can slow test).
|
|
222
|
-
network: featureFlagService.flags.LTNetworkCapabilities.isEnabled(), // [NOTE]: activate LT capture network logs feature (can cause network issues).
|
|
223
|
-
// fixedIP: '10.80.34.143', // [NOTE]: this is for debug purposes with LT team.
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
module.exports = LambdatestService;
|