@testomatio/reporter 2.1.0-beta.1-codeceptjs → 2.1.0-beta.2-codeceptjs
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/lib/client.js +2 -1
- package/lib/replay.js +12 -8
- package/lib/xmlReader.js +3 -0
- package/package.json +1 -1
- package/src/adapter/webdriver.js +11 -3
- package/src/bin/cli.js +9 -6
- package/src/client.js +11 -3
- package/src/pipe/testomatio.js +8 -4
- package/src/replay.js +16 -10
- package/src/utils/utils.js +21 -2
- package/src/xmlReader.js +3 -0
package/lib/client.js
CHANGED
|
@@ -179,7 +179,7 @@ class Client {
|
|
|
179
179
|
/**
|
|
180
180
|
* @type {TestData}
|
|
181
181
|
*/
|
|
182
|
-
const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, timestamp, manuallyAttachedArtifacts, labels, } = testData;
|
|
182
|
+
const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, timestamp, manuallyAttachedArtifacts, labels, overwrite, } = testData;
|
|
183
183
|
let { message = '', meta = {} } = testData;
|
|
184
184
|
// stringify meta values and limit keys and values length to 255
|
|
185
185
|
meta = Object.entries(meta)
|
|
@@ -267,6 +267,7 @@ class Client {
|
|
|
267
267
|
artifacts,
|
|
268
268
|
meta,
|
|
269
269
|
labels,
|
|
270
|
+
overwrite,
|
|
270
271
|
...(rootSuiteId && { root_suite_id: rootSuiteId }),
|
|
271
272
|
};
|
|
272
273
|
// debug('Adding test run...', data);
|
package/lib/replay.js
CHANGED
|
@@ -35,7 +35,10 @@ class Replay {
|
|
|
35
35
|
throw new Error(`Debug file not found: ${debugFile}`);
|
|
36
36
|
}
|
|
37
37
|
const fileContent = fs_1.default.readFileSync(debugFile, 'utf-8');
|
|
38
|
-
const lines = fileContent
|
|
38
|
+
const lines = fileContent
|
|
39
|
+
.trim()
|
|
40
|
+
.split('\n')
|
|
41
|
+
.filter(line => line.trim() !== '');
|
|
39
42
|
if (lines.length === 0) {
|
|
40
43
|
throw new Error('Debug file is empty');
|
|
41
44
|
}
|
|
@@ -79,7 +82,8 @@ class Replay {
|
|
|
79
82
|
// Merge artifacts arrays
|
|
80
83
|
mergedTest.artifacts = [...(existingTest.artifacts || []), ...test[key]];
|
|
81
84
|
}
|
|
82
|
-
else if (existingTest[key] === null ||
|
|
85
|
+
else if (existingTest[key] === null ||
|
|
86
|
+
existingTest[key] === undefined ||
|
|
83
87
|
(Array.isArray(existingTest[key]) && existingTest[key].length === 0)) {
|
|
84
88
|
// Use new value if existing is null/undefined/empty array
|
|
85
89
|
mergedTest[key] = test[key];
|
|
@@ -145,7 +149,7 @@ class Replay {
|
|
|
145
149
|
envVars,
|
|
146
150
|
parseErrors,
|
|
147
151
|
totalLines: lines.length,
|
|
148
|
-
runId
|
|
152
|
+
runId,
|
|
149
153
|
};
|
|
150
154
|
}
|
|
151
155
|
/**
|
|
@@ -190,7 +194,7 @@ class Replay {
|
|
|
190
194
|
finishParams,
|
|
191
195
|
envVars,
|
|
192
196
|
runId,
|
|
193
|
-
dryRun: true
|
|
197
|
+
dryRun: true,
|
|
194
198
|
};
|
|
195
199
|
}
|
|
196
200
|
// Create client and restore the run
|
|
@@ -213,13 +217,13 @@ class Replay {
|
|
|
213
217
|
let failureCount = 0;
|
|
214
218
|
for (const [index, test] of tests.entries()) {
|
|
215
219
|
try {
|
|
216
|
-
await client.addTestRun(test.status, test);
|
|
220
|
+
await client.addTestRun(test.status, { ...test, overwrite: true });
|
|
217
221
|
successCount++;
|
|
218
222
|
this.onProgress({
|
|
219
223
|
current: index + 1,
|
|
220
224
|
total: tests.length,
|
|
221
225
|
test,
|
|
222
|
-
success: true
|
|
226
|
+
success: true,
|
|
223
227
|
});
|
|
224
228
|
}
|
|
225
229
|
catch (err) {
|
|
@@ -230,7 +234,7 @@ class Replay {
|
|
|
230
234
|
total: tests.length,
|
|
231
235
|
test,
|
|
232
236
|
success: false,
|
|
233
|
-
error: err.message
|
|
237
|
+
error: err.message,
|
|
234
238
|
});
|
|
235
239
|
}
|
|
236
240
|
}
|
|
@@ -243,7 +247,7 @@ class Replay {
|
|
|
243
247
|
runParams,
|
|
244
248
|
finishParams,
|
|
245
249
|
envVars,
|
|
246
|
-
runId: runId || client.runId
|
|
250
|
+
runId: runId || client.runId,
|
|
247
251
|
};
|
|
248
252
|
this.onLog(`Successfully replayed ${successCount}/${tests.length} tests from debug file`);
|
|
249
253
|
return result;
|
package/lib/xmlReader.js
CHANGED
|
@@ -186,6 +186,7 @@ class XmlReader {
|
|
|
186
186
|
if (test.file)
|
|
187
187
|
r.file = test.file;
|
|
188
188
|
r.create = true;
|
|
189
|
+
r.overwrite = true;
|
|
189
190
|
if (r.status === 'Passed')
|
|
190
191
|
r.status = constants_js_1.STATUS.PASSED;
|
|
191
192
|
if (r.status === 'Failed')
|
|
@@ -254,6 +255,7 @@ class XmlReader {
|
|
|
254
255
|
title,
|
|
255
256
|
suite_title,
|
|
256
257
|
run_time,
|
|
258
|
+
retry: false,
|
|
257
259
|
});
|
|
258
260
|
});
|
|
259
261
|
});
|
|
@@ -509,6 +511,7 @@ function reduceTestCases(prev, item) {
|
|
|
509
511
|
root_suite_id: TESTOMATIO_SUITE,
|
|
510
512
|
suite_title: suiteTitle,
|
|
511
513
|
files,
|
|
514
|
+
retry: false,
|
|
512
515
|
});
|
|
513
516
|
});
|
|
514
517
|
return prev;
|
package/package.json
CHANGED
package/src/adapter/webdriver.js
CHANGED
|
@@ -14,8 +14,10 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
14
14
|
this._addTestPromises = [];
|
|
15
15
|
|
|
16
16
|
this._isSynchronising = false;
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
// run is created by cli, if enabling the row below, it mat lead to multiple runs being created
|
|
19
|
+
// thus, need to check if process.env.runId is set and/or add more checks to avoid creating multiple runs
|
|
20
|
+
// this.client.createRun();
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
get isSynchronised() {
|
|
@@ -40,7 +42,6 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
40
42
|
|
|
41
43
|
onRunnerStart() {
|
|
42
44
|
// clear dir with artifacts/logs
|
|
43
|
-
//
|
|
44
45
|
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -140,3 +141,10 @@ function getTestLogs(fullTestTitle) {
|
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
export default WebdriverReporter;
|
|
144
|
+
|
|
145
|
+
/* INVESTIGATION RESULTS:
|
|
146
|
+
If you run tests in parallel, the WDIO creates a separate process for each parallel instance.
|
|
147
|
+
As a result, there is own WDIOReporter instance for each parallel process.
|
|
148
|
+
This means, its impossible to create or finish run, because can't understand if its was already created
|
|
149
|
+
in other process or not.
|
|
150
|
+
*/
|
package/src/bin/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import createDebugMessages from 'debug';
|
|
|
7
7
|
import TestomatClient from '../client.js';
|
|
8
8
|
import XmlReader from '../xmlReader.js';
|
|
9
9
|
import { APP_PREFIX, STATUS } from '../constants.js';
|
|
10
|
-
import { getPackageVersion } from '../utils/utils.js';
|
|
10
|
+
import { cleanLatestRunId, getPackageVersion } from '../utils/utils.js';
|
|
11
11
|
import { config } from '../config.js';
|
|
12
12
|
import { readLatestRunId } from '../utils/utils.js';
|
|
13
13
|
import pc from 'picocolors';
|
|
@@ -36,6 +36,8 @@ program
|
|
|
36
36
|
.command('start')
|
|
37
37
|
.description('Start a new run and return its ID')
|
|
38
38
|
.action(async () => {
|
|
39
|
+
cleanLatestRunId();
|
|
40
|
+
|
|
39
41
|
console.log('Starting a new Run on Testomat.io...');
|
|
40
42
|
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
|
|
41
43
|
const client = new TestomatClient({ apiKey });
|
|
@@ -70,6 +72,7 @@ program
|
|
|
70
72
|
|
|
71
73
|
program
|
|
72
74
|
.command('run')
|
|
75
|
+
.alias('test')
|
|
73
76
|
.description('Run tests with the specified command')
|
|
74
77
|
.argument('<command>', 'Test runner command')
|
|
75
78
|
.option('--filter <filter>', 'Additional execution filter')
|
|
@@ -100,25 +103,25 @@ program
|
|
|
100
103
|
|
|
101
104
|
console.log(APP_PREFIX, `🚀 Running`, pc.green(command));
|
|
102
105
|
|
|
103
|
-
const runTests = () => {
|
|
106
|
+
const runTests = async () => {
|
|
104
107
|
const testCmds = command.split(' ');
|
|
105
108
|
const cmd = spawn(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
|
|
106
109
|
|
|
107
|
-
cmd.on('close', code => {
|
|
110
|
+
cmd.on('close', async code => {
|
|
108
111
|
const emoji = code === 0 ? '🟢' : '🔴';
|
|
109
112
|
console.log(APP_PREFIX, emoji, `Runner exited with ${pc.bold(code)}`);
|
|
110
113
|
if (apiKey) {
|
|
111
114
|
const status = code === 0 ? 'passed' : 'failed';
|
|
112
|
-
client.updateRunStatus(status, true);
|
|
115
|
+
await client.updateRunStatus(status, true);
|
|
113
116
|
}
|
|
114
117
|
process.exit(code);
|
|
115
118
|
});
|
|
116
119
|
};
|
|
117
120
|
|
|
118
121
|
if (apiKey) {
|
|
119
|
-
client.createRun().then(runTests);
|
|
122
|
+
await client.createRun().then(runTests);
|
|
120
123
|
} else {
|
|
121
|
-
runTests();
|
|
124
|
+
await runTests();
|
|
122
125
|
}
|
|
123
126
|
});
|
|
124
127
|
|
package/src/client.js
CHANGED
|
@@ -10,7 +10,7 @@ import { glob } from 'glob';
|
|
|
10
10
|
import path, { sep } from 'path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { S3Uploader } from './uploader.js';
|
|
13
|
-
import { formatStep, storeRunId, validateSuiteId } from './utils/utils.js';
|
|
13
|
+
import { formatStep, readLatestRunId, storeRunId, validateSuiteId } from './utils/utils.js';
|
|
14
14
|
import { filesize as prettyBytes } from 'filesize';
|
|
15
15
|
|
|
16
16
|
const debug = createDebugMessages('@testomatio/reporter:client');
|
|
@@ -34,7 +34,7 @@ class Client {
|
|
|
34
34
|
constructor(params = {}) {
|
|
35
35
|
this.paramsForPipesFactory = params;
|
|
36
36
|
this.pipeStore = {};
|
|
37
|
-
this.runId =
|
|
37
|
+
this.runId = '';
|
|
38
38
|
this.queue = Promise.resolve();
|
|
39
39
|
|
|
40
40
|
// @ts-ignore this line will be removed in compiled code, because __dirname is defined in commonjs
|
|
@@ -139,6 +139,9 @@ class Client {
|
|
|
139
139
|
* @returns {Promise<PipeResult[]>}
|
|
140
140
|
*/
|
|
141
141
|
async addTestRun(status, testData) {
|
|
142
|
+
if (!this.pipes || !this.pipes.length)
|
|
143
|
+
this.pipes = await pipesFactory(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
144
|
+
|
|
142
145
|
// all pipes disabled, skipping
|
|
143
146
|
if (!this.pipes?.filter(p => p.isEnabled).length) return [];
|
|
144
147
|
|
|
@@ -180,6 +183,7 @@ class Client {
|
|
|
180
183
|
timestamp,
|
|
181
184
|
manuallyAttachedArtifacts,
|
|
182
185
|
labels,
|
|
186
|
+
overwrite,
|
|
183
187
|
} = testData;
|
|
184
188
|
let { message = '', meta = {} } = testData;
|
|
185
189
|
|
|
@@ -277,6 +281,7 @@ class Client {
|
|
|
277
281
|
artifacts,
|
|
278
282
|
meta,
|
|
279
283
|
labels,
|
|
284
|
+
overwrite,
|
|
280
285
|
...(rootSuiteId && { root_suite_id: rootSuiteId }),
|
|
281
286
|
};
|
|
282
287
|
|
|
@@ -308,7 +313,10 @@ class Client {
|
|
|
308
313
|
* @param {boolean} [isParallel] - Whether the current test run was executed in parallel with other tests.
|
|
309
314
|
* @returns {Promise<any>} - A Promise that resolves when finishes the run.
|
|
310
315
|
*/
|
|
311
|
-
updateRunStatus(status, isParallel = false) {
|
|
316
|
+
async updateRunStatus(status, isParallel = false) {
|
|
317
|
+
this.pipes ||= await pipesFactory(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
318
|
+
this.runId ||= readLatestRunId();
|
|
319
|
+
|
|
312
320
|
debug('Updating run status...');
|
|
313
321
|
// all pipes disabled, skipping
|
|
314
322
|
if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
|
package/src/pipe/testomatio.js
CHANGED
|
@@ -3,7 +3,7 @@ import pc from 'picocolors';
|
|
|
3
3
|
import { Gaxios } from 'gaxios';
|
|
4
4
|
import JsonCycle from 'json-cycle';
|
|
5
5
|
import { APP_PREFIX, STATUS, AXIOS_TIMEOUT, REPORTER_REQUEST_RETRIES } from '../constants.js';
|
|
6
|
-
import { isValidUrl, foundedTestLog } from '../utils/utils.js';
|
|
6
|
+
import { isValidUrl, foundedTestLog, readLatestRunId } from '../utils/utils.js';
|
|
7
7
|
import { parseFilterParams, generateFilterRequestParams, setS3Credentials } from '../utils/pipe_utils.js';
|
|
8
8
|
import { config } from '../config.js';
|
|
9
9
|
|
|
@@ -367,10 +367,14 @@ class TestomatioPipe {
|
|
|
367
367
|
* Adds a test to the batch uploader (or reports a single test if batch uploading is disabled)
|
|
368
368
|
*/
|
|
369
369
|
addTest(data) {
|
|
370
|
-
this.isEnabled = this.apiKey ?? this.isEnabled;
|
|
371
|
-
|
|
370
|
+
this.isEnabled = !!(this.apiKey ?? this.isEnabled);
|
|
372
371
|
if (!this.isEnabled) return;
|
|
373
|
-
|
|
372
|
+
|
|
373
|
+
this.runId = this.runId || process.env.runId || this.store.runId || readLatestRunId();
|
|
374
|
+
if (!this.runId) {
|
|
375
|
+
console.warn(APP_PREFIX, pc.red('Run ID is not set, skipping test reporting'));
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
374
378
|
|
|
375
379
|
// add test ID + run ID
|
|
376
380
|
if (data.rid) data.rid = `${this.runId}-${data.rid}`;
|
package/src/replay.js
CHANGED
|
@@ -33,7 +33,10 @@ export class Replay {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const fileContent = fs.readFileSync(debugFile, 'utf-8');
|
|
36
|
-
const lines = fileContent
|
|
36
|
+
const lines = fileContent
|
|
37
|
+
.trim()
|
|
38
|
+
.split('\n')
|
|
39
|
+
.filter(line => line.trim() !== '');
|
|
37
40
|
|
|
38
41
|
if (lines.length === 0) {
|
|
39
42
|
throw new Error('Debug file is empty');
|
|
@@ -77,8 +80,11 @@ export class Replay {
|
|
|
77
80
|
} else if (key === 'artifacts' && Array.isArray(test[key]) && test[key].length > 0) {
|
|
78
81
|
// Merge artifacts arrays
|
|
79
82
|
mergedTest.artifacts = [...(existingTest.artifacts || []), ...test[key]];
|
|
80
|
-
} else if (
|
|
81
|
-
|
|
83
|
+
} else if (
|
|
84
|
+
existingTest[key] === null ||
|
|
85
|
+
existingTest[key] === undefined ||
|
|
86
|
+
(Array.isArray(existingTest[key]) && existingTest[key].length === 0)
|
|
87
|
+
) {
|
|
82
88
|
// Use new value if existing is null/undefined/empty array
|
|
83
89
|
mergedTest[key] = test[key];
|
|
84
90
|
}
|
|
@@ -139,7 +145,7 @@ export class Replay {
|
|
|
139
145
|
envVars,
|
|
140
146
|
parseErrors,
|
|
141
147
|
totalLines: lines.length,
|
|
142
|
-
runId
|
|
148
|
+
runId,
|
|
143
149
|
};
|
|
144
150
|
}
|
|
145
151
|
|
|
@@ -193,7 +199,7 @@ export class Replay {
|
|
|
193
199
|
finishParams,
|
|
194
200
|
envVars,
|
|
195
201
|
runId,
|
|
196
|
-
dryRun: true
|
|
202
|
+
dryRun: true,
|
|
197
203
|
};
|
|
198
204
|
}
|
|
199
205
|
|
|
@@ -219,13 +225,13 @@ export class Replay {
|
|
|
219
225
|
|
|
220
226
|
for (const [index, test] of tests.entries()) {
|
|
221
227
|
try {
|
|
222
|
-
await client.addTestRun(test.status, test);
|
|
228
|
+
await client.addTestRun(test.status, { ...test, overwrite: true });
|
|
223
229
|
successCount++;
|
|
224
230
|
this.onProgress({
|
|
225
231
|
current: index + 1,
|
|
226
232
|
total: tests.length,
|
|
227
233
|
test,
|
|
228
|
-
success: true
|
|
234
|
+
success: true,
|
|
229
235
|
});
|
|
230
236
|
} catch (err) {
|
|
231
237
|
failureCount++;
|
|
@@ -235,7 +241,7 @@ export class Replay {
|
|
|
235
241
|
total: tests.length,
|
|
236
242
|
test,
|
|
237
243
|
success: false,
|
|
238
|
-
error: err.message
|
|
244
|
+
error: err.message,
|
|
239
245
|
});
|
|
240
246
|
}
|
|
241
247
|
}
|
|
@@ -250,7 +256,7 @@ export class Replay {
|
|
|
250
256
|
runParams,
|
|
251
257
|
finishParams,
|
|
252
258
|
envVars,
|
|
253
|
-
runId: runId || client.runId
|
|
259
|
+
runId: runId || client.runId,
|
|
254
260
|
};
|
|
255
261
|
|
|
256
262
|
this.onLog(`Successfully replayed ${successCount}/${tests.length} tests from debug file`);
|
|
@@ -259,4 +265,4 @@ export class Replay {
|
|
|
259
265
|
}
|
|
260
266
|
}
|
|
261
267
|
|
|
262
|
-
export default Replay;
|
|
268
|
+
export default Replay;
|
package/src/utils/utils.js
CHANGED
|
@@ -353,20 +353,38 @@ function storeRunId(runId) {
|
|
|
353
353
|
fs.writeFileSync(filePath, runId);
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
/**
|
|
357
|
+
*
|
|
358
|
+
* @returns {String|null} latest run ID
|
|
359
|
+
*/
|
|
356
360
|
function readLatestRunId() {
|
|
357
361
|
try {
|
|
358
362
|
const filePath = path.join(os.tmpdir(), `testomatio.latest.run`);
|
|
359
363
|
const stats = fs.statSync(filePath);
|
|
360
364
|
const diff = +new Date() - +stats.mtime;
|
|
361
365
|
const diffHours = diff / 1000 / 60 / 60;
|
|
362
|
-
if (diffHours > 1) return;
|
|
366
|
+
if (diffHours > 1) return null;
|
|
363
367
|
|
|
364
|
-
return fs.readFileSync(filePath)?.toString()?.trim();
|
|
368
|
+
return fs.readFileSync(filePath)?.toString()?.trim() ?? null;
|
|
365
369
|
} catch (e) {
|
|
370
|
+
console.warn('Could not read latest run ID from file: ', e);
|
|
366
371
|
return null;
|
|
367
372
|
}
|
|
368
373
|
}
|
|
369
374
|
|
|
375
|
+
function cleanLatestRunId() {
|
|
376
|
+
try {
|
|
377
|
+
const filePath = path.join(os.tmpdir(), `testomatio.latest.run`);
|
|
378
|
+
const runId = readLatestRunId();
|
|
379
|
+
if (fs.existsSync(filePath)) {
|
|
380
|
+
fs.unlinkSync(filePath);
|
|
381
|
+
}
|
|
382
|
+
debug(`Cleaned latest run ID (${runId}) file`, filePath);
|
|
383
|
+
} catch (e) {
|
|
384
|
+
console.warn('Could not clean latest run ID file: ', e);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
370
388
|
function formatStep(step, shift = 0) {
|
|
371
389
|
const prefix = ' '.repeat(shift);
|
|
372
390
|
|
|
@@ -393,6 +411,7 @@ export function getPackageVersion() {
|
|
|
393
411
|
|
|
394
412
|
export {
|
|
395
413
|
ansiRegExp,
|
|
414
|
+
cleanLatestRunId,
|
|
396
415
|
isSameTest,
|
|
397
416
|
fetchSourceCode,
|
|
398
417
|
fetchSourceCodeFromStackTrace,
|
package/src/xmlReader.js
CHANGED
|
@@ -217,6 +217,7 @@ class XmlReader {
|
|
|
217
217
|
if (test.example) r.example = test.example;
|
|
218
218
|
if (test.file) r.file = test.file;
|
|
219
219
|
r.create = true;
|
|
220
|
+
r.overwrite = true;
|
|
220
221
|
if (r.status === 'Passed') r.status = STATUS.PASSED;
|
|
221
222
|
if (r.status === 'Failed') r.status = STATUS.FAILED;
|
|
222
223
|
if (r.status === 'Skipped') r.status = STATUS.SKIPPED;
|
|
@@ -293,6 +294,7 @@ class XmlReader {
|
|
|
293
294
|
title,
|
|
294
295
|
suite_title,
|
|
295
296
|
run_time,
|
|
297
|
+
retry: false,
|
|
296
298
|
});
|
|
297
299
|
});
|
|
298
300
|
});
|
|
@@ -569,6 +571,7 @@ function reduceTestCases(prev, item) {
|
|
|
569
571
|
root_suite_id: TESTOMATIO_SUITE,
|
|
570
572
|
suite_title: suiteTitle,
|
|
571
573
|
files,
|
|
574
|
+
retry: false,
|
|
572
575
|
});
|
|
573
576
|
});
|
|
574
577
|
return prev;
|