@testim/testim-cli 3.245.0 → 3.246.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.
@@ -23,8 +23,8 @@ class LauncherDriver {
23
23
  this.chrome.process.once('exit', () => { this._isAlive = false; });
24
24
  this.chrome.process.once('close', () => { this._isAlive = false; });
25
25
  this._isAlive = true;
26
- const browserEndpoint = await httpRequest.get(`http://localhost:${this.chrome.port}/json/version`);
27
- await this.cdpTestRunner.initSession(browserEndpoint.webSocketDebuggerUrl);
26
+ const webSocketDebuggerUrl = await utils.getCdpAddressForHost(`localhost:${this.chrome.port}`);
27
+ await this.cdpTestRunner.initSession(webSocketDebuggerUrl);
28
28
 
29
29
  registerExitHook(() => this.chrome.kill());
30
30
  }
@@ -4,6 +4,7 @@ const _ = require('lodash');
4
4
 
5
5
  const COMMUNICATION_BUFFER_TIME = 1000;
6
6
  const UI_VERIFICATION_STEPS = ['simple-ui-verification', 'wait-for-simple-ui-verification'];
7
+ const FULL_TIMEOUT_STEP_TYPES = [...UI_VERIFICATION_STEPS, 'custom-validation', 'sfdc-step-login'];
7
8
 
