@mablhq/mabl-cli 2.100.6 → 2.101.2

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.
@@ -30,6 +30,7 @@ class DownloadStep extends MablStepV2_1.MablStepV2 {
30
30
  file_size_bytes_max: descriptor.sizeBytesMax,
31
31
  file_size_bytes_min: descriptor.sizeBytesMin,
32
32
  ai_prompt: descriptor.aiPrompt,
33
+ file_name_match_mode: descriptor.fileNameMatchMode,
33
34
  };
34
35
  }
35
36
  getStepName() {
@@ -57,6 +58,7 @@ class DownloadStep extends MablStepV2_1.MablStepV2 {
57
58
  fileMd5: fileAttributes.file_md5,
58
59
  fileSha256: fileAttributes.file_sha256,
59
60
  aiPrompt: fileAttributes.ai_prompt,
61
+ fileNameMatchMode: fileAttributes.file_name_match_mode,
60
62
  };
61
63
  const isPdfDownload = awaitDownloadAction instanceof AwaitPDFDownloadAction_1.AwaitPDFDownloadAction;
62
64
  if (isPdfDownload) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mablhq/mabl-cli",
3
- "version": "2.100.6",
3
+ "version": "2.101.2",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "The official mabl command line interface tool",
6
6
  "main": "index.js",
@@ -16,6 +16,7 @@ const type_utils_1 = require("../../type-utils");
16
16
  const variable_names_util_1 = require("../../variable-names-util");
17
17
  const BaseStep_1 = __importDefault(require("./BaseStep"));
18
18
  const DownloadStep_1 = require("../../../../mablscript/steps/DownloadStep");
19
+ const fileNameMatchUtil_1 = require("../../../../util/fileNameMatchUtil");
19
20
  const yaml_utils_1 = require("../yaml-utils");
20
21
  exports.DOWNLOAD_COMPLETE_POLLING_INTERVAL_MILLISECONDS = 200;
21
22
  const MISSING_DATA_STATUS = 'MISSING DATA';
@@ -90,6 +91,7 @@ class DownloadBaseStep extends BaseStep_1.default {
90
91
  this.stepSymbol = 'assert_download';
91
92
  this.awaitParams = values.awaitParameters || {};
92
93
  this.aiPrompt = values.aiPrompt;
94
+ this.fileNameMatchMode = values.fileNameMatchMode;
93
95
  this.options = {
94
96
  editable: true,
95
97
  executionContext: 'background',
@@ -146,6 +148,7 @@ class DownloadBaseStep extends BaseStep_1.default {
146
148
  fileMd5: getAssertionValue(DownloadProperty.FileMd5),
147
149
  fileSha256: getAssertionValue(DownloadProperty.FileSha256),
148
150
  aiPrompt: this.aiPrompt,
151
+ fileNameMatchMode: this.fileNameMatchMode,
149
152
  detected: {
150
153
  file_mime_type: this.detectedProps?.file_mime_type,
151
154
  file_name: this.detectedProps?.file_name,
@@ -168,6 +171,9 @@ class DownloadBaseStep extends BaseStep_1.default {
168
171
  if (this.aiPrompt) {
169
172
  stepParams.ai_prompt = this.aiPrompt;
170
173
  }
174
+ if (this.fileNameMatchMode && this.fileNameMatchMode !== 'equals') {
175
+ stepParams.file_name_match_mode = this.fileNameMatchMode;
176
+ }
171
177
  this.assertions
172
178
  .filter((assertion) => assertion.expected)
173
179
  .forEach((assertion) => {
@@ -235,11 +241,14 @@ function fileMimeTypeChecker(assertion, downloadData) {
235
241
  }
236
242
  return MISSING_DATA_STATUS;
237
243
  }
238
- function fileNameChecker(assertion, downloadData) {
244
+ function fileNameChecker(assertion, downloadData, fileNameMatchMode) {
239
245
  if (downloadData?.[DownloadProperty.FileName]) {
240
246
  const filename = removeFileAutoNumbering(DownloadBaseStep.downloadPathToFilename(downloadData[DownloadProperty.FileName]) ?? '');
241
247
  const expected = removeFileAutoNumbering(assertion.expected);
242
- return filename === expected || expected === ''
248
+ if (expected === '') {
249
+ return exports.SUCCESS_STATUS;
250
+ }
251
+ return (0, fileNameMatchUtil_1.evaluateFileNameMatch)(filename, expected, fileNameMatchMode)
243
252
  ? exports.SUCCESS_STATUS
244
253
  : generateFail(expected, filename, 'Unexpected file name');
245
254
  }
@@ -297,7 +306,12 @@ function humanizeDownloadBaseStep(step) {
297
306
  (!assertion.expected || !cleanFilename)) {
298
307
  return 'Assert file downloaded';
299
308
  }
300
- return `Assert ${assertion.desc} equals ${assertionTarget} for download ${cleanFilename}`;
309
+ const matchVerb = assertion.name === DownloadProperty.FileName &&
310
+ typeof step.fileNameMatchMode === 'string' &&
311
+ step.fileNameMatchMode !== 'equals'
312
+ ? step.fileNameMatchMode.replace('_', ' ')
313
+ : 'equals';
314
+ return `Assert ${assertion.desc} ${matchVerb} ${assertionTarget} for download ${cleanFilename}`;
301
315
  })
302
316
  .join('\t');
303
317
  }
@@ -313,6 +327,11 @@ function downloadBaseStepToAIString(step) {
313
327
  result.assertions = activeAssertions.map((a) => ({
314
328
  property: a.name,
315
329
  expected: a.expected,
330
+ ...(a.name === DownloadProperty.FileName &&
331
+ step.fileNameMatchMode &&
332
+ step.fileNameMatchMode !== 'equals'
333
+ ? { match_mode: step.fileNameMatchMode }
334
+ : {}),
316
335
  }));
317
336
  }
318
337
  if (step.aiPrompt?.userPrompt) {
@@ -97,6 +97,7 @@ class DownloadPdfStep extends DownloadBaseStep_1.default {
97
97
  assertions,
98
98
  awaitParameters,
99
99
  aiPrompt: mablDownloadStep.descriptor.aiPrompt,
100
+ fileNameMatchMode: mablDownloadStep.descriptor.fileNameMatchMode,
100
101
  });
101
102
  }
102
103
  static create(descriptor) {
@@ -121,6 +122,11 @@ function downloadPdfStepToAIString(step) {
121
122
  result.assertions = activeAssertions.map((a) => ({
122
123
  property: a.name,
123
124
  expected: a.expected,
125
+ ...(a.name === DownloadBaseStep_1.DownloadProperty.FileName &&
126
+ step.fileNameMatchMode &&
127
+ step.fileNameMatchMode !== 'equals'
128
+ ? { match_mode: step.fileNameMatchMode }
129
+ : {}),
124
130
  }));
125
131
  }
126
132
  if (step.aiPrompt?.userPrompt) {
@@ -82,6 +82,7 @@ class DownloadStep extends DownloadBaseStep_1.default {
82
82
  assertions,
83
83
  awaitParameters,
84
84
  aiPrompt: mablDownloadStep.descriptor.aiPrompt,
85
+ fileNameMatchMode: mablDownloadStep.descriptor.fileNameMatchMode,
85
86
  });
86
87
  }
87
88
  }
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.PROCESSED_DIR_NAME = void 0;
37
+ exports.createFilenameMatcher = createFilenameMatcher;
37
38
  exports.getAwaitDownloadTimeout = getAwaitDownloadTimeout;
38
39
  exports.awaitDownload = awaitDownload;
39
40
  exports.getLatestFile = getLatestFile;
@@ -47,15 +48,41 @@ const AWAIT_DOWNLOAD_MAX_TIMEOUT_MILLIS = 60_000;
47
48
  const FILE_MIN_STABLE_TIME_MILLIS = 3000;
48
49
  const AWAIT_DOWNLOAD_POLLING_INTERVAL_MILLISECONDS = 500;
49
50
  exports.PROCESSED_DIR_NAME = 'Processed';
51
+ function createFilenameMatcher(pattern, mode) {
52
+ if (!pattern) {
53
+ return () => true;
54
+ }
55
+ switch (mode) {
56
+ case 'contains':
57
+ return (fullPath) => path.basename(fullPath).includes(pattern);
58
+ case 'starts_with':
59
+ return (fullPath) => path.basename(fullPath).startsWith(pattern);
60
+ case 'ends_with':
61
+ return (fullPath) => path.basename(fullPath).endsWith(pattern);
62
+ case 'matches_regex': {
63
+ let regex;
64
+ try {
65
+ regex = new RegExp(pattern);
66
+ }
67
+ catch {
68
+ return () => false;
69
+ }
70
+ return (fullPath) => regex.test(path.basename(fullPath));
71
+ }
72
+ case 'equals':
73
+ default:
74
+ return (fullPath) => fullPath.toLowerCase().endsWith(pattern.toLowerCase());
75
+ }
76
+ }
50
77
  function getAwaitDownloadTimeout() {
51
78
  return AWAIT_DOWNLOAD_MAX_TIMEOUT_MILLIS;
52
79
  }
53
- async function awaitDownload(downloadDirectory, filePathSuffix, timeoutMillis = getAwaitDownloadTimeout()) {
80
+ async function awaitDownload(downloadDirectory, fileMatcher, timeoutMillis = getAwaitDownloadTimeout()) {
54
81
  const startTime = Date.now();
55
82
  let foundDownloadPath;
56
83
  let downloadComplete = false;
57
84
  while (Date.now() - timeoutMillis < startTime && !downloadComplete) {
58
- const latestFile = await getLatestFile(downloadDirectory, filePathSuffix);
85
+ const latestFile = await getLatestFile(downloadDirectory, fileMatcher);
59
86
  if (latestFile) {
60
87
  downloadComplete = true;
61
88
  foundDownloadPath = latestFile;
@@ -66,7 +93,10 @@ async function awaitDownload(downloadDirectory, filePathSuffix, timeoutMillis =
66
93
  }
67
94
  return foundDownloadPath;
68
95
  }
69
- async function getLatestFile(downloadDirectory, filePathSuffix) {
96
+ async function getLatestFile(downloadDirectory, fileMatcher) {
97
+ const matchFn = typeof fileMatcher === 'string'
98
+ ? (fullPath) => fullPath.toLowerCase().endsWith(fileMatcher.toLowerCase())
99
+ : fileMatcher;
70
100
  const readDirectoryAsync = util.promisify(fs.readdir);
71
101
  const filesInDownloadDir = await readDirectoryAsync(downloadDirectory);
72
102
  let latestFileTime = 0;
@@ -79,7 +109,7 @@ async function getLatestFile(downloadDirectory, filePathSuffix) {
79
109
  stats.ctimeMs > latestFileTime &&
80
110
  Date.now() - stats.mtimeMs >= FILE_MIN_STABLE_TIME_MILLIS &&
81
111
  !fullPath.includes('.crDownload') &&
82
- fullPath.toLowerCase().endsWith(filePathSuffix.toLowerCase())) {
112
+ matchFn(fullPath)) {
83
113
  latestFileTime = stats.ctimeMs;
84
114
  latestFilePath = fullPath;
85
115
  }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.evaluateFileNameMatch = evaluateFileNameMatch;
4
+ function evaluateFileNameMatch(actualFileName, expectedPattern, mode) {
5
+ if (!expectedPattern) {
6
+ return true;
7
+ }
8
+ switch (mode) {
9
+ case 'contains':
10
+ return actualFileName.includes(expectedPattern);
11
+ case 'starts_with':
12
+ return actualFileName.startsWith(expectedPattern);
13
+ case 'ends_with':
14
+ return actualFileName.endsWith(expectedPattern);
15
+ case 'matches_regex': {
16
+ try {
17
+ return new RegExp(expectedPattern).test(actualFileName);
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ case 'equals':
24
+ default:
25
+ return actualFileName === expectedPattern;
26
+ }
27
+ }