@datadog/datadog-ci 1.7.1 → 1.7.3-alpha

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.
Files changed (51) hide show
  1. package/README.md +26 -0
  2. package/dist/commands/dsyms/interfaces.d.ts +1 -0
  3. package/dist/commands/dsyms/interfaces.js +15 -4
  4. package/dist/commands/dsyms/interfaces.js.map +1 -1
  5. package/dist/commands/git-metadata/interfaces.d.ts +1 -0
  6. package/dist/commands/git-metadata/interfaces.js +15 -4
  7. package/dist/commands/git-metadata/interfaces.js.map +1 -1
  8. package/dist/commands/sourcemaps/interfaces.d.ts +1 -0
  9. package/dist/commands/sourcemaps/interfaces.js +32 -20
  10. package/dist/commands/sourcemaps/interfaces.js.map +1 -1
  11. package/dist/commands/synthetics/__tests__/api.test.js +5 -13
  12. package/dist/commands/synthetics/__tests__/api.test.js.map +1 -1
  13. package/dist/commands/synthetics/__tests__/cli.test.js +49 -211
  14. package/dist/commands/synthetics/__tests__/cli.test.js.map +1 -1
  15. package/dist/commands/synthetics/__tests__/fixtures.d.ts +10 -15
  16. package/dist/commands/synthetics/__tests__/fixtures.js +99 -56
  17. package/dist/commands/synthetics/__tests__/fixtures.js.map +1 -1
  18. package/dist/commands/synthetics/__tests__/reporters/default.test.js +14 -26
  19. package/dist/commands/synthetics/__tests__/reporters/default.test.js.map +1 -1
  20. package/dist/commands/synthetics/__tests__/reporters/junit.test.js +1 -2
  21. package/dist/commands/synthetics/__tests__/reporters/junit.test.js.map +1 -1
  22. package/dist/commands/synthetics/__tests__/run-test.test.js +35 -3
  23. package/dist/commands/synthetics/__tests__/run-test.test.js.map +1 -1
  24. package/dist/commands/synthetics/__tests__/utils.test.js +269 -225
  25. package/dist/commands/synthetics/__tests__/utils.test.js.map +1 -1
  26. package/dist/commands/synthetics/__tests__/websocket.test.js +2 -0
  27. package/dist/commands/synthetics/__tests__/websocket.test.js.map +1 -1
  28. package/dist/commands/synthetics/api.d.ts +3 -4
  29. package/dist/commands/synthetics/api.js +13 -3
  30. package/dist/commands/synthetics/api.js.map +1 -1
  31. package/dist/commands/synthetics/command.d.ts +1 -9
  32. package/dist/commands/synthetics/command.js +7 -66
  33. package/dist/commands/synthetics/command.js.map +1 -1
  34. package/dist/commands/synthetics/errors.d.ts +1 -1
  35. package/dist/commands/synthetics/errors.js +1 -1
  36. package/dist/commands/synthetics/errors.js.map +1 -1
  37. package/dist/commands/synthetics/interfaces.d.ts +28 -31
  38. package/dist/commands/synthetics/interfaces.js +1 -7
  39. package/dist/commands/synthetics/interfaces.js.map +1 -1
  40. package/dist/commands/synthetics/reporters/default.d.ts +3 -3
  41. package/dist/commands/synthetics/reporters/default.js +35 -19
  42. package/dist/commands/synthetics/reporters/default.js.map +1 -1
  43. package/dist/commands/synthetics/reporters/junit.js +4 -5
  44. package/dist/commands/synthetics/reporters/junit.js.map +1 -1
  45. package/dist/commands/synthetics/run-test.d.ts +2 -3
  46. package/dist/commands/synthetics/run-test.js +13 -9
  47. package/dist/commands/synthetics/run-test.js.map +1 -1
  48. package/dist/commands/synthetics/utils.d.ts +19 -5
  49. package/dist/commands/synthetics/utils.js +118 -104
  50. package/dist/commands/synthetics/utils.js.map +1 -1
  51. package/package.json +2 -1
@@ -42,7 +42,11 @@ const api_1 = require("../api");
42
42
  const errors_1 = require("../errors");
43
43
  const interfaces_1 = require("../interfaces");
44
44
  const utils = __importStar(require("../utils"));
45
+ const command_1 = require("../command");
45
46
  const fixtures_1 = require("./fixtures");
47
+ beforeEach(() => {
48
+ jest.restoreAllMocks();
49
+ });
46
50
  describe('utils', () => {
47
51
  const apiConfiguration = {
48
52
  apiKey: '123',
@@ -69,19 +73,11 @@ describe('utils', () => {
69
73
  describe('runTest', () => {
70
74
  const fakeId = '123-456-789';
71
75
  const fakeTrigger = {
72
- results: [],
73
- triggered_check_ids: [fakeId],
76
+ batch_id: 'bid',
77
+ locations: [],
74
78
  };
75
- afterAll(() => {
76
- jest.clearAllMocks();
77
- });
78
79
  test('should run test', () => __awaiter(void 0, void 0, void 0, function* () {
79
- const axiosMock = jest.spyOn(axios_1.default, 'create');
80
- axiosMock.mockImplementation((() => (e) => {
81
- if (e.url === '/synthetics/tests/trigger/ci') {
82
- return { data: fakeTrigger };
83
- }
84
- }));
80
+ jest.spyOn(api, 'triggerTests').mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return fakeTrigger; }));
85
81
  const output = yield utils.runTests(api, [{ public_id: fakeId, executionRule: interfaces_1.ExecutionRule.NON_BLOCKING }]);
86
82
  expect(output).toEqual(fakeTrigger);
87
83
  }));
@@ -120,12 +116,7 @@ describe('utils', () => {
120
116
  expect(headersMetadataSpy).toHaveBeenCalledWith(expect.objectContaining({ 'X-Trigger-App': 'unit_test' }));
121
117
  }));
122
118
  test('should run test with publicId from url', () => __awaiter(void 0, void 0, void 0, function* () {
123
- const axiosMock = jest.spyOn(axios_1.default, 'create');
124
- axiosMock.mockImplementation((() => (e) => {
125
- if (e.url === '/synthetics/tests/trigger/ci') {
126
- return { data: fakeTrigger };
127
- }
128
- }));
119
+ jest.spyOn(api, 'triggerTests').mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return fakeTrigger; }));
129
120
  const output = yield utils.runTests(api, [
130
121
  {
131
122
  executionRule: interfaces_1.ExecutionRule.NON_BLOCKING,
@@ -143,11 +134,9 @@ describe('utils', () => {
143
134
  status: 502,
144
135
  },
145
136
  });
146
- const requestMock = jest.fn();
147
- requestMock.mockImplementation(() => {
137
+ jest.spyOn(api, 'triggerTests').mockImplementation(() => {
148
138
  throw serverError;
149
139
  });
150
- jest.spyOn(axios_1.default, 'create').mockImplementation((() => requestMock));
151
140
  yield expect(utils.runTests(api, [{ public_id: fakeId, executionRule: interfaces_1.ExecutionRule.NON_BLOCKING }])).rejects.toThrow(/Failed to trigger tests:/);
152
141
  }));
153
142
  });
@@ -167,7 +156,7 @@ describe('utils', () => {
167
156
  suite: 'Suite 3',
168
157
  },
169
158
  };
170
- beforeAll(() => {
159
+ beforeEach(() => {
171
160
  const axiosMock = jest.spyOn(axios_1.default, 'create');
172
161
  axiosMock.mockImplementation((() => (e) => {
173
162
  const publicId = e.url.slice(18);
@@ -179,9 +168,6 @@ describe('utils', () => {
179
168
  throw error;
180
169
  }));
181
170
  });
182
- afterAll(() => {
183
- jest.clearAllMocks();
184
- });
185
171
  test('only existing tests are returned', () => __awaiter(void 0, void 0, void 0, function* () {
186
172
  const triggerConfigs = [
187
173
  { suite: 'Suite 1', config: {}, id: '123-456-789' },
@@ -344,71 +330,59 @@ describe('utils', () => {
344
330
  const result = {
345
331
  device: { height: 0, id: 'laptop_large', width: 0 },
346
332
  duration: 0,
347
- eventType: 'finished',
348
333
  passed: true,
349
334
  startUrl: '',
350
335
  stepDetails: [],
351
336
  };
352
- expect(utils.hasResultPassed(result, false, true)).toBeTruthy();
353
- expect(utils.hasResultPassed(result, true, true)).toBeTruthy();
337
+ expect(utils.hasResultPassed(result, false, false, true)).toBeTruthy();
338
+ expect(utils.hasResultPassed(result, false, true, true)).toBeTruthy();
354
339
  result.passed = false;
355
- expect(utils.hasResultPassed(result, false, true)).toBeFalsy();
356
- expect(utils.hasResultPassed(result, true, true)).toBeFalsy();
340
+ expect(utils.hasResultPassed(result, false, false, true)).toBeFalsy();
341
+ expect(utils.hasResultPassed(result, false, true, true)).toBeFalsy();
357
342
  });
358
343
  test('result with error', () => {
359
344
  const result = {
360
345
  device: { height: 0, id: 'laptop_large', width: 0 },
361
346
  duration: 0,
362
- errorCode: 'ERRABORTED',
363
- eventType: 'finished',
347
+ failure: {
348
+ code: 'ERRABORTED',
349
+ message: 'Connection aborted',
350
+ },
364
351
  passed: false,
365
352
  startUrl: '',
366
353
  stepDetails: [],
367
354
  };
368
- expect(utils.hasResultPassed(result, false, true)).toBeFalsy();
369
- expect(utils.hasResultPassed(result, true, true)).toBeFalsy();
355
+ expect(utils.hasResultPassed(result, false, false, true)).toBeFalsy();
356
+ expect(utils.hasResultPassed(result, false, true, true)).toBeFalsy();
370
357
  });
371
358
  test('result with unhealthy result', () => {
372
359
  const result = {
373
360
  device: { height: 0, id: 'laptop_large', width: 0 },
374
361
  duration: 0,
375
- errorCode: 'ERRABORTED',
376
- eventType: 'finished',
362
+ failure: {
363
+ code: 'ERRABORTED',
364
+ message: 'Connection aborted',
365
+ },
377
366
  passed: false,
378
367
  startUrl: '',
379
368
  stepDetails: [],
380
369
  unhealthy: true,
381
370
  };
382
- expect(utils.hasResultPassed(result, false, true)).toBeTruthy();
383
- expect(utils.hasResultPassed(result, true, true)).toBeFalsy();
371
+ expect(utils.hasResultPassed(result, false, false, true)).toBeTruthy();
372
+ expect(utils.hasResultPassed(result, false, true, true)).toBeFalsy();
384
373
  });
385
374
  test('result with timeout result', () => {
386
375
  const result = {
387
376
  device: { height: 0, id: 'laptop_large', width: 0 },
388
377
  duration: 0,
389
- error: interfaces_1.ERRORS.TIMEOUT,
390
- eventType: 'finished',
391
378
  passed: false,
392
379
  startUrl: '',
393
380
  stepDetails: [],
394
381
  };
395
- expect(utils.hasResultPassed(result, true, true)).toBeFalsy();
396
- expect(utils.hasResultPassed(result, true, false)).toBeTruthy();
382
+ expect(utils.hasResultPassed(result, true, true, true)).toBeFalsy();
383
+ expect(utils.hasResultPassed(result, true, true, false)).toBeTruthy();
397
384
  });
398
385
  });
399
- test('result with endpoint failure result', () => {
400
- const result = {
401
- device: { height: 0, id: 'laptop_large', width: 0 },
402
- duration: 0,
403
- error: interfaces_1.ERRORS.ENDPOINT,
404
- eventType: 'finished',
405
- passed: false,
406
- startUrl: '',
407
- stepDetails: [],
408
- };
409
- expect(utils.hasResultPassed(result, false, true)).toBeTruthy();
410
- expect(utils.hasResultPassed(result, true, true)).toBeFalsy();
411
- });
412
386
  describe('getExecutionRule', () => {
413
387
  const cases = [
414
388
  [undefined, undefined, interfaces_1.ExecutionRule.BLOCKING],
@@ -437,211 +411,142 @@ describe('utils', () => {
437
411
  jest.spyOn(utils, 'getExecutionRule').mockReturnValue(resultRule);
438
412
  const test = fixtures_1.getApiTest('abc-def-ghi');
439
413
  const result = fixtures_1.getApiResult('1', test);
414
+ result.executionRule = resultRule;
440
415
  result.passed = resultPassed;
441
416
  expect(utils.getResultOutcome(result)).toEqual(expectedOutcome);
442
417
  });
443
418
  });
444
419
  describe('waitForResults', () => {
445
- const mockAxiosWithDefaultResult = () => {
446
- jest.spyOn(axios_1.default, 'create').mockImplementation((() => (r) => __awaiter(void 0, void 0, void 0, function* () {
447
- yield utils.wait(100);
448
- const results = JSON.parse(r.params.result_ids)
449
- .filter((resultId) => resultId !== 'timingOutTest')
450
- .map((resultId) => getPassingPollResult(resultId));
451
- return { data: { results } };
452
- })));
453
- };
454
- afterAll(() => {
455
- jest.clearAllMocks();
420
+ beforeAll(() => {
421
+ // We still wait a few milliseconds to avoid the test going crazy on a infinite loop
422
+ // if case of mistakes in the code or test.
423
+ jest.spyOn(utils, 'wait').mockImplementation(() => new Promise((r) => setTimeout(r, 10)));
456
424
  });
457
- const testConfig = fixtures_1.getApiTest('abc-def-ghi');
458
- const getPassingPollResult = (resultId) => {
459
- const result = getPassingResult(resultId);
460
- return {
461
- check: result.test,
462
- enrichment: result.enrichment,
463
- result: result.result,
464
- resultID: result.resultId,
465
- timestamp: result.timestamp,
466
- };
467
- };
468
- const getPassingResult = (resultId) => ({
425
+ const batch = fixtures_1.getBatch();
426
+ const apiTest = fixtures_1.getApiTest(batch.results[0].test_public_id);
427
+ const result = {
428
+ executionRule: interfaces_1.ExecutionRule.BLOCKING,
469
429
  location: fixtures_1.mockLocation.display_name,
470
430
  passed: true,
471
- result: fixtures_1.getBrowserServerResult({ error: interfaces_1.ERRORS.TIMEOUT, passed: false }),
472
- resultId,
473
- test: testConfig,
431
+ result: fixtures_1.getBrowserServerResult({ passed: true }),
432
+ resultId: batch.results[0].result_id,
433
+ test: apiTest,
434
+ timedOut: false,
474
435
  timestamp: 0,
475
- });
476
- const getTestAndResult = (publicId = 'abc-def-ghi', resultId = '0123456789') => {
477
- const triggerResult = {
478
- device: 'laptop_large',
479
- location: fixtures_1.mockLocation.id,
480
- public_id: publicId,
481
- result_id: resultId,
482
- };
483
- const triggerConfig = {
484
- config: {},
485
- id: publicId,
486
- suite: 'Suite 1',
487
- };
488
- const passingResult = getPassingResult(resultId);
489
- return {
490
- passingResult,
491
- test: passingResult.test,
492
- trigger: { locations: [fixtures_1.mockLocation], results: [triggerResult] },
493
- triggerConfig,
494
- triggerResult,
495
- };
436
+ };
437
+ const pollResult = {
438
+ check: result.test,
439
+ result: result.result,
440
+ resultID: result.resultId,
441
+ timestamp: result.timestamp,
442
+ };
443
+ const trigger = { batch_id: 'bid', locations: [fixtures_1.mockLocation] };
444
+ const mockApi = ({ getBatchImplementation, pollResultsImplementation, } = {}) => {
445
+ const getBatchMock = jest
446
+ .spyOn(api, 'getBatch')
447
+ .mockImplementation(getBatchImplementation || (() => __awaiter(void 0, void 0, void 0, function* () { return JSON.parse(JSON.stringify(batch)); })));
448
+ const pollResultsMock = jest
449
+ .spyOn(api, 'pollResults')
450
+ .mockImplementation(pollResultsImplementation || (() => __awaiter(void 0, void 0, void 0, function* () { return JSON.parse(JSON.stringify([pollResult])); })));
451
+ return { getBatchMock, pollResultsMock };
496
452
  };
497
453
  test('should poll result ids', () => __awaiter(void 0, void 0, void 0, function* () {
498
- mockAxiosWithDefaultResult();
499
- const { test, trigger, passingResult, triggerConfig } = getTestAndResult();
500
- const waitMock = jest.spyOn(utils, 'wait');
501
- waitMock.mockImplementation();
502
- const expectedResults = [passingResult];
503
- expect(yield utils.waitForResults(api, trigger, [triggerConfig], [test], { defaultTimeout: 120000, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual(expectedResults);
504
- expect(fixtures_1.mockReporter.resultReceived).toHaveBeenCalledWith(passingResult);
454
+ mockApi();
455
+ expect(yield utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 120000, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual([result]);
456
+ expect(fixtures_1.mockReporter.resultReceived).toHaveBeenCalledWith(batch.results[0]);
505
457
  }));
506
- test('results should be timed-out if global pollingTimeout is exceeded', () => __awaiter(void 0, void 0, void 0, function* () {
507
- const { test, trigger, triggerResult } = getTestAndResult();
508
- const expectedResults = [
509
- {
510
- location: fixtures_1.mockLocation.display_name,
511
- passed: true,
512
- result: fixtures_1.getBrowserServerResult({
513
- device: { height: 0, id: triggerResult.device, width: 0 },
514
- error: interfaces_1.ERRORS.TIMEOUT,
515
- passed: false,
516
- }),
517
- resultId: triggerResult.result_id,
518
- test,
519
- timestamp: 0,
520
- },
521
- ];
522
- expect(yield utils.waitForResults(api, trigger, [], [test], { defaultTimeout: 0, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual(expectedResults);
458
+ test('results should be timed out if global pollingTimeout is exceeded', () => __awaiter(void 0, void 0, void 0, function* () {
459
+ mockApi({ getBatchImplementation: () => __awaiter(void 0, void 0, void 0, function* () { return (Object.assign(Object.assign({}, batch), { status: 'in_progress' })); }) });
460
+ expect(yield utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 0, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual([
461
+ Object.assign(Object.assign({}, result), { result: Object.assign(Object.assign({}, result.result), { failure: { code: 'TIMEOUT', message: 'Result timed out' }, passed: false }), timedOut: true }),
462
+ ]);
523
463
  }));
524
- test('results should be timeout-ed if test pollingTimeout is exceeded', () => __awaiter(void 0, void 0, void 0, function* () {
525
- const { test, trigger, triggerResult } = getTestAndResult();
526
- const expectedResults = [
527
- {
528
- location: fixtures_1.mockLocation.display_name,
529
- passed: true,
530
- result: fixtures_1.getBrowserServerResult({
531
- device: { height: 0, id: triggerResult.device, width: 0 },
532
- error: interfaces_1.ERRORS.TIMEOUT,
533
- passed: false,
534
- }),
535
- resultId: triggerResult.result_id,
536
- test,
537
- timestamp: 0,
538
- },
539
- ];
540
- const testTriggerConfig = {
541
- config: { pollingTimeout: 0 },
542
- id: triggerResult.public_id,
543
- suite: 'Suite 1',
544
- };
545
- expect(yield utils.waitForResults(api, trigger, [testTriggerConfig], [test], { defaultTimeout: 120000, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual(expectedResults);
464
+ test('results failure should ignore if timed-out', () => __awaiter(void 0, void 0, void 0, function* () {
465
+ // The original failure of a result received between timing-out in batch poll
466
+ // and retrieving it should be ignored in favor of timeout.
467
+ mockApi({
468
+ pollResultsImplementation: () => __awaiter(void 0, void 0, void 0, function* () {
469
+ return [
470
+ Object.assign(Object.assign({}, pollResult), { result: Object.assign(Object.assign({}, pollResult.result), { failure: { code: 'FAILURE', message: 'Original failure, should be ignored' }, passed: false }) }),
471
+ ];
472
+ }),
473
+ });
474
+ expect(yield utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 0, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toStrictEqual([
475
+ Object.assign(Object.assign({}, result), { result: Object.assign(Object.assign({}, result.result), { failure: { code: 'TIMEOUT', message: 'Result timed out' }, passed: false }), timedOut: true }),
476
+ ]);
477
+ }));
478
+ test('results should be timed out if batch result is timed out', () => __awaiter(void 0, void 0, void 0, function* () {
479
+ const batchWithTimeoutResult = Object.assign(Object.assign({}, batch), { results: [Object.assign(Object.assign({}, batch.results[0]), { timed_out: true })] });
480
+ mockApi({ getBatchImplementation: () => __awaiter(void 0, void 0, void 0, function* () { return batchWithTimeoutResult; }) });
481
+ expect(yield utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 120000, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual([
482
+ Object.assign(Object.assign({}, result), { result: Object.assign(Object.assign({}, result.result), { failure: { code: 'TIMEOUT', message: 'Result timed out' }, passed: false }), timedOut: true }),
483
+ ]);
484
+ }));
485
+ test('wait between batch polling', () => __awaiter(void 0, void 0, void 0, function* () {
486
+ jest.restoreAllMocks();
487
+ const waitMock = jest.spyOn(utils, 'wait').mockImplementation(() => new Promise((r) => setTimeout(r, 10)));
488
+ let counter = 0;
489
+ mockApi({
490
+ getBatchImplementation: () => __awaiter(void 0, void 0, void 0, function* () {
491
+ counter += 1;
492
+ return counter === 3 ? batch : Object.assign(Object.assign({}, batch), { status: 'in_progress' });
493
+ }),
494
+ });
495
+ expect(yield utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 120000, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual([result]);
496
+ expect(counter).toBe(3);
497
+ expect(waitMock).toHaveBeenCalledTimes(2);
546
498
  }));
547
499
  test('correct number of pass and timeout results', () => __awaiter(void 0, void 0, void 0, function* () {
548
- mockAxiosWithDefaultResult();
549
- const { test, trigger, triggerResult, passingResult } = getTestAndResult();
550
- const waitMock = jest.spyOn(utils, 'wait');
551
- waitMock.mockImplementation();
552
- const triggerResultTimeOut = Object.assign(Object.assign({}, triggerResult), { result_id: 'timingOutTest' });
553
- const expectedResults = [
554
- passingResult,
555
- {
556
- location: fixtures_1.mockLocation.display_name,
557
- passed: true,
558
- result: fixtures_1.getBrowserServerResult({
559
- device: { height: 0, id: triggerResultTimeOut.device, width: 0 },
560
- error: interfaces_1.ERRORS.TIMEOUT,
561
- passed: false,
562
- }),
563
- resultId: triggerResultTimeOut.result_id,
564
- test,
565
- timestamp: 0,
566
- },
567
- ];
568
- trigger.results = [triggerResult, triggerResultTimeOut];
569
- expect(yield utils.waitForResults(api, trigger, [], [test], { defaultTimeout: 2000, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual(expectedResults);
500
+ const pollTimeoutResult = Object.assign(Object.assign({}, pollResult), { resultID: 'another-id' });
501
+ const batchWithTimeoutResult = Object.assign(Object.assign({}, batch), { results: [batch.results[0], Object.assign(Object.assign({}, batch.results[0]), { timed_out: true, result_id: pollTimeoutResult.resultID })] });
502
+ mockApi({
503
+ getBatchImplementation: () => __awaiter(void 0, void 0, void 0, function* () { return batchWithTimeoutResult; }),
504
+ pollResultsImplementation: () => __awaiter(void 0, void 0, void 0, function* () { return [pollResult, pollTimeoutResult]; }),
505
+ });
506
+ expect(yield utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 2000, failOnCriticalErrors: false }, fixtures_1.mockReporter)).toEqual([result, Object.assign(Object.assign({}, result), { resultId: pollTimeoutResult.resultID, timedOut: true })]);
570
507
  }));
571
508
  test('tunnel failure', () => __awaiter(void 0, void 0, void 0, function* () {
572
- const waitMock = jest.spyOn(utils, 'wait');
573
- waitMock.mockImplementation();
574
- const { test, trigger, triggerResult } = getTestAndResult();
575
- // Fake pollResults to not update results and iterate until the isTunnelConnected is equal to false
576
- jest
577
- .spyOn(axios_1.default, 'create')
578
- .mockImplementation((() => (r) => __awaiter(void 0, void 0, void 0, function* () { return ({ data: { results: [] } }); })));
509
+ mockApi();
579
510
  const mockTunnel = {
580
511
  keepAlive: () => __awaiter(void 0, void 0, void 0, function* () {
581
512
  throw new Error('keepAlive failed');
582
513
  }),
583
514
  };
584
- const expectedResults = [
585
- {
586
- location: 'Tunneled',
587
- passed: false,
588
- result: fixtures_1.getBrowserServerResult({
589
- device: { height: 0, id: triggerResult.device, width: 0 },
590
- error: interfaces_1.ERRORS.TUNNEL,
591
- passed: false,
592
- }),
593
- resultId: triggerResult.result_id,
594
- test,
595
- timestamp: 0,
596
- },
597
- ];
598
- expect(yield utils.waitForResults(api, trigger, [], [test], { defaultTimeout: 2000, failOnCriticalErrors: true }, fixtures_1.mockReporter, mockTunnel)).toEqual(expectedResults);
599
- expect(yield utils.waitForResults(api, trigger, [], [test], { defaultTimeout: 2000, failOnCriticalErrors: false }, fixtures_1.mockReporter, mockTunnel)).toEqual(expectedResults);
515
+ yield utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 2000, failOnCriticalErrors: true }, fixtures_1.mockReporter, mockTunnel);
516
+ expect(fixtures_1.mockReporter.error).toBeCalledWith('The tunnel has stopped working, this may have affected the results.');
600
517
  }));
601
518
  test('location when tunnel', () => __awaiter(void 0, void 0, void 0, function* () {
602
- const waitMock = jest.spyOn(utils, 'wait');
603
- waitMock.mockImplementation();
604
- const { test, trigger } = getTestAndResult();
605
- const mockTunnel = {
606
- keepAlive: () => __awaiter(void 0, void 0, void 0, function* () { return true; }),
607
- };
608
- let results = yield utils.waitForResults(api, trigger, [], [test], { defaultTimeout: 2000, failOnCriticalErrors: true }, fixtures_1.mockReporter, mockTunnel);
519
+ mockApi();
520
+ const mockTunnel = { keepAlive: () => __awaiter(void 0, void 0, void 0, function* () { return true; }) };
521
+ let results = yield utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 2000, failOnCriticalErrors: true }, fixtures_1.mockReporter, mockTunnel);
609
522
  expect(results[0].location).toBe('Tunneled');
610
- const newTest = Object.assign({}, test);
523
+ const newTest = Object.assign({}, result.test);
611
524
  newTest.type = 'api';
612
525
  newTest.subtype = 'http';
613
- results = yield utils.waitForResults(api, trigger, [], [newTest], { defaultTimeout: 2000, failOnCriticalErrors: true }, fixtures_1.mockReporter, mockTunnel);
526
+ results = yield utils.waitForResults(api, trigger, [newTest], { maxPollingTimeout: 2000, failOnCriticalErrors: true }, fixtures_1.mockReporter, mockTunnel);
614
527
  expect(results[0].location).toBe('Tunneled');
615
528
  newTest.type = 'api';
616
529
  newTest.subtype = 'ssl';
617
- results = yield utils.waitForResults(api, trigger, [], [newTest], { defaultTimeout: 2000, failOnCriticalErrors: true }, fixtures_1.mockReporter, mockTunnel);
530
+ results = yield utils.waitForResults(api, trigger, [newTest], { failOnCriticalErrors: true, maxPollingTimeout: 2000 }, fixtures_1.mockReporter, mockTunnel);
618
531
  expect(results[0].location).toBe('Frankfurt (AWS)');
619
532
  }));
620
533
  test('pollResults throws', () => __awaiter(void 0, void 0, void 0, function* () {
621
- const { test, trigger, triggerResult } = getTestAndResult();
622
- jest.spyOn(utils, 'wait').mockImplementation();
623
- const axiosMock = jest.spyOn(axios_1.default, 'create');
624
- const serverError = new Error('Server Error');
625
- serverError.response = { status: 502 };
626
- axiosMock.mockImplementation((() => (r) => __awaiter(void 0, void 0, void 0, function* () {
627
- throw serverError;
628
- })));
629
- const mockTunnel = { keepAlive: () => __awaiter(void 0, void 0, void 0, function* () { return Promise.reject(); }) };
630
- expect(yield utils.waitForResults(api, trigger, [], [test], { defaultTimeout: 2000, failOnCriticalErrors: false }, fixtures_1.mockReporter, mockTunnel)).toEqual([
631
- {
632
- location: 'Tunneled',
633
- passed: true,
634
- result: fixtures_1.getBrowserServerResult({
635
- device: { height: 0, id: triggerResult.device, width: 0 },
636
- error: interfaces_1.ERRORS.ENDPOINT,
637
- passed: false,
638
- }),
639
- resultId: triggerResult.result_id,
640
- test,
641
- timestamp: 0,
534
+ const { pollResultsMock } = mockApi({
535
+ pollResultsImplementation: () => {
536
+ throw new Error('Poll results server error');
642
537
  },
643
- ]);
644
- yield expect(utils.waitForResults(api, trigger, [], [test], { defaultTimeout: 2000, failOnCriticalErrors: true }, fixtures_1.mockReporter, mockTunnel)).rejects.toThrow();
538
+ });
539
+ yield expect(utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 2000 }, fixtures_1.mockReporter)).rejects.toThrowError('Failed to poll results: Poll results server error');
540
+ expect(pollResultsMock).toHaveBeenCalledWith([result.resultId]);
541
+ }));
542
+ test('getBatch throws', () => __awaiter(void 0, void 0, void 0, function* () {
543
+ const { getBatchMock } = mockApi({
544
+ getBatchImplementation: () => {
545
+ throw new Error('Get batch server error');
546
+ },
547
+ });
548
+ yield expect(utils.waitForResults(api, trigger, [result.test], { maxPollingTimeout: 2000 }, fixtures_1.mockReporter)).rejects.toThrowError('Failed to get batch: Get batch server error');
549
+ expect(getBatchMock).toHaveBeenCalledWith(trigger.batch_id);
645
550
  }));
646
551
  });
647
552
  test('getStrictestExecutionRule', () => {
@@ -699,5 +604,144 @@ describe('utils', () => {
699
604
  expect(utils.parseVariablesFromCli([''], mockLogFunction)).toBeUndefined();
700
605
  expect(utils.parseVariablesFromCli(undefined, mockLogFunction)).toBeUndefined();
701
606
  });
607
+ test('getAppBaseURL', () => {
608
+ expect(utils.getAppBaseURL({ datadogSite: 'datadoghq.eu', subdomain: 'custom' })).toBe('https://custom.datadoghq.eu/');
609
+ });
610
+ describe('sortResultsByOutcome', () => {
611
+ const results = fixtures_1.getResults([
612
+ { executionRule: interfaces_1.ExecutionRule.NON_BLOCKING, passed: false },
613
+ { executionRule: interfaces_1.ExecutionRule.BLOCKING, passed: true },
614
+ { executionRule: interfaces_1.ExecutionRule.BLOCKING, passed: false },
615
+ { executionRule: interfaces_1.ExecutionRule.NON_BLOCKING, passed: true },
616
+ ]);
617
+ test('should sort tests with success, non_blocking failures then failures', () => __awaiter(void 0, void 0, void 0, function* () {
618
+ const sortedResults = [...results];
619
+ sortedResults.sort(utils.sortResultsByOutcome());
620
+ expect(sortedResults.map((r) => r.resultId)).toStrictEqual(['3', '1', '0', '2']);
621
+ }));
622
+ });
623
+ describe('Render results', () => {
624
+ const emptySummary = utils.createSummary();
625
+ const cases = [
626
+ {
627
+ description: '1 API test with 1 config override, 1 result (passed)',
628
+ expected: {
629
+ exitCode: 0,
630
+ summary: Object.assign(Object.assign({}, emptySummary), { passed: 1 }),
631
+ },
632
+ failOnCriticalErrors: false,
633
+ failOnTimeout: false,
634
+ results: fixtures_1.getResults([{ passed: true }]),
635
+ summary: Object.assign({}, emptySummary),
636
+ },
637
+ {
638
+ description: '1 API test with 1 config override, 1 result (failed timeout), no fail on timeout, no fail on critical errors',
639
+ expected: {
640
+ exitCode: 0,
641
+ summary: Object.assign(Object.assign({}, emptySummary), { passed: 1, timedOut: 1 }),
642
+ },
643
+ failOnCriticalErrors: false,
644
+ failOnTimeout: false,
645
+ results: fixtures_1.getResults([{ timedOut: true }]),
646
+ summary: Object.assign({}, emptySummary),
647
+ },
648
+ {
649
+ description: '1 API test with 1 config override, 1 result (failed timeout), fail on timeout, no fail on critical errors',
650
+ expected: {
651
+ exitCode: 1,
652
+ summary: Object.assign(Object.assign({}, emptySummary), { failed: 1 }),
653
+ },
654
+ failOnCriticalErrors: false,
655
+ failOnTimeout: true,
656
+ results: fixtures_1.getResults([{ timedOut: true }]),
657
+ summary: Object.assign({}, emptySummary),
658
+ },
659
+ {
660
+ description: '1 API test with 1 config override, 1 result (failed critical error), no fail on timeout, no fail on critical errors',
661
+ expected: {
662
+ exitCode: 0,
663
+ summary: Object.assign(Object.assign({}, emptySummary), { passed: 1, criticalErrors: 1 }),
664
+ },
665
+ failOnCriticalErrors: false,
666
+ failOnTimeout: false,
667
+ results: fixtures_1.getResults([{ unhealthy: true }]),
668
+ summary: Object.assign({}, emptySummary),
669
+ },
670
+ {
671
+ description: '1 API test with 1 config override, 1 result (failed critical error), no fail on timeout, fail on critical errors',
672
+ expected: {
673
+ exitCode: 1,
674
+ summary: Object.assign(Object.assign({}, emptySummary), { criticalErrors: 0, failed: 1 }),
675
+ },
676
+ failOnCriticalErrors: true,
677
+ failOnTimeout: false,
678
+ results: fixtures_1.getResults([{ unhealthy: true }]),
679
+ summary: Object.assign({}, emptySummary),
680
+ },
681
+ {
682
+ description: '1 API test (blocking) with 4 config overrides (1 skipped), 3 results (1 passed, 1 failed, 1 failed non-blocking)',
683
+ expected: {
684
+ exitCode: 1,
685
+ summary: Object.assign(Object.assign({}, emptySummary), { failed: 1, failedNonBlocking: 1, passed: 1, skipped: 1 }),
686
+ },
687
+ failOnCriticalErrors: false,
688
+ failOnTimeout: false,
689
+ results: fixtures_1.getResults([{ passed: true }, { executionRule: interfaces_1.ExecutionRule.NON_BLOCKING }, {}]),
690
+ summary: Object.assign(Object.assign({}, emptySummary), { skipped: 1 }),
691
+ },
692
+ {
693
+ description: '1 API test (non-blocking) with 4 config overrides (1 skipped), 3 results (1 passed, 1 failed, 1 failed non-blocking)',
694
+ expected: {
695
+ exitCode: 0,
696
+ summary: Object.assign(Object.assign({}, emptySummary), { failedNonBlocking: 2, passed: 1, skipped: 1 }),
697
+ },
698
+ failOnCriticalErrors: false,
699
+ failOnTimeout: false,
700
+ results: fixtures_1.getResults([
701
+ {
702
+ executionRule: interfaces_1.ExecutionRule.NON_BLOCKING,
703
+ testExecutionRule: interfaces_1.ExecutionRule.NON_BLOCKING,
704
+ },
705
+ { passed: true, testExecutionRule: interfaces_1.ExecutionRule.NON_BLOCKING },
706
+ {
707
+ testExecutionRule: interfaces_1.ExecutionRule.NON_BLOCKING,
708
+ },
709
+ ]),
710
+ summary: Object.assign(Object.assign({}, emptySummary), { skipped: 1 }),
711
+ },
712
+ {
713
+ description: '3 API tests (blocking) with 1 config override each, 3 results (1 failed non-blocking, 1 failed, 1 passed)',
714
+ expected: {
715
+ exitCode: 1,
716
+ summary: Object.assign(Object.assign({}, emptySummary), { failed: 1, failedNonBlocking: 1, passed: 1 }),
717
+ },
718
+ failOnCriticalErrors: false,
719
+ failOnTimeout: false,
720
+ results: fixtures_1.getResults([{}, { passed: true }, { executionRule: interfaces_1.ExecutionRule.NON_BLOCKING }]),
721
+ summary: Object.assign({}, emptySummary),
722
+ },
723
+ ];
724
+ test.each(cases)('$description', (testCase) => __awaiter(void 0, void 0, void 0, function* () {
725
+ testCase.results.forEach((result) => (result.passed = utils.hasResultPassed(result.result, result.timedOut, testCase.failOnCriticalErrors, testCase.failOnTimeout)));
726
+ const config = Object.assign(Object.assign({}, command_1.DEFAULT_COMMAND_CONFIG), { failOnCriticalErrors: testCase.failOnCriticalErrors, failOnTimeout: testCase.failOnTimeout });
727
+ const startTime = Date.now();
728
+ const exitCode = utils.renderResults({
729
+ config,
730
+ reporter: fixtures_1.mockReporter,
731
+ results: testCase.results,
732
+ startTime,
733
+ summary: testCase.summary,
734
+ });
735
+ expect(fixtures_1.mockReporter.reportStart).toHaveBeenCalledWith({ startTime });
736
+ expect(fixtures_1.mockReporter.resultEnd).toHaveBeenCalledTimes(testCase.results.length);
737
+ const baseUrl = `https://${command_1.DEFAULT_COMMAND_CONFIG.subdomain}.${command_1.DEFAULT_COMMAND_CONFIG.datadogSite}/`;
738
+ for (const result of testCase.results) {
739
+ expect(fixtures_1.mockReporter.resultEnd).toHaveBeenCalledWith(result, baseUrl);
740
+ }
741
+ expect(testCase.summary).toEqual(testCase.expected.summary);
742
+ expect(fixtures_1.mockReporter.runEnd).toHaveBeenCalledWith(testCase.expected.summary, baseUrl);
743
+ expect(exitCode).toBe(testCase.expected.exitCode);
744
+ }));
745
+ });
702
746
  });
703
747
  //# sourceMappingURL=utils.test.js.map