8
9
  class PlaybackTimeoutCalculator {
9
10
  constructor(isDebuggerConnected) {
@@ -41,7 +42,7 @@ class PlaybackTimeoutCalculator {
41
42
  };
42
43
  stepPlayback.setStartTimestamp();
43
44
  const totalStepTime = this.getTotalStepRunTime(stepPlayback);
44
- const currentRetryTimes = [...UI_VERIFICATION_STEPS, 'custom-validation'].includes(stepPlayback.stepType) ? [totalStepTime] : getRetryTimeoutSuggestions(totalStepTime);
45
+ const currentRetryTimes = FULL_TIMEOUT_STEP_TYPES.includes(stepPlayback.stepType) ? [totalStepTime] : getRetryTimeoutSuggestions(totalStepTime);
45
46
  this.resetStepVariables(totalStepTime, currentRetryTimes);
46
47
  stepPlayback.context.data.maxTotalStepTime = totalStepTime;
47
48
  }
@@ -61,6 +62,9 @@ class PlaybackTimeoutCalculator {
61
62
  if (UI_VERIFICATION_STEPS.includes(stepPlayback.stepType)) {
62
63
  fallbackTimeout = stepPlayback.context.config.applitoolsStepTimeout || HALF_HOUR_IN_MS;
63
64
  }
65
+ if (stepPlayback.step.type.startsWith('sfdc-')) {
66
+ fallbackTimeout = stepPlayback.step.defaultTimeout;
67
+ }
64
68
  return (stepPlayback.step.useStepTimeout && stepPlayback.step.stepTimeout) ? stepPlayback.step.stepTimeout : fallbackTimeout;
65
69
  }
66
70
  getTotalStepTimeLeftToPlay(stepPlayback, totalStepTime = this.totalStepTime) {
@@ -0,0 +1,27 @@
1
+ const StepAction = require('./stepAction');
2
+ const { sfdc } = require('../../commons/getSessionPlayerRequire');
3
+
4
+ class SfdcStepAction extends StepAction {
5
+ async performAction() {
6
+ const page = sfdc.sfdcNewSePage(this.driver);
7
+ try {
8
+ const actions = this.context.sfdcTestActions;
9
+ if (actions === undefined) {
10
+ throw new Error('No test actions were compiled');
11
+ }
12
+ await sfdc.sfdcExecute(page, actions);
13
+ return { success: true };
14
+ } catch (err) {
15
+ return {
16
+ success: false,
17
+ reason: err.reason || err.message,
18
+ exception: err,
19
+ shouldRetry: false, // TODO - check this. Our (bFormat) steps are probably not retryable?
20
+ };
21
+ } finally {
22
+ page.releaseObjects();
23
+ }
24
+ }
25
+ }
26
+
27
+ module.exports = SfdcStepAction;
@@ -27,6 +27,7 @@ const CliJsStepAction = require('./cliJsStepAction');
27
27
  const CliConditionStepAction = require('./cliConditionStepAction');
28
28
  const NodePackageStepAction = require('./nodePackageStepAction');
29
29
  const ExtensionOnlyStepAction = require('./extensionOnlyStepAction');
30
+ const SfdcStepAction = require('./sfdcStepAction');
30
31
 
31
32
  function register(stepActionByType, stepActionFactory) {
32
33
  Object.keys(stepActionByType).forEach(type => {
@@ -82,6 +83,17 @@ module.exports = function (driver, stepActionFactory, runMode) {
82
83
  'email-code-step': JsCodeStepAction,
83
84
  'cli-email-code-step': CliJsStepAction,
84
85
  'tdk-hybrid': TdkHybridStepAction,
86
+
87
+ 'sfdc-step-login': SfdcStepAction,
88
+ 'sfdc-step-logout': SfdcStepAction,
89
+ 'sfdc-step-sobjectcreate': SfdcStepAction,
90
+ 'sfdc-step-sobjectdelete': SfdcStepAction,
91
+ 'sfdc-step-findrecord': SfdcStepAction,
92
+ 'sfdc-step-quickaction': SfdcStepAction,
93
+ 'sfdc-step-edit': SfdcStepAction,
94
+ 'sfdc-step-sobjectvalidate': SfdcStepAction,
95
+ 'sfdc-step-launchapp': SfdcStepAction,
96
+ 'sfdc-step-closeconsoletabs': SfdcStepAction,
85
97
  };
86
98
 
87
99
  register(STEP_ACTION_MAPPING, stepActionFactory);
@@ -1,6 +1,7 @@
1
- "use strict";
1
+ /* eslint-disable no-var */
2
+ 'use strict';
2
3
 
3
- const logger = require('../commons/logger').getLogger("webdriver");
4
+ const logger = require('../commons/logger').getLogger('webdriver');
4
5
  const Promise = require('bluebird');
5
6
  const parser = require('ua-parser-js');
6
7
  const desiredCapabilitiesBuilder = require('../commons/testimDesiredCapabilitiesBuilder');
@@ -14,8 +15,7 @@ const featureFlags = require('../commons/featureFlags');
14
15
  const _ = require('lodash');
15
16
 
16
17
  const [LEFT, RIGHT] = [0, 2];
17
- const { extractElementId } = utils;
18
- const httpRequest = require('../commons/httpRequest');
18
+ const { extractElementId, getCdpAddressForHost } = utils;
19
19
  const perf = require('../commons/performance-logger');
20
20
  const { SeleniumPerfStats, SELENIUM_PERF_MARKS } = require('../commons/SeleniumPerfStats');
21
21
 
@@ -34,15 +34,11 @@ const playerUtils = () => {
34
34
 
35
35
  async function getCdpAddress(initResult) {
36
36
  try {
37
- const debuggerAddress = initResult && initResult.value && initResult.value['goog:chromeOptions'] && initResult.value['goog:chromeOptions'].debuggerAddress;
38
- if (!debuggerAddress) {
37
+ const debuggerHost = initResult && initResult.value && initResult.value['goog:chromeOptions'] && initResult.value['goog:chromeOptions'].debuggerAddress;
38
+ if (!debuggerHost) {
39
39
  return undefined;
40
40
  }
41
- const browserEndpoint = await httpRequest.get('http://' + debuggerAddress + '/json/version');
42
- if (!browserEndpoint) {
43
- return undefined;
44
- }
45
- return browserEndpoint.webSocketDebuggerUrl;
41
+ return await getCdpAddressForHost(debuggerHost);
46
42
  } catch (e) {
47
43
  logger.info('Error getting cdpAddress', e);
48
44
  return undefined;
@@ -202,7 +198,10 @@ class WebDriver extends WebDriverApi {
202
198
 
203
199
  switchToLocatedFrame(locatedElement) {
204
200
  return this.getElement(locatedElement)
205
- .tap(el => this.switchToFrame(el.value));
201
+ .then(async el => {
202
+ await this.switchToFrame(el.value);
203
+ return el;
204
+ });
206
205
  }
207
206
 
208
207
  switchToFrame(el) {
@@ -220,7 +219,7 @@ class WebDriver extends WebDriverApi {
220
219
 
221
220
  getElement(locatedElement) {
222
221
  const perfId = this.seleniumPerfStats.markStart(SELENIUM_PERF_MARKS.GET_ELEMENT);
223
- if (typeof locatedElement === "string" || typeof locatedElement === "number") { // support testimId in the meanwhile for backwards compatability
222
+ if (typeof locatedElement === 'string' || typeof locatedElement === 'number') { // support testimId in the meanwhile for backwards compatability
224
223
  return this.getElementBySelector(`[testim_dom_element_id='${locatedElement}']`)
225
224
  .finally(() => this.seleniumPerfStats.markEnd(perfId, SELENIUM_PERF_MARKS.GET_ELEMENT));
226
225
  }
@@ -353,13 +352,13 @@ class WebDriver extends WebDriverApi {
353
352
 
354
353
  function isTextElement(element) {
355
354
  var tagName = element.tagName;
356
- return (tagName === "INPUT" || tagName === "TEXTAREA");
355
+ return (tagName === 'INPUT' || tagName === 'TEXTAREA');
357
356
  }
358
357
 
359
358
  function getFixedTextContent(element) {
360
359
  try { // fix for salesforce's custom-elements
361
360
  if (element.shadowRoot && Object.getOwnPropertyDescriptor(element.ownerDocument.defaultView.Node.prototype,'textContent').get.toString().indexOf('[native code]') === -1) {
362
- return element.shadowRoot.textContent.replace(/(\r\n|\n|\r)/gm, "");
361
+ return element.shadowRoot.textContent.replace(/(\r\n|\n|\r)/gm, '');
363
362
  }
364
363
  } catch (err) { }
365
364
  if (ignoreHiddenTagsText && Array.prototype.some.call(element.children, function (elem) { return elem.hidden; })) {
@@ -386,7 +385,7 @@ class WebDriver extends WebDriverApi {
386
385
  var svgContent = new XMLSerializer().serializeToString(element);
387
386
  copyElement = new DOMParser().parseFromString(svgContent, 'text/html').body.firstChild;
388
387
  }
389
- return clearTitleTags(copyElement).textContent.replace(/(\r\n|\n|\r)/gm, "");
388
+ return clearTitleTags(copyElement).textContent.replace(/(\r\n|\n|\r)/gm, '');
390
389
  } else {
391
390
  return getFixedTextContent(element);
392
391
  }
@@ -429,7 +428,7 @@ class WebDriver extends WebDriverApi {
429
428
  })
430
429
  .catch(err => !(err instanceof IeError), err => {
431
430
  this.seleniumPerfStats.markEnd(perfId, SELENIUM_PERF_MARKS.GET_HTML);
432
- const testimInternalError = Object.assign(new Error(), { success: false, reason: err.toString(), errorType: "internal-error" });
431
+ const testimInternalError = Object.assign(new Error(), { success: false, reason: err.toString(), errorType: 'internal-error' });
433
432
  if (!this.client.requestHandler.sessionID) {
434
433
  // we got here after the driver has been disposed of. It's impossible to run JavaScirpt on the page.
435
434
  testimInternalError.extraInfo = 'Inside getHtml catch and trying to check if in quirks mode - but the session has already terminated';
@@ -438,10 +437,10 @@ class WebDriver extends WebDriverApi {
438
437
  if (!this.isIE()) { // no need to check quirks mode if I'm not in IE
439
438
  throw testimInternalError;
440
439
  }
441
- return this.executeJS(`return navigator.userAgent;`)
440
+ return this.executeJS('return navigator.userAgent;')
442
441
  .catch(() => Promise.reject(testimInternalError))
443
442
  .then(ua => {
444
- const error = this.isUsingUnsupportedCompabilityMode(ua.value) ? this.getIeError("Can’t run test in IE compatibility mode") : testimInternalError;
443
+ const error = this.isUsingUnsupportedCompabilityMode(ua.value) ? this.getIeError('Can’t run test in IE compatibility mode') : testimInternalError;
445
444
  return Promise.reject(error);
446
445
  });
447
446
  });
@@ -527,7 +526,7 @@ class WebDriver extends WebDriverApi {
527
526
  try {
528
527
  return parseInt(parse.browser.major);
529
528
  } catch (err) {
530
- logger.error("failed to get browser version", { err: err });
529
+ logger.error('failed to get browser version', { err: err });
531
530
  return 0;
532
531
  }
533
532
  }
@@ -536,13 +535,13 @@ class WebDriver extends WebDriverApi {
536
535
  function getBrowserName(ua, browserDetails) {
537
536
  var M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
538
537
  if (/trident/i.test(M[1])) {
539
- return "Internet Explorer " + browserDetails.major;
538
+ return 'Internet Explorer ' + browserDetails.major;
540
539
  }
541
540
  if (M[1] === 'Chrome' && ua.match(/\bOPR\/(\d+)/) !== null) {
542
- return "opera";
541
+ return 'opera';
543
542
  }
544
543
  if (M[1] === 'Chrome' && ua.match(/\bEdge|Edg\/(\d+)/) !== null) {
545
- return "edge";
544
+ return 'edge';
546
545
  }
547
546
  M = M[2] ? [M[1], M[2]] : [ua.appName, ua.appVersion, '-?'];
548
547
  var tem = ua.match(/version\/(\d+)/i);
@@ -558,7 +557,7 @@ class WebDriver extends WebDriverApi {
558
557
 
559
558
  return this.executeJS(function () {
560
559
  if (typeof window === 'undefined' || !window.navigator || !window.navigator.userAgent) {
561
- return "unknown";
560
+ return 'unknown';
562
561
  }
563
562
  return window.navigator.userAgent;
564
563
  }).then(userAgent => {
@@ -566,7 +565,7 @@ class WebDriver extends WebDriverApi {
566
565
  const parse = parser(rawUserAgent);
567
566
  const osDetails = parse.os;
568
567
  this.browserAndOS = {
569
- os: osDetails.name + " " + osDetails.version,
568
+ os: osDetails.name + ' ' + osDetails.version,
570
569
  browserMajor: this.getBrowserMajorVersion(parse),
571
570
  browser: getBrowserName(userAgent.value, parse.browser),
572
571
  userAgent: rawUserAgent,
@@ -595,7 +594,7 @@ class WebDriver extends WebDriverApi {
595
594
  !playerUtils().isWithinBounds(-inViewCenter.y, inViewCenter.y, top)) {
596
595
  // [NOTE] the code is not supposed to get here! - using center (0,0) instead of step offsets.
597
596
  // this is a fallback so the action will take place even if for some reason calculation went out of element..
598
- logger.warn("using center as fallback for offset");
597
+ logger.warn('using center as fallback for offset');
599
598
  return this.getMoveActions(0, 0, element);
600
599
  }
601
600
  return this.getMoveActions(left, top, element);
@@ -612,20 +611,20 @@ class WebDriver extends WebDriverApi {
612
611
  const moveActions = this.getRelativeMoveActions(offsets, element);
613
612
  const clickActions = this.getClickActions(actions, button);
614
613
  return this.actions([{
615
- type: "pointer",
616
- id: "mouse",
614
+ type: 'pointer',
615
+ id: 'mouse',
617
616
  parameters: { pointerType: 'mouse' },
618
- actions: moveActions.concat(clickActions)
617
+ actions: moveActions.concat(clickActions),
619
618
  }]).catch(err => {
620
619
  logger.error('tried to use element origin but failed because of visibility, trying absolute', err);
621
620
  const { x, y } = this.computeAbsoluteMovement(offsets);
622
621
  const moveActions = this.getMoveActions(x, y);
623
622
  return this.actions([{
624
- type: "pointer",
625
- id: "mouse",
623
+ type: 'pointer',
624
+ id: 'mouse',
626
625
  parameters: { pointerType: 'mouse' },
627
- actions: moveActions.concat(clickActions)
628
- }])
626
+ actions: moveActions.concat(clickActions),
627
+ }]);
629
628
  });
630
629
  }
631
630
 
@@ -639,7 +638,7 @@ class WebDriver extends WebDriverApi {
639
638
  var getLocatedElement = ${codeSnippets().getLocatedElementCode};
640
639
  var dispatchFocus = ${dispatchFocus.toString()};
641
640
  var doubleClick = ${doubleClick.toString()};
642
- var eventData = ${this.isEdge() ? `JSON.parse(arguments[0])` : `arguments[0]`};
641
+ var eventData = ${this.isEdge() ? 'JSON.parse(arguments[0])' : 'arguments[0]'};
643
642
  var done = arguments[1];
644
643
  return doubleClick.call(null, eventData, done);
645
644
  `, eventData.timeout, eventParam);
@@ -651,8 +650,8 @@ class WebDriver extends WebDriverApi {
651
650
 
652
651
  getClickActionList(types = [], button) {
653
652
  return [{
654
- type: "pointer",
655
- id: "mouse",
653
+ type: 'pointer',
654
+ id: 'mouse',
656
655
  actions: this.getClickActions(types, button)
657
656
  }];
658
657
  }
@@ -740,26 +739,26 @@ class WebDriver extends WebDriverApi {
740
739
 
741
740
  getMoveActions(x = 1, y = 1, origin = 'viewport', duration = 0) {
742
741
  // force x != 0 ,y != 0 because of Safari issues
743
- return [{ type: "pointerMove", duration, x: Math.floor(x) || 1, y: Math.floor(y) || 1, origin }];
742
+ return [{ type: 'pointerMove', duration, x: Math.floor(x) || 1, y: Math.floor(y) || 1, origin }];
744
743
  }
745
744
 
746
745
  moveWithActionsAPI(point) {
747
746
  const actions = this.getMoveActions(point.x, point.y);
748
747
  return this.actions([{
749
- type: "pointer",
750
- id: "mouse",
748
+ type: 'pointer',
749
+ id: 'mouse',
751
750
  actions: actions
752
751
  }]);
753
752
  }
754
753
 
755
754
  moveToElementWithActionsAPI(seleniumElement, offsets) {
756
755
  return this.actions([{
757
- type: "pointer",
758
- id: "mouse",
759
- actions: this.getRelativeMoveActions(offsets, seleniumElement)
756
+ type: 'pointer',
757
+ id: 'mouse',
758
+ actions: this.getRelativeMoveActions(offsets, seleniumElement),
760
759
  }]).catch(err => {
761
760
  logger.error('tried to use element origin but failed because of visibility, trying location', err);
762
- const point = this.computeAbsoluteMovement(offsets)
761
+ const point = this.computeAbsoluteMovement(offsets);
763
762
  return this.moveWithActionsAPI(point);
764
763
  });
765
764
  }
@@ -789,14 +788,14 @@ class WebDriver extends WebDriverApi {
789
788
  const doDrag = this.getMoveActions(xDiff, yDiff, 'pointer', 1);
790
789
  const endDrag = this.getClickActions(['pointerUp'], LEFT);
791
790
  return this.actions([{
792
- type: "pointer",
793
- id: "mouse",
791
+ type: 'pointer',
792
+ id: 'mouse',
794
793
  actions: goToDrag.concat(startDrag).concat(doDrag).concat(endDrag)
795
- }])
794
+ }]);
796
795
  }
797
796
 
798
797
  drag(seleniumElement, targetRect, xElementOffset, yElementOffset, events) {
799
- const { width, height } = targetRect
798
+ const { width, height } = targetRect;
800
799
  const midXRelative = this.isEdge() ? xElementOffset : (xElementOffset - width / 2 + 1);
801
800
  const midYRelative = this.isEdge() ? yElementOffset : (yElementOffset - height / 2);
802
801
  return this.getDragCoordinates(events)
@@ -870,8 +869,8 @@ class WebDriver extends WebDriverApi {
870
869
  const actions = startMovePositionActions.concat(pointerDownActions).concat(moveSequenceActions).concat(endMovePositionActions).concat(pointerUpActions);
871
870
 
872
871
  return this.actions([{
873
- type: "pointer",
874
- id: "mouse",
872
+ type: 'pointer',
873
+ id: 'mouse',
875
874
  actions: actions
876
875
  }]);
877
876
  }
@@ -957,7 +956,7 @@ class WebDriver extends WebDriverApi {
957
956
 
958
957
  getElementRect(target) {
959
958
  let defaultLocation = { width: 0, height: 0, top: 0, left: 0 };
960
- return this.getElementLocation(target).catch((err) => logger.error("error getting element location", { err }))
959
+ return this.getElementLocation(target).catch((err) => logger.error('error getting element location', { err }))
961
960
  .then(loc => {
962
961
  if (loc && loc.value) {
963
962
  return {
@@ -972,9 +971,9 @@ class WebDriver extends WebDriverApi {
972
971
  }
973
972
 
974
973
  end() {
975
- logger.info("delete session", { sessionId: this.getSessionId() });
974
+ logger.info('delete session', { sessionId: this.getSessionId() });
976
975
  if (!this.getSessionId()) {
977
- logger.warn("failed to close session because session is undefined");
976
+ logger.warn('failed to close session because session is undefined');
978
977
  }
979
978
  clearInterval(this.keepAliveTimer);
980
979
  return super.end()
package/runOptions.js CHANGED
@@ -474,7 +474,7 @@ module.exports = {
474
474
  const originalRequire = Module.prototype.require;
475
475
  Module.prototype.require = function requireThatOverridesSessionPlayer(id) {
476
476
  if (id.endsWith('getSessionPlayerRequire')) {
477
- const sessionPlayerPath = path.resolve(fullPlayerPath, 'src/background/sessionPlayerInit.js');
477
+ const sessionPlayerPath = path.resolve(fullPlayerPath, 'src/background/sessionPlayerInit.ts');
478
478
  return originalRequire.call(this, sessionPlayerPath);
479
479
  }
480
480
  if (id === 'rox-alias') {
@@ -1230,6 +1230,8 @@ module.exports = {
1230
1230
  suiteNames: program.intersectWithSuite.length ? [program.intersectWithSuite].flat() : undefined,
1231
1231
  suiteIds: program.intersectWithSuiteId.length ? [program.intersectWithSuiteId].flat() : undefined,
1232
1232
  },
1233
+
1234
+ downloadBrowser: program.downloadBrowser,
1233
1235
  });
1234
1236
  },
1235
1237
  };
package/runner.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  /* eslint-disable no-console */
4
4
  const { CLI_MODE } = require('./commons/constants');
5
- const Promise = require('bluebird');
6
5
  const _ = require('lodash');
7
6
  const { EDITOR_URL } = require('./commons/config');
8
7
  const tunnel = require('./commons/testimTunnel');
@@ -29,7 +28,7 @@ const featureAvailabilityService = require('./commons/featureAvailabilityService
29
28
  const logger = require('./commons/logger').getLogger('runner');
30
29
 
31
30
  function validateCLIRunsAreAllowed(options) {
32
- const hasCliAccess = _(options).get('company.activePlan.premiumFeatures.allowCLI');
31
+ const hasCliAccess = _.get(options, 'company.activePlan.premiumFeatures.allowCLI');
33
32
 
34
33
  if (!hasCliAccess) {
35
34
  const projectId = options.project;
@@ -38,20 +37,18 @@ function validateCLIRunsAreAllowed(options) {
38
37
  }
39
38
  }
40
39
 
41
- function validateProjectQuotaNotDepleted(options) {
40
+ async function validateProjectQuotaNotDepleted(options) {
42
41
  const projectId = options.project;
43
42
 
44
- return servicesApi.getUsageForCurrentBillingPeriod(projectId)
45
- .then(usage => {
46
- const isExecutionBlocked = usage && usage.isExecutionBlocked;
47
- if (!isExecutionBlocked) {
48
- return;
49
- }
50
-
51
- console.error('You have reached the limit of runs for the billing month, please upgrade your plan at https://www.testim.io/upgrade-contact-us?source=cli');
52
- analytics.track(options.authData.uid, 'execution-quota-surpassed', { projectId });
53
- throw new QuotaDepletedError();
54
- });
43
+ const usage = await servicesApi.getUsageForCurrentBillingPeriod(projectId);
44
+ const isExecutionBlocked = usage && usage.isExecutionBlocked;
45
+ if (!isExecutionBlocked) {
46
+ return;
47
+ }
48
+
49
+ console.error('You have reached the limit of runs for the billing month, please upgrade your plan at https://www.testim.io/upgrade-contact-us?source=cli');
50
+ analytics.track(options.authData.uid, 'execution-quota-surpassed', { projectId });
51
+ throw new QuotaDepletedError();
55
52
  }
56
53
 
57
54
  function validateOptionsForCompany(options, company) {
@@ -60,26 +57,27 @@ function validateOptionsForCompany(options, company) {
60
57
  return;
61
58
  }
62
59
 
63
- const companyRetention = _(company).get('activePlan.premiumFeatures.resultRetention');
60
+ const companyRetention = _.get(company, 'activePlan.premiumFeatures.resultRetention');
64
61
  if (optionsRetention > companyRetention) {
65
62
  throw new ArgError(`Retention days (${optionsRetention}) cannot be greater than the company's retention days (${companyRetention}). Run aborted`);
66
63
  }
67
64
  }
68
65
 
69
- function validateCliAccount(options) {
66
+ async function validateCliAccount(options) {
70
67
  if (options.lightweightMode && options.lightweightMode.disableQuotaBlocking) {
71
- return Promise.resolve();
68
+ return;
72
69
  }
73
- return Promise.all([
74
- validateProjectQuotaNotDepleted(options),
75
- validateCLIRunsAreAllowed(options),
76
- ]).catch(err => {
70
+ try {
71
+ await Promise.all([
72
+ validateProjectQuotaNotDepleted(options),
73
+ validateCLIRunsAreAllowed(options),
74
+ ]);
75
+ } catch (err) {
77
76
  if (err instanceof ArgError || err instanceof QuotaDepletedError) {
78
- return Promise.reject(err);
77
+ throw err;
79
78
  }
80
79
  logger.error('could not validate cli account', { err });
81
- return undefined;
82
- });
80
+ }
83
81
  }
84
82
 
85
83
  function analyticsIdentify(projectId) {
@@ -125,10 +123,10 @@ function setCompany(options, company) {
125
123
  if (onprem) {
126
124
  const { mode, extensionPath, ext, playerPath } = options;
127
125
  if ([CLI_MODE.SELENIUM].includes(mode) && !playerPath) {
128
- return Promise.reject(new ArgError('in selenium on prem mode --player-path must be provided'));
126
+ throw new ArgError('in selenium on prem mode --player-path must be provided');
129
127
  }
130
128
  if (mode === 'extension' && !extensionPath && !ext) {
131
- return Promise.reject(new ArgError('In extension on prem mode --ext or --extension-path must be provided'));
129
+ throw new ArgError('In extension on prem mode --ext or --extension-path must be provided');
132
130
  }
133
131
  }
134
132
  const isPOC = Boolean(activePlan.isPoc);
@@ -153,16 +151,14 @@ function setCompany(options, company) {
153
151
  isStartUp,
154
152
  activePlan,
155
153
  };
156
- return undefined;
157
154
  }
158
155
 
159
156
  function setSystemInfo(options, editorConfig) {
160
157
  if (EDITOR_URL) {
161
158
  options.editorUrl = EDITOR_URL;
162
- return Promise.resolve();
159
+ return;
163
160
  }
164
161
  options.editorUrl = editorConfig.editorUrl;
165
- return undefined;
166
162
  }
167
163
 
168
164
  function setAllGrids(options, allGrids) {
@@ -198,7 +194,7 @@ async function setMockNetworkRules(options) {
198
194
  }
199
195
  }
200
196
 
201
- function runRunner(options, customExtensionLocalLocation) {
197
+ async function runRunner(options, customExtensionLocalLocation) {
202
198
  perf.log('in runner.js runRunner');
203
199
 
204
200
  const { project, remoteRunId, useLocalChromeDriver, useChromeLauncher } = options;
@@ -210,15 +206,21 @@ function runRunner(options, customExtensionLocalLocation) {
210
206
  npmDriver.checkNpmVersion();
211
207
  perf.log('in runner.js after checkNpmVersion');
212
208
 
213
- return validateCliAccount(options)
214
- .log('in runRunner before tunnel.connect')
215
- .then(() => tunnel.connect(options))
216
- .log('in runRunner after tunnel.connect')
217
- .then(() => new TestPlanRunner(customExtensionLocalLocation).run(options))
218
- .log('before tunnel.disconnect')
219
- .tap(() => tunnel.disconnect(options))
220
- .tap(() => gridService.keepAlive.end(project))
221
- .log('after tunnel.disconnect and gridService.keepAlive.end');
209
+ await validateCliAccount(options);
210
+
211
+ perf.log('in runRunner before tunnel.connect');
212
+ await tunnel.connect(options);
213
+ perf.log('in runRunner after tunnel.connect');
214
+
215
+ const testPlanRunner = new TestPlanRunner(customExtensionLocalLocation);
216
+ const results = await testPlanRunner.run(options);
217
+
218
+ perf.log('before tunnel.disconnect');
219
+ await tunnel.disconnect(options);
220
+ await gridService.keepAlive.end(project);
221
+ perf.log('after tunnel.disconnect and gridService.keepAlive.end');
222
+
223
+ return results;
222
224
  }
223
225
 
224
226
  function showFreeGridRunWarningIfNeeded(options) {
@@ -237,7 +239,7 @@ function showFreeGridRunWarningIfNeeded(options) {
237
239
  * - Reporting the user to analytics
238
240
  * - Authenticating the user and exchanging their token for a jwt
239
241
  * - Sets the grids for the company and validates the user has permission to run the CLI
240
- * @param {Object} options - the run options rpassed to the CLI, namely the project and token
242
+ * @param {Object} options - the run options passed to the CLI, namely the project and token
241
243
  */
242
244
  async function init(options) {
243
245
  perf.log('start runner init');
@@ -273,6 +275,7 @@ async function init(options) {
273
275
  }
274
276
 
275
277
  if (options.lightweightMode && options.lightweightMode.type === 'turboMode') {
278
+ // eslint-disable-next-line max-len
276
279
  console.log('\nTurbo mode will ignore step delays. Test artifacts like screenshots and logs will only be saved for failed runs. For more information see our docs: https://help.testim.io/docs/turbo-mode');
277
280
  }
278
281
 
@@ -288,5 +291,5 @@ async function init(options) {
288
291
 
289
292
  module.exports = {
290
293
  run: runRunner,
291
- init: Promise.method(init),
294
+ init,
292
295
  };
package/testRunHandler.js CHANGED
@@ -520,7 +520,10 @@ class TestRun {
520
520
  waitForTestEnd();
521
521
  }
522
522
  })
523
- .tap(() => this.onCompletedCleanup())
523
+ .then(async res => {
524
+ await this.onCompletedCleanup();
525
+ return res;
526
+ })
524
527
  .finally(() => onConnected && !this._options.disableSockets && testResultService.off('socket-connected', onConnected));
525
528
  }
526
529