@testrevolution/bugbug-cli 3.1.0 → 2022.3.2-9.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/README.md CHANGED
@@ -10,20 +10,20 @@ https://bugbug.io
10
10
 
11
11
  ### CLI Installation
12
12
 
13
- ```
13
+ ```bash
14
14
  npm install -g @testrevolution/bugbug-cli
15
15
  ```
16
16
 
17
17
  ### Configuration
18
18
 
19
- ```
19
+ ```bash
20
20
  bugbug config set-token API_TOKEN_FROM_WEBAPP_PROJECT_SETTINGS
21
21
  ```
22
22
 
23
23
 
24
24
  ### Available commands
25
25
 
26
- ```
26
+ ```bash
27
27
  bugbug help
28
28
 
29
29
  bugbug config <option>
@@ -32,22 +32,25 @@ bugbug config <option>
32
32
 
33
33
  bugbug remote <option>
34
34
 
35
- * list [test|suite] [--nowait] [--noprogress] [--debug]
36
- * run [test|suite] <string:testId|suiteId> [--nowait] [--noprogress] [--debug] [--with-details]
35
+ options:
36
+ * list [test|suite|profile] [--nowait] [--noprogress] [--debug]
37
+ * run [test|suite] <string:testId|suiteId> [--nowait] [--noprogress] [--debug] [--with-details] [--profile] [--variable]
37
38
  * status [test|suite] <string:testRunId|suiteRunId> [--noprogress] [--debug]
38
39
  * result [test|suite] <string:testRunId|suiteRunId> [--noprogress] [--debug] [--with-details]
39
40
 
40
41
  optional flags:
41
- * --nowait - exit immediately, don't wait for result
42
+ * --debug - show more data (like raw API response)
42
43
  * --noprogress - don't show progress spinner
44
+ * --nowait - exit immediately, don't wait for result
45
+ * --profile <string:"profile name"> - run with specific profile
46
+ * --variable <string:"varName=varValue"> - overrider variable during single run
43
47
  * --with-details - show result with details
44
- * --debug - show more data (like raw API response)
45
48
  ```
46
49
 
47
50
  Run test:
48
51
 
49
52
  ```bash
50
- $ bugbug remote run test ubzwl45t
51
- ℹ Queued (testRunId: sbglfg05)
53
+ bugbug remote run test f95f9f55-f1bf-4e2d-ab27-9ef4752ebbfb
54
+ ℹ Queued (testRunId: 2d6401a7-de84-4785-8fa2-4227b3330a8e)
52
55
  ✔ Status: passed
53
56
  ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@testrevolution/bugbug-cli",
3
3
  "description": "BugBug CLI",
4
- "version": "3.1.0",
4
+ "version": "2022.3.29.0",
5
5
  "keywords": [
6
6
  "automation",
7
7
  "cli",
@@ -32,7 +32,7 @@
32
32
  "pub": "NODE_ENV=production npm publish --access public --loglevel verbose"
33
33
  },
34
34
  "engines": {
35
- "node": ">= 11.14"
35
+ "node": ">= 14.10"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@babel/plugin-proposal-throw-expressions": "^7.14.5",
@@ -46,4 +46,4 @@
46
46
  "jest": "^27.5.1"
47
47
  },
48
48
  "author": "TestRevolution sp. z o.o."
49
- }
49
+ }
@@ -222,7 +222,7 @@ describe('commands module', () => {
222
222
  noprogress: true,
223
223
  };
224
224
  await remoteCommand(args);
225
- expect(runFunMock).toHaveBeenCalledWith(id, undefined, {
225
+ expect(runFunMock).toHaveBeenCalledWith(id, undefined, [], {
226
226
  noprogress: true,
227
227
  nowait: false,
228
228
  withDetails: false,
@@ -241,7 +241,25 @@ describe('commands module', () => {
241
241
  profile: profileName,
242
242
  };
243
243
  await remoteCommand(args);
244
- expect(runFunMock).toHaveBeenCalledWith(id, profileName, {
244
+ expect(runFunMock).toHaveBeenCalledWith(id, profileName, [], {
245
+ noprogress: true,
246
+ nowait: false,
247
+ withDetails: false,
248
+ });
249
+ });
250
+
251
+ it('run test action with variable override flags should call runTest function', async () => {
252
+ const runFunMock = jest.fn();
253
+ remoteCommand.__RewireAPI__.__Rewire__('runTest', runFunMock);
254
+
255
+ const id = '999';
256
+ const args = {
257
+ _: ['remote', 'run', 'test', id],
258
+ noprogress: true,
259
+ variable: ['var=val', 'var2=val2'],
260
+ };
261
+ await remoteCommand(args);
262
+ expect(runFunMock).toHaveBeenCalledWith(id, undefined, [{ key: 'var', value: 'val' }, { key: 'var2', value: 'val2' }], {
245
263
  noprogress: true,
246
264
  nowait: false,
247
265
  withDetails: false,
@@ -259,7 +277,7 @@ describe('commands module', () => {
259
277
  noprogress: true,
260
278
  };
261
279
  await remoteCommand(args);
262
- expect(runFunMock).toHaveBeenCalledWith(id, undefined, {
280
+ expect(runFunMock).toHaveBeenCalledWith(id, undefined, [], {
263
281
  noprogress: true,
264
282
  nowait: true,
265
283
  withDetails: false,
@@ -277,7 +295,7 @@ describe('commands module', () => {
277
295
  noprogress: true,
278
296
  };
279
297
  await remoteCommand(args);
280
- expect(runFunMock).toHaveBeenCalledWith(id, undefined, {
298
+ expect(runFunMock).toHaveBeenCalledWith(id, undefined, [], {
281
299
  noprogress: true,
282
300
  nowait: true,
283
301
  withDetails: false,
@@ -294,7 +312,7 @@ describe('commands module', () => {
294
312
  'with-details': true,
295
313
  };
296
314
  await remoteCommand(args);
297
- expect(runFunMock).toHaveBeenCalledWith(id, undefined, {
315
+ expect(runFunMock).toHaveBeenCalledWith(id, undefined, [], {
298
316
  noprogress: false,
299
317
  nowait: false,
300
318
  withDetails: true,
@@ -311,7 +329,25 @@ describe('commands module', () => {
311
329
  noprogress: true,
312
330
  };
313
331
  await remoteCommand(args);
314
- expect(runFunMock).toHaveBeenCalledWith(id, undefined, {
332
+ expect(runFunMock).toHaveBeenCalledWith(id, undefined, [], {
333
+ noprogress: true,
334
+ nowait: false,
335
+ withDetails: false,
336
+ });
337
+ });
338
+
339
+ it('run suite action with variable override flags should call runSuite function', async () => {
340
+ const runFunMock = jest.fn();
341
+ remoteCommand.__RewireAPI__.__Rewire__('runSuite', runFunMock);
342
+
343
+ const id = '999';
344
+ const args = {
345
+ _: ['remote', 'run', 'suite', id],
346
+ noprogress: true,
347
+ variable: ['var=val', 'var2=val2'],
348
+ };
349
+ await remoteCommand(args);
350
+ expect(runFunMock).toHaveBeenCalledWith(id, undefined, [{ key: 'var', value: 'val' }, { key: 'var2', value: 'val2' }], {
315
351
  noprogress: true,
316
352
  nowait: false,
317
353
  withDetails: false,
@@ -330,7 +366,7 @@ describe('commands module', () => {
330
366
  undefined,
331
367
  };
332
368
  await remoteCommand(args);
333
- expect(runFunMock).toHaveBeenCalledWith(id, undefined, {
369
+ expect(runFunMock).toHaveBeenCalledWith(id, undefined, [], {
334
370
  noprogress: true,
335
371
  nowait: true,
336
372
  withDetails: false,
@@ -491,6 +527,7 @@ describe('commands module', () => {
491
527
  data: {
492
528
  suite_id: id,
493
529
  profile_name: undefined,
530
+ variables: [],
494
531
  },
495
532
  params: {},
496
533
  headers: { Authorization: `Token ${configValue.token}` },
@@ -516,6 +553,7 @@ describe('commands module', () => {
516
553
  data: {
517
554
  suite_id: id,
518
555
  profile_name: profileName,
556
+ variables: [],
519
557
  },
520
558
  params: {},
521
559
  headers: { Authorization: `Token ${configValue.token}` },
@@ -569,7 +607,7 @@ describe('commands module', () => {
569
607
  method: 'POST',
570
608
  url: 'https://bugbug.io/test-api/v1/testruns/',
571
609
  data: {
572
- test_id: id, profile_name: undefined,
610
+ test_id: id, profile_name: undefined, variables: [],
573
611
  },
574
612
  params: {},
575
613
  headers: { Authorization: `Token ${configValue.token}` },
@@ -594,7 +632,7 @@ describe('commands module', () => {
594
632
  method: 'POST',
595
633
  url: 'https://bugbug.io/test-api/v1/testruns/',
596
634
  data: {
597
- test_id: id, profile_name: profileName,
635
+ test_id: id, profile_name: profileName, variables: [],
598
636
  },
599
637
  params: {},
600
638
  headers: { Authorization: `Token ${configValue.token}` },
@@ -620,7 +658,7 @@ describe('commands module', () => {
620
658
  method: 'POST',
621
659
  url: 'https://bugbug.io/test-api/v1/testruns/',
622
660
  data: {
623
- test_id: id, profile_name: undefined,
661
+ test_id: id, profile_name: undefined, variables: [],
624
662
  },
625
663
  params: {},
626
664
  headers: { Authorization: `Token ${configValue.token}` },
@@ -50,45 +50,39 @@ describe('utils module', () => {
50
50
  it('printStatus should console.log when success', async () => {
51
51
  const consoleLogSpyOn = jest.spyOn(console, 'log').mockImplementation();
52
52
  const spinner = getSpinner(true);
53
- print.printStatus(spinner, 'success', 'https://bugbug.io/tests');
53
+ print.printStatus(spinner, 'success');
54
54
 
55
55
  expect(consoleLogSpyOn).toHaveBeenCalledWith('Status: success');
56
56
  });
57
57
 
58
58
  it('printStatus should console.warn when failed', async () => {
59
59
  const consoleErrorSpyOn = jest.spyOn(console, 'error').mockImplementation();
60
- const consoleLogSpyOn = jest.spyOn(console, 'log').mockImplementation();
61
60
  const spinner = getSpinner(true);
62
- print.printStatus(spinner, 'failed', 'https://bugbug.io/tests');
61
+ print.printStatus(spinner, 'failed');
63
62
 
64
- expect(consoleLogSpyOn).toHaveBeenCalledWith('https://bugbug.io/tests');
65
63
  expect(consoleErrorSpyOn).toHaveBeenCalledWith('Status: failed');
66
64
  });
67
65
 
68
66
  it('printStatus should console.warn when error', async () => {
69
67
  const consoleErrorSpyOn = jest.spyOn(console, 'error').mockImplementation();
70
- const consoleLogSpyOn = jest.spyOn(console, 'log').mockImplementation();
71
68
  const spinner = getSpinner(true);
72
- print.printStatus(spinner, 'error', 'https://bugbug.io/tests');
69
+ print.printStatus(spinner, 'error');
73
70
 
74
- expect(consoleLogSpyOn).toHaveBeenCalledWith('https://bugbug.io/tests');
75
71
  expect(consoleErrorSpyOn).toHaveBeenCalledWith('Status: error');
76
72
  });
77
73
 
78
74
  it('printStatus should console.warn when stopped', async () => {
79
75
  const consoleErrorSpyOn = jest.spyOn(console, 'error').mockImplementation();
80
- const consoleLogSpyOn = jest.spyOn(console, 'log').mockImplementation();
81
76
  const spinner = getSpinner(true);
82
- print.printStatus(spinner, 'stopped', 'https://bugbug.io/tests');
77
+ print.printStatus(spinner, 'stopped');
83
78
 
84
- expect(consoleLogSpyOn).toHaveBeenCalledWith('https://bugbug.io/tests');
85
79
  expect(consoleErrorSpyOn).toHaveBeenCalledWith('Status: stopped');
86
80
  });
87
81
 
88
82
  it('printStatus should console.log when other status', async () => {
89
83
  const consoleLogSpyOn = jest.spyOn(console, 'log').mockImplementation();
90
84
  const spinner = getSpinner(true);
91
- print.printStatus(spinner, '21', 'https://bugbug.io/tests');
85
+ print.printStatus(spinner, '21');
92
86
 
93
87
  expect(consoleLogSpyOn).toHaveBeenCalledWith('Status: 21');
94
88
  });
@@ -114,6 +108,33 @@ describe('utils module', () => {
114
108
  it('testing getExitCode function: 3 stopped exit code', async () => {
115
109
  expect(await helper.getExitCode('stopped')).toBe(3);
116
110
  });
111
+
112
+ it('testing parseVariables function: empty data', async () => {
113
+ expect(await helper.parseVariables(undefined)).toStrictEqual([]);
114
+ expect(await helper.parseVariables('')).toStrictEqual([]);
115
+ expect(await helper.parseVariables([])).toStrictEqual([]);
116
+ });
117
+
118
+ it('testing parseVariables function: single variable', async () => {
119
+ expect(await helper.parseVariables('var=val')).toStrictEqual([
120
+ { key: 'var', value: 'val' },
121
+ ]);
122
+ });
123
+
124
+ it('testing parseVariables function: = in variable value', async () => {
125
+ expect(await helper.parseVariables('var=val=ue')).toStrictEqual([
126
+ { key: 'var', value: 'val=ue' },
127
+ ]);
128
+ expect(await helper.parseVariables(['var=val=ue', 'var2=val=ue2'])).toStrictEqual([
129
+ { key: 'var', value: 'val=ue' }, { key: 'var2', value: 'val=ue2' },
130
+ ]);
131
+ });
132
+
133
+ it('testing parseVariables function: multiple variable', async () => {
134
+ expect(await helper.parseVariables(['var=val', 'var2=val2'])).toStrictEqual([
135
+ { key: 'var', value: 'val' }, { key: 'var2', value: 'val2' },
136
+ ]);
137
+ });
117
138
  });
118
139
 
119
140
  describe('config', () => {
@@ -19,16 +19,17 @@ const help = {
19
19
 
20
20
  options:
21
21
  * list [test|suite|profile] [--nowait] [--noprogress] [--debug]
22
- * run [test|suite] <string:testId|suiteId> [--nowait] [--noprogress] [--debug] [--with-details] [--profile]
22
+ * run [test|suite] <string:testId|suiteId> [--nowait] [--noprogress] [--debug] [--with-details] [--profile] [--variable]
23
23
  * status [test|suite] <string:testRunId|suiteRunId> [--noprogress] [--debug]
24
24
  * result [test|suite] <string:testRunId|suiteRunId> [--noprogress] [--debug] [--with-details]
25
25
 
26
26
  optional flags:
27
- * --profile=<string:"profile name"> - run with specific profile
28
- * --nowait - exit immediately, don't wait for result
27
+ * --debug - show more data (like raw API response)
29
28
  * --noprogress - don't show progress spinner
29
+ * --nowait - exit immediately, don't wait for result
30
+ * --profile <string:"profile name"> - run with specific profile
31
+ * --variable <string:"varName=varValue"> - overrider variable during single run
30
32
  * --with-details - show result with details
31
- * --debug - show more data (like raw API response)
32
33
  `,
33
34
  };
34
35
 
@@ -3,7 +3,7 @@ const { format } = require('util');
3
3
  const settings = require('../settings');
4
4
  const { getSpinner } = require('../utils/spinner');
5
5
  const { getConfigValue } = require('../utils/config');
6
- const { getExitCode } = require('../utils/helper');
6
+ const { getExitCode, parseVariables, printErrorResponse } = require('../utils/helper');
7
7
  const { apiCall, apiCallPoll } = require('../utils/api');
8
8
  const {
9
9
  printList,
@@ -28,7 +28,7 @@ const getList = async (type, query, extraParams) => {
28
28
  return getExitCode(true);
29
29
  }
30
30
  } catch (error) {
31
- spinner.fail(error.toString());
31
+ await printErrorResponse(spinner, error);
32
32
  }
33
33
  return getExitCode(false);
34
34
  };
@@ -50,7 +50,7 @@ const getResult = async (type, id, extraParams) => {
50
50
  return getExitCode(result.data.status);
51
51
  }
52
52
  } catch (error) {
53
- spinner.fail(error.toString());
53
+ await printErrorResponse(spinner, error);
54
54
  }
55
55
 
56
56
  return getExitCode(false);
@@ -70,14 +70,14 @@ const checkStatus = async (type, id, extraParams) => {
70
70
  } else {
71
71
  response = await apiCallPoll(path, route.method, {}, spinner, 0);
72
72
  }
73
- if (withDetails) {
73
+ if (withDetails || settings.FAILED_STATUS.includes(response.data.status)) {
74
74
  await getResult(type, id, extraParams);
75
75
  } else {
76
- printStatus(spinner, response.data.status, response.data.webappUrl);
76
+ printStatus(spinner, response.data.status);
77
77
  }
78
78
  exitCode = getExitCode(response.data.status);
79
79
  } catch (error) {
80
- spinner.fail(error.toString());
80
+ await printErrorResponse(spinner, error);
81
81
  await getResult(type, id, extraParams);
82
82
  exitCode = getExitCode(false);
83
83
  }
@@ -99,17 +99,17 @@ const run = async (type, data, extraParams) => {
99
99
  return getExitCode(true);
100
100
  }
101
101
  } catch (error) {
102
- spinner.fail(error.toString());
102
+ await printErrorResponse(spinner, error);
103
103
  }
104
104
  return getExitCode(false);
105
105
  };
106
106
 
107
- const runTest = async (testId, profileName, extraParams) => run(
108
- settings.TYPE_TEST, { test_id: testId, profile_name: profileName }, extraParams,
107
+ const runTest = async (testId, profileName, variables, extraParams) => run(
108
+ settings.TYPE_TEST, { test_id: testId, profile_name: profileName, variables }, extraParams,
109
109
  );
110
110
 
111
- const runSuite = async (suiteId, profileName, extraParams) => run(
112
- settings.TYPE_SUITE, { suite_id: suiteId, profile_name: profileName }, extraParams,
111
+ const runSuite = async (suiteId, profileName, variables, extraParams) => run(
112
+ settings.TYPE_SUITE, { suite_id: suiteId, profile_name: profileName, variables }, extraParams,
113
113
  );
114
114
 
115
115
  const checkTokenConfig = async () => {
@@ -131,6 +131,7 @@ module.exports = async (args) => {
131
131
  const subCmd = args._[1].toLowerCase();
132
132
  const objectType = args._[2].toLowerCase();
133
133
  const id = args._[3];
134
+ const parsedVariables = await parseVariables(args.variable);
134
135
  const extraParams = {
135
136
  nowait: args.nowait || false,
136
137
  noprogress: args.noprogress || false,
@@ -152,9 +153,9 @@ module.exports = async (args) => {
152
153
  break;
153
154
  case settings.ACTION_RUN:
154
155
  if (objectType === settings.TYPE_TEST) {
155
- exitCode = await runTest(id, args.profile, extraParams);
156
+ exitCode = await runTest(id, args.profile, parsedVariables, extraParams);
156
157
  } else if (objectType === settings.TYPE_SUITE) {
157
- exitCode = await runSuite(id, args.profile, extraParams);
158
+ exitCode = await runSuite(id, args.profile, parsedVariables, extraParams);
158
159
  }
159
160
  break;
160
161
  case settings.ACTION_STATUS:
@@ -5,6 +5,32 @@ const getExitCode = async (status) => {
5
5
  return exitCode === undefined ? 1 : exitCode;
6
6
  };
7
7
 
8
+ const parseVariables = async (variablesData) => {
9
+ if (!variablesData) {
10
+ return [];
11
+ }
12
+ let variablesArray = variablesData;
13
+ if (typeof (variablesData) === 'string') {
14
+ variablesArray = [variablesData];
15
+ }
16
+ return variablesArray.map((variable) => {
17
+ const [variableKey, ...variableValue] = variable.split('=');
18
+ return { key: variableKey, value: variableValue.join('=') };
19
+ });
20
+ };
21
+
22
+ const printErrorResponse = async (spinner, error) => {
23
+ if (
24
+ error?.response.status === 400
25
+ && error.response?.data[0]?.message) {
26
+ spinner.fail(error.response.data[0].message);
27
+ } else {
28
+ spinner.fail(error.toString());
29
+ }
30
+ };
31
+
8
32
  module.exports = {
9
33
  getExitCode,
34
+ parseVariables,
35
+ printErrorResponse,
10
36
  };
@@ -2,11 +2,8 @@ const util = require('util');
2
2
  const { Table, printTable } = require('console-table-printer');
3
3
  const settings = require('../settings');
4
4
 
5
- const printStatus = (spinner, status, webappUrl) => {
5
+ const printStatus = (spinner, status) => {
6
6
  if (settings.FAILED_STATUS.includes(status)) {
7
- if (webappUrl) {
8
- spinner.info(webappUrl);
9
- }
10
7
  spinner.fail(`Status: ${status}`);
11
8
  } else {
12
9
  spinner.succeed(`Status: ${status}`);
@@ -33,6 +30,8 @@ const printTestRunInfo = (spinner, result, withDetails = false) => {
33
30
  spinner.info(`Sequence: ${result.sequence}`);
34
31
  spinner.info(`Created: ${result.queued}`);
35
32
  spinner.info(`Duration: ${result.duration}`);
33
+ } else if (settings.FAILED_STATUS.includes(result.status)) {
34
+ spinner.info(result.webappUrl);
36
35
  }
37
36
  printStatus(spinner, result.status);
38
37
 
@@ -59,6 +58,8 @@ const printSuiteRunInfo = (spinner, result, withDetails = false) => {
59
58
  spinner.info(`Sequence: ${result.sequence}`);
60
59
  spinner.info(`Created: ${result.queued}`);
61
60
  spinner.info(`Duration: ${result.duration}`);
61
+ } else if (settings.FAILED_STATUS.includes(result.status)) {
62
+ spinner.info(result.webappUrl);
62
63
  }
63
64
  printStatus(spinner, result.status);
64
65