runner-runtime 1.0.90 → 1.0.91

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/events/index.js DELETED
@@ -1,408 +0,0 @@
1
- const atomicSleep = require("atomic-sleep"),
2
- jsonpath = require("jsonpath"),
3
- aTools = require("apipost-tools"),
4
- JSON5 = require('json5'),
5
- _ = require('lodash'),
6
- { returnBoolean, variableReplaceForCondition, variableReplace, getVariableActedUpon, convertEndRuntimeState, repeatArrayToLength } = require('../libs/utils');
7
- const executeApi = require('./api'),
8
- executeWait = require('./wait');
9
-
10
- const events = {
11
- api: executeApi,
12
- sample: executeApi,
13
- sse: executeApi,
14
- request: executeApi, // 特殊一些。1、相当于复制;2、无目录 todo....
15
- script: executeApi,
16
- assert: executeApi,
17
- assert_visual: executeApi,
18
- sql: executeApi,
19
- wait: executeWait, // ok
20
- if: async (event, option, callback, eventRuntimeData, eventResultList) => { // ok
21
- try {
22
- const { data, children } = event;
23
- const finalChildren = _.filter(children, (o) => {
24
- return _.toInteger(o?.enabled > 0)
25
- });
26
- const exp = variableReplaceForCondition(data?.var, eventRuntimeData?.variables, option),
27
- compare = data?.compare, value = variableReplaceForCondition(data?.value, eventRuntimeData?.variables, option);
28
- const isMatched = returnBoolean(exp, compare, value);
29
- const runtimeState = convertEndRuntimeState(eventRuntimeData, event?.event_id) || {}
30
- const finalIfData = {
31
- action: 'request',
32
- data: _.assign(
33
- {
34
- data: {
35
- isMatched,
36
- condition: {
37
- leftValue: {
38
- variable: data?.var,
39
- value: exp,
40
- actedUpon: getVariableActedUpon(eventRuntimeData, _.trim(data?.var, "{}")) || 'Fixed'
41
- },
42
- compare,
43
- rightValue: {
44
- variable: data?.value,
45
- value: value,
46
- actedUpon: getVariableActedUpon(eventRuntimeData, _.trim(data?.value, "{}")) || 'Fixed'
47
- }
48
- }
49
- }
50
- },
51
- _.pick(runtimeState, ['startTime', 'endTime', 'currentVariables']),
52
- _.pick(event, ['project_id', 'test_id', 'event_id', 'type']),
53
- _.pick(_.get(eventRuntimeData, event?.event_id), ['iteration_id']),
54
- )
55
- };
56
-
57
- eventResultList.push(finalIfData?.data)
58
- callback(finalIfData);
59
-
60
- if (isMatched && _.isArray(finalChildren) && !_.isEmpty(finalChildren)) {
61
- const { iterationData } = option, iterationDataArr = [iterationData];
62
-
63
- // if (!_.isEmpty(iterationData) && _.isObject(iterationData)) {
64
- // iterationDataArr = _.flatMap([iterationData], (element) => _.times(_.size(finalChildren), () => element));
65
- // }
66
- await iterationEvent(finalChildren, _.assign(_.cloneDeep(option), { iterationDataArr }), callback, eventRuntimeData, eventResultList)
67
- }
68
-
69
- } catch (e) {
70
- throw ({
71
- action: 'systemError',
72
- message: String(e),
73
- event_id: event?.event_id
74
- })
75
- }
76
-
77
- return;
78
- },
79
- begin: async (event, option, callback, eventRuntimeData, eventResultList) => { // ok
80
- try {
81
- const { children } = event;
82
- const finalChildren = _.filter(children, (o) => {
83
- return _.toInteger(o?.enabled > 0)
84
- });
85
-
86
- if (_.isArray(finalChildren) && !_.isEmpty(finalChildren)) {
87
- const { iterationData } = option, iterationDataArr = [iterationData];
88
-
89
- // if (!_.isEmpty(iterationData) && _.isObject(iterationData)) {
90
- // iterationDataArr = _.flatMap([iterationData], (element) => _.times(_.size(finalChildren), () => element));
91
- // }
92
-
93
- await iterationEvent(finalChildren, _.assign(_.cloneDeep(option), { iterationDataArr }), callback, eventRuntimeData, eventResultList)
94
- // 分组不再支持测试数据(无实际意义),测试数据只针对循环单独设置
95
- // const { iterationData, enable_data } = data;
96
-
97
- // if (_.toInteger(enable_data) > 0) {
98
- // await iterationEvent(finalChildren, _.assign(_.cloneDeep(option), { iterationDataArr: iterationData }), callback, eventRuntimeData, eventResultList)
99
- // } else {
100
- // await iterationEvent(finalChildren, option, callback, eventRuntimeData, eventResultList)
101
- // }
102
- }
103
- } catch (e) {
104
- throw ({
105
- action: 'systemError',
106
- message: String(e),
107
- event_id: event?.event_id
108
- })
109
- }
110
-
111
- return;
112
- },
113
- loop: async (event, option, callback, eventRuntimeData, eventResultList) => { // ok
114
- try {
115
- const { data, children } = event;
116
- const finalChildren = _.filter(children, (o) => {
117
- return _.toInteger(o?.enabled > 0)
118
- })
119
-
120
- const sleep = _.toInteger(variableReplace(_.get(data, 'sleep', 0), eventRuntimeData?.variables, option));
121
- const startTime = _.toInteger(_.get(option, 'startTime', Date.now()));
122
- const timeout = _.toInteger(_.get(option, 'timeout', _.toInteger(variableReplace(_.get(data, 'loop_timeout', 0), eventRuntimeData?.variables, option))));
123
-
124
- if (_.isArray(finalChildren) && !_.isEmpty(finalChildren)) {
125
- const iterationData = [];
126
- const enableData = _.toInteger(data?.enable_data) || 1;
127
-
128
- if (enableData > 0) {
129
- const loopDataType = _.toInteger(data?.loop_data_type) || 1;
130
- switch (loopDataType) {
131
- case 1:
132
- _.assign(iterationData, _.values(_.get(data, 'loop_traverse_data.iterationData', [])))
133
- break;
134
- case 2:
135
- const previousEventId = _.trim(_.get(data, 'loop_iteration_data'));
136
-
137
- if (_.isString(previousEventId) && previousEventId != '') {
138
- const dataTypeMap = {
139
- 1: `response.body`,
140
- 2: `response.headers`,
141
- 3: `response.cookies`,
142
- 4: `response.code`,
143
- 5: `request.url`,
144
- 6: `request.method`,
145
- 7: `request.querys`,
146
- 8: `request.body`,
147
- 9: `request.headers`,
148
- 10: `request.cookies`,
149
- };
150
- const dataSourceType = _.toInteger(_.get(data, 'loop_extract.var'));
151
- const responseData = _.get(eventRuntimeData, `${previousEventId}.${dataTypeMap[dataSourceType] || "response.body"}`);
152
- const dataJsonPath = _.trim(String(_.get(data, 'loop_extract.value', '$')));
153
-
154
- // 获取前置接口的数据
155
- let preRequestData = undefined;
156
- switch (dataSourceType) {
157
- case 1:
158
- case 8:
159
- try {
160
- if (_.isString(responseData)) {
161
- preRequestData = jsonpath.value(JSON5.parse(responseData), dataJsonPath);
162
- } else {
163
- preRequestData = jsonpath.value(responseData, dataJsonPath);
164
- }
165
- } catch (e) { }
166
- break;
167
- case 2:
168
- case 3:
169
- case 7:
170
- case 9:
171
- case 10:
172
- preRequestData = _.get(responseData, dataJsonPath);
173
- break;
174
- case 4:
175
- case 5:
176
- case 6:
177
- preRequestData = _.cloneDeep(responseData);
178
- break;
179
- }
180
-
181
- if (_.isString(preRequestData)) {
182
- try {
183
- preRequestData = JSON5.parse(preRequestData)
184
- } catch (e) { }
185
- }
186
-
187
- if (_.isNumber(preRequestData)) {
188
- preRequestData = String(preRequestData);
189
- }
190
-
191
- let loopVariableArr = [];
192
-
193
- if (_.isArray(preRequestData)) {
194
- loopVariableArr = _.map(preRequestData, (arr) => {
195
- if (_.isObject(arr)) {
196
- return _.pickBy(arr, (o) => {
197
- return !_.isObject(o)
198
- })
199
- } else {
200
- return {
201
- "0": arr
202
- }
203
- }
204
- })
205
- } else if (_.isObject(preRequestData)) {
206
- _.forEach(preRequestData, (val, key) => {
207
- if (!_.isObject(val)) {
208
- const tempObj = {};
209
- _.set(tempObj, key, val);
210
- loopVariableArr.push(tempObj)
211
- }
212
- })
213
- } else if (_.isString(preRequestData)) {
214
- loopVariableArr = _.map(_.values(preRequestData), (val, key) => {
215
- const tempObj = {};
216
- tempObj[String(key)] = val;
217
- return tempObj;
218
- })
219
- }
220
-
221
- _.assign(iterationData, loopVariableArr);
222
- }
223
-
224
- break;
225
- case 3:
226
- {
227
- let loopVariable = variableReplace(_.get(data, 'loop_variable', ''), eventRuntimeData?.variables, option);
228
-
229
- if (_.isString(loopVariable)) {
230
- try {
231
- loopVariable = JSON5.parse(loopVariable)
232
- } catch (e) { }
233
- }
234
-
235
- if (_.isNumber(loopVariable)) {
236
- loopVariable = String(loopVariable);
237
- }
238
-
239
- let loopVariableArr = [];
240
- if (_.isArray(loopVariable)) {
241
- loopVariableArr = _.map(loopVariable, (arr) => {
242
- if (_.isObject(arr)) {
243
- return _.pickBy(arr, (o) => {
244
- return !_.isObject(o)
245
- })
246
- } else {
247
- return {
248
- "0": arr
249
- }
250
- }
251
- })
252
- } else if (_.isObject(loopVariable)) {
253
- _.forEach(loopVariable, (val, key) => {
254
- if (!_.isObject(val)) {
255
- const tempObj = {};
256
- _.set(tempObj, key, val);
257
- loopVariableArr.push(tempObj)
258
- }
259
- })
260
- } else if (_.isString(loopVariable)) {
261
- loopVariableArr = _.map(_.values(loopVariable), (val, key) => {
262
- const tempObj = {};
263
- tempObj[String(key)] = val;
264
- return tempObj;
265
- })
266
- }
267
-
268
- _.assign(iterationData, loopVariableArr);
269
- }
270
- break;
271
- case 4:
272
- {
273
- let loopVariable = _.split(_.get(data, 'loop_fixed_value', ''), ',');
274
- let loopVariableArr = [];
275
-
276
- _.forEach(loopVariable, (val, key) => {
277
- if (!_.isObject(val)) {
278
- loopVariableArr.push(
279
- {
280
- "$key": key,
281
- "$item": val
282
- }
283
- )
284
- }
285
- })
286
- _.assign(iterationData, loopVariableArr);
287
- }
288
-
289
- break;
290
- }
291
- }
292
-
293
- // 获取循环次数
294
- const loopType = _.toInteger(_.get(data, 'loop_type', 1));
295
-
296
- if (_.includes([1, 2], loopType)) {
297
- const limit = loopType === 1 ? _.size(iterationData) : _.toInteger(_.get(data, 'limit', 1));
298
-
299
- if (limit > 0) {
300
- let iterationDataArr = _.cloneDeep(iterationData);
301
-
302
- if (loopType === 2) {
303
- iterationDataArr = repeatArrayToLength(iterationDataArr, limit)
304
- }
305
-
306
- iterationDataArr = _.flatMap(iterationDataArr, (element) => _.times(_.size(finalChildren), () => element));
307
- const events = _.flatten(_.times(limit, () => _.cloneDeep(finalChildren)));
308
- await iterationEvent(events, _.assign(_.cloneDeep(option), { iterationDataArr, sleep, startTime, timeout }), callback, eventRuntimeData, eventResultList)
309
- }
310
- } else if (_.includes([3], loopType)) {
311
- while (true) {
312
- const condition = _.get(option, 'condition', _.get(data, 'loop_condition', {}));
313
- const exp = variableReplaceForCondition(condition?.var, eventRuntimeData?.variables, option), compare = condition?.compare, value = variableReplaceForCondition(condition?.value, eventRuntimeData?.variables, option);
314
-
315
- if ((timeout === 0 || (timeout > 0 && startTime + timeout > Date.now())) && !returnBoolean(exp, compare, value)) {
316
- await iterationEvent(_.cloneDeep(finalChildren), _.assign(_.cloneDeep(option), { iterationDataArr: iterationData, sleep, condition, startTime, timeout }), callback, eventRuntimeData, eventResultList);
317
- } else {
318
- break;
319
- }
320
- }
321
- }
322
- }
323
- } catch (e) {
324
- throw ({
325
- action: 'systemError',
326
- message: String(e),
327
- event_id: event?.event_id
328
- })
329
- }
330
-
331
- return;
332
- }
333
- };
334
-
335
- // 调度器核心函数,用于递归执行具体场景步骤
336
- const executeEvent = async (event, option, callback, eventRuntimeData, eventResultList) => {
337
- if (_.isUndefined(event.event_id) && option?.scene === 'http_request') {
338
- _.set(event, 'event_id', _.get(event, 'data.target_id'))
339
- }
340
-
341
- _.assign(eventRuntimeData?.variables?.iterationData, _.get(option, 'iterationData'))
342
- _.set(eventRuntimeData, [event.event_id, 'iteration_id'], aTools.snowflakeId());
343
-
344
- const eventCall = _.get(events, `${event?.type}`);
345
-
346
- if (_.isFunction(eventCall)) {
347
- const { total, scene } = option;
348
-
349
- // 增加控制器开始时间
350
- _.set(eventRuntimeData, [event.event_id, 'iteration', 'startTime'], Date.now());
351
-
352
- // 增加初始化变量
353
- const { environment = {}, variables = {}, _globals = {}, iterationData = {} } = _.get(eventRuntimeData, 'variables', {});
354
- _.set(eventRuntimeData, [event.event_id, 'iteration', 'startVariables'], _.cloneDeep({ environment, variables, iterationData, globals: _globals }));
355
-
356
- try {
357
- await eventCall(event, option, callback, eventRuntimeData, eventResultList);
358
- if (scene == 'auto_test' && _.isInteger(event?.progress)) {
359
- callback({
360
- action: 'process',
361
- data: {
362
- processCurrentCount: event?.progress + 1,
363
- processTotalCount: total,
364
- event_id: event?.event_id,
365
- testing_id: event?.test_id,
366
- }
367
- })
368
- }
369
- } catch (e) { // 中止跳出执行的系统错误
370
- if (_.toInteger(option?.ignore_error) < 1) {
371
- throw (e)
372
- }
373
- }
374
- }
375
- }
376
-
377
- const iterationEvent = async (events, option, callback, eventRuntimeData, eventResultList) => {
378
- const event = events.shift();
379
-
380
- if (_.isObject(event) && _.has(event, 'type') && _.toInteger(event?.enabled) > 0) {
381
- let { iterationDataArr } = option;
382
- if (!_.isArray(iterationDataArr)) {
383
- iterationDataArr = _.values(iterationDataArr);
384
- }
385
-
386
- const iterationData = iterationDataArr.shift() || {};
387
- try {
388
- const { sleep } = option;
389
- await executeEvent(event, { ...option, iterationData }, callback, eventRuntimeData, eventResultList);
390
-
391
- if (_.toInteger(sleep) > 0) {
392
- atomicSleep(sleep);
393
- }
394
- } catch (e) {
395
- if (_.toInteger(option?.ignore_error) < 1) {
396
- throw (e)
397
- }
398
- }
399
-
400
- if (_.isArray(events) && !_.isEmpty(events)) {
401
- await iterationEvent(events, { ...option, iterationDataArr }, callback, eventRuntimeData, eventResultList);
402
- }
403
- }
404
- }
405
-
406
- module.exports = {
407
- executeEvent, iterationEvent
408
- };
package/events/wait.js DELETED
@@ -1,39 +0,0 @@
1
- const atomicSleep = require("atomic-sleep"),
2
- _ = require('lodash');
3
- const { variableReplace, convertEndRuntimeState } = require("../libs/utils");
4
-
5
- module.exports = async (event, option, callback, eventRuntimeData, eventResultList) => {
6
- try {
7
- const sleep = _.toInteger(variableReplace(event?.data?.sleep, eventRuntimeData?.variables, option)) || 0;
8
-
9
- if (sleep > 0) {
10
- atomicSleep(sleep)
11
- }
12
-
13
- const runtimeState = convertEndRuntimeState(eventRuntimeData, event?.event_id) || {}
14
- const finalWaitData = {
15
- action: 'request',
16
- data: _.assign(
17
- {
18
- data: {
19
- sleep
20
- }
21
- },
22
- _.pick(runtimeState, ['startTime', 'endTime', 'currentVariables']),
23
- _.pick(event, ['project_id', 'test_id', 'event_id', 'type']),
24
- _.pick(_.get(eventRuntimeData, event?.event_id), ['iteration_id']),
25
- )
26
- };
27
-
28
- eventResultList.push(finalWaitData?.data)
29
- callback(finalWaitData);
30
- } catch (e) {
31
- throw ({
32
- action: 'systemError',
33
- message: String(e),
34
- event_id: event?.event_id
35
- })
36
- }
37
-
38
- return;
39
- }
package/index.js DELETED
@@ -1,180 +0,0 @@
1
- const { repeatArrayToLength, formatAutotestReport, formatAutotestReportList, getInsideVariables } = require('./libs/utils');
2
- const _ = require('lodash');
3
- const { mockExp } = require('exp-mock');
4
- const aTools = require("apipost-tools");
5
- const { executeEvent, iterationEvent } = require('./events');
6
- const ABORT_RECURSION_ERROR = ['systemError']; //需要跳出递归的错误
7
-
8
- // 执行场景
9
- const run = async (events, option, callback) => {
10
- const { iterationCount, iterationData, scene } = option;
11
-
12
- if (scene == 'auto_test') {
13
- events = _.filter(events, function (o) { return _.toInteger(o?.enabled) > 0; })
14
- }
15
-
16
- const tempEvents = _.cloneDeep(_.flatten(new Array(scene == 'auto_test' ? _.max([_.toInteger(iterationCount), 1]) : 1).fill(events))) || [];
17
- // const iterationDataArr = _.flatMap(repeatArrayToLength(_.cloneDeep(iterationData), _.max([_.toInteger(iterationCount), 1])), (element) => _.times(_.size(events), () => element));
18
- const eventOptions = _.pick(option, ["scene", "lang", "project", "env", "globals", "cookies", "system_configs", "custom_functions", "collection", "database_configs", "enable_sandbox", "sleep", "name", "testing_id"]);
19
-
20
- // 初始化变量
21
- const eventRuntimeData = {
22
- variables: {
23
- environment: {},
24
- variables: {},
25
- globals: {},
26
- _globals: {},
27
- iterationData: {},
28
- iterationDataArr: []
29
- }
30
- };
31
-
32
- const iterationDataArrTemp = [];
33
- if (scene != 'http_request') {
34
- // 解析测试数据
35
- const regex = /{{([^}]+)}}/g;
36
- _.forEach(_.values(_.cloneDeep(iterationData)), (obj, index) => {
37
- const cloneObj = _.cloneDeep(obj);
38
- if (_.isObject(obj) && !_.isEmpty(obj)) {
39
- _.forEach(obj, (val, key) => {
40
- if (_.trim(val) != '' && _.isString(val)) {
41
- const matches = val.match(regex);
42
-
43
- if (!_.isEmpty(matches)) {
44
- _.forEach(matches, (match) => {
45
- try {
46
- const tmpVal = mockExp(match, getInsideVariables(), option?.lang, option?.custom_functions || {});
47
-
48
- if (tmpVal != match && !_.isUndefined(tmpVal)) {
49
- _.set(cloneObj, [key], _.replace(val, match, tmpVal));
50
-
51
- }
52
- } catch (e) { }
53
- })
54
- }
55
- }
56
- })
57
- }
58
-
59
- iterationDataArrTemp.push(cloneObj)
60
- })
61
- }
62
-
63
- const iterationDataArr = _.flatMap(repeatArrayToLength(_.cloneDeep(iterationDataArrTemp), _.max([_.toInteger(iterationCount), 1])), (element) => _.times(_.size(events), () => element));
64
-
65
- const tempVars = { environment: _.get(option, 'env.environment', {}), globals: _.get(option, 'globals', {}), iterationDataArr };
66
-
67
- ['environment', 'globals', 'iterationDataArr'].forEach((type) => {
68
- _.forEach(tempVars[type], (value, key) => {
69
- if (_.isUndefined(_.get(eventRuntimeData, ['variables', type, key]))) {
70
- _.set(eventRuntimeData, ['variables', type, key], value);
71
-
72
- if (type === 'globals') {
73
- _.set(eventRuntimeData, ['variables', `_${type}`, key], value);
74
- }
75
- }
76
- })
77
- })
78
-
79
- const eventResultList = [];
80
- const startTimeAt = Date.now();
81
-
82
- // 单接口测试
83
- if (scene == 'http_request') {
84
- const event = _.first(tempEvents);
85
-
86
- if (_.isObject(event) && _.has(event, 'type')) {
87
- try {
88
- await executeEvent(event, _.assign(eventOptions, { iterationData: {} }), callback, eventRuntimeData, eventResultList);
89
- } catch (e) {
90
- if (_.includes(ABORT_RECURSION_ERROR, e?.action)) {
91
- throw (e);
92
- }
93
- }
94
- }
95
- } else {
96
- const finalEvents = [];
97
- _.forEach(tempEvents, (event, progress) => {
98
- if (event?.enabled > 0) {
99
- finalEvents.push(_.assign(_.cloneDeep(event), { progress }))
100
- }
101
- })
102
-
103
- // 自动化测试
104
- if (!_.isEmpty(finalEvents)) {
105
- const total = _.size(finalEvents);
106
- try {
107
- await iterationEvent(finalEvents, _.assign(eventOptions, { iterationDataArr, total }), callback, eventRuntimeData, eventResultList);
108
- } catch (e) {
109
- callback(e)
110
- }
111
- }
112
-
113
- const tempVars = _.pick(_.get(eventRuntimeData, 'variables'), ['environment', '_globals']);
114
-
115
- // 最后返回报告
116
- callback({
117
- action: 'complete',
118
- data: _.assign(
119
- formatAutotestReport(startTimeAt, eventResultList),
120
- {
121
- list: formatAutotestReportList(eventResultList),
122
- variables: {
123
- globals: option?.enable_sandbox > 0 ? {} : _.get(tempVars, '_globals', {}),
124
- environment: option?.enable_sandbox > 0 ? {} : _.get(tempVars, 'environment', {})
125
- }
126
- },
127
- _.pick(_.get(option, 'env', {}), ['env_id', 'env_name']),
128
- { testing_id: _.get(_.first(eventResultList), 'test_id') },
129
- { report_name: `${option?.name}` },
130
- { report_id: aTools.snowflakeId() }
131
- )
132
- })
133
- }
134
- }
135
-
136
- const request2HAR = (events, option) => {
137
- return new Promise(async (resolve, reject) => {
138
- const eventOptions = _.pick(option, ["scene", "lang", "project", "env", "globals", "cookies", "system_configs", "custom_functions", "collection", "database_configs", "enable_sandbox", "sleep", "name", "testing_id"]);
139
-
140
- // 初始化变量
141
- const eventRuntimeData = {
142
- variables: {
143
- environment: {},
144
- variables: {},
145
- globals: {},
146
- _globals: {},
147
- iterationData: {},
148
- iterationDataArr: []
149
- }
150
- };
151
-
152
- const tempVars = { environment: _.get(option, 'env.environment', {}), globals: _.get(option, 'globals', {}) };
153
-
154
- ['environment', 'globals'].forEach((type) => {
155
- _.forEach(tempVars[type], (value, key) => {
156
- if (_.isUndefined(_.get(eventRuntimeData, ['variables', type, key]))) {
157
- _.set(eventRuntimeData, ['variables', type, key], value);
158
-
159
- if (type === 'globals') {
160
- _.set(eventRuntimeData, ['variables', `_${type}`, key], value);
161
- }
162
- }
163
- })
164
- });
165
-
166
- const eventResultList = [];
167
- const event = _.first(events);
168
-
169
- if (_.isObject(event) && _.has(event, 'type')) {
170
- try {
171
- await executeEvent(event, _.assign(eventOptions, { scene: 'get_parsed_request' }), (data) => {
172
- resolve(data)
173
- }, eventRuntimeData, eventResultList);
174
- } catch (e) {
175
- reject(e)
176
- }
177
- }
178
- })
179
- }
180
- module.exports = { run, request2HAR }
Binary file