ff-automationv2 2.2.32-beta.0 → 2.2.32-beta.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.
- package/dist/ai/llmprompts/systemPrompts/fileVerifyActionExtractorPrompt.js +33 -23
- package/dist/automation/actions/executor.d.ts +1 -1
- package/dist/automation/actions/executor.js +3 -1
- package/dist/automation/actions/interaction/files/VerifyPDF.js +84 -133
- package/dist/automation/actions/interface/FireFlinkResponseSchema.d.ts +17 -0
- package/dist/automation/actions/interface/FireFlinkResponseSchema.js +22 -2
- package/dist/automation/actions/interface/verifyActionInterface.d.ts +2 -0
- package/dist/core/constants/supportedActions.js +1 -0
- package/dist/core/main/actionHandlerFactory.js +12 -1
- package/dist/core/main/runAutomationScript.d.ts +3 -0
- package/dist/core/main/runAutomationScript.js +21 -9
- package/dist/utils/browserCap/capability.js +4 -10
- package/dist/utils/browserCap/systemCapabilities.d.ts +6 -0
- package/dist/utils/browserCap/systemCapabilities.js +25 -0
- package/dist/utils/downloadWatcher/pdfDownloadWatcher.d.ts +8 -0
- package/dist/utils/downloadWatcher/pdfDownloadWatcher.js +61 -0
- package/dist/utils/logger/logData.d.ts +2 -0
- package/dist/utils/logger/logData.js +4 -1
- package/dist/utils/textExtractor/pdfTextExtractor.d.ts +2 -0
- package/dist/utils/textExtractor/pdfTextExtractor.js +40 -0
- package/package.json +1 -1
|
@@ -5,31 +5,41 @@ async function fileVerifyActionExtractorPrompt({ extractedDomJson, priorAndNextS
|
|
|
5
5
|
const nlpList = `VerifyPDF,CompareTwoPdf,VerifyPDFExist,CheckAccessibilityForPdfUsingPython,CheckIfPdfIsXfaFrom,VerifyElementIsDisplayed,VerifyElementIsNotDisplayed,VerifyElementIsEnabled,VerifyElementIsDisabled,VerifyElementIsClickable,VerifyNavigateURL,VerifyIfLinkIsWorking`;
|
|
6
6
|
const elementType = ['link', 'textfield', 'icon', 'button', 'radioButton', 'text', 'textarea', 'image', 'dropdown', 'checkbox', 'tab', 'action overflow button', 'hamburger icon', 'toggle button', 'suggestion', 'time picker', 'date picker', 'toaster message', 'card', 'tooltip', 'option', 'calender', 'sliders', 'visual testing'];
|
|
7
7
|
const prompt = `You are an intelligent assistant that extracts structured UI action data for file and element verification.
|
|
8
|
-
Given the step, the simplified DOM: ${extractedDomJson}, and the list of NLP names: ${nlpList} don't provide the other nlp names which are not present in the list in any manner.
|
|
8
|
+
Given the step, the simplified DOM: ${extractedDomJson}, and the list of NLP names: ${nlpList} don't provide the other nlp names which are not present in the list in any manner.
|
|
9
9
|
|
|
10
|
-
Select the perfect matching NLP name from the list that best fits the step's intent.
|
|
11
|
-
**The NLP name must match exactly one value from the approved NLP list. Do not generate or suggest any new NLP names under any condition.**
|
|
12
|
-
step may give any way, you need to get most matching and most related nlp name from the list that relates to step.
|
|
10
|
+
Select the perfect matching NLP name from the list that best fits the step's intent.
|
|
11
|
+
**The NLP name must match exactly one value from the approved NLP list. Do not generate or suggest any new NLP names under any condition.**
|
|
12
|
+
step may give any way, you need to get most matching and most related nlp name from the list that relates to step.
|
|
13
13
|
|
|
14
|
-
- Return ONLY a valid JSON object; the response must start with '{' and end with '}', with no markdown, code fences, explanations, or extra text.
|
|
15
|
-
Return **only valid JSON** in the following format:
|
|
16
|
-
{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
- Return ONLY a valid JSON object; the response must start with '{' and end with '}', with no markdown, code fences, explanations, or extra text.
|
|
15
|
+
Return **only valid JSON** in the following format:
|
|
16
|
+
{
|
|
17
|
+
"attributeValue": "Fire-Flink-x",
|
|
18
|
+
"nlpName": "x",
|
|
19
|
+
"inputText": "x",
|
|
20
|
+
"elementType": "x",
|
|
21
|
+
"isDownload": true,
|
|
22
|
+
"path": null
|
|
23
|
+
}
|
|
22
24
|
|
|
23
|
-
Rules:
|
|
24
|
-
- PDF steps: if step specifies checking/verifying text, content, or phrases inside a PDF → choose VerifyPDF and extract the expected text into inputText | if step only verifies the presence, creation, or download of the file without checking inner text → choose VerifyPDFExist with inputText: "".
|
|
25
|
-
- nlpName must be exactly one from the provided list.
|
|
26
|
-
- While matching nlpName with step (ignore spaces, case differences). If multiple NLPs are similar, choose the MOST SPECIFIC and EXACT match.
|
|
27
|
-
- Use context from the steps: ${priorAndNextSteps}, keyword and json to search for FF-inspecter.
|
|
28
|
-
- **Find the FF-inspecter attribute value of the element in the Simplified JSON whose text or any other attributes best match the step in the simplified DOM.**
|
|
29
|
-
- If no matching element found for the step or if step is file/pdf verification, return attributeValue as 'Fire-Flink-0'.
|
|
30
|
-
-
|
|
31
|
-
- Based on step give most relevant type of element. Use this list to choose elementType: ${elementType}. If elementType is not there in list return 'link'.
|
|
32
|
-
- **
|
|
33
|
-
|
|
25
|
+
Rules:
|
|
26
|
+
- PDF steps: if step specifies checking/verifying text, content, or phrases inside a PDF → choose VerifyPDF and extract the expected text into inputText | if step only verifies the presence, creation, or download of the file without checking inner text → choose VerifyPDFExist with inputText: "".
|
|
27
|
+
- nlpName must be exactly one from the provided list.
|
|
28
|
+
- While matching nlpName with step (ignore spaces, case differences). If multiple NLPs are similar, choose the MOST SPECIFIC and EXACT match.
|
|
29
|
+
- Use context from the steps: ${priorAndNextSteps}, keyword and json to search for FF-inspecter.
|
|
30
|
+
- **Find the FF-inspecter attribute value of the element in the Simplified JSON whose text or any other attributes best match the step in the simplified DOM.**
|
|
31
|
+
- If no matching element found for the step or if step is file/pdf verification, return attributeValue as 'Fire-Flink-0'.
|
|
32
|
+
- **inputText rules**: Extract ONLY the expected text phrase to verify inside the PDF or element. Strip any wrapping quotes (' or "). **NEVER include the file path or file name in inputText**. If no text is being verified, return "".
|
|
33
|
+
- Based on step give most relevant type of element. Use this list to choose elementType: ${elementType}. If elementType is not there in list return 'link'.
|
|
34
|
+
- **isDownload & path rules**:
|
|
35
|
+
- If the step contains both a file path and expected text (e.g. "Verify existing PDF contains text 'path/to/doc.pdf',Expected Text" or "Verify PDF 'path/to/doc.pdf' contains 'Expected Text'"):
|
|
36
|
+
• Extract the file path into "path" (stripped of surrounding quotes).
|
|
37
|
+
• Extract ONLY the search phrase into "inputText" (do NOT include the file path in inputText).
|
|
38
|
+
• Set isDownload: false.
|
|
39
|
+
- If verifying an existing file at a specified path or location (e.g. contains drive letter C:, /, or file extension .pdf) → set isDownload: false and path to the extracted file path string.
|
|
40
|
+
- If the step is about verifying a file downloaded via the browser (e.g. "verify downloaded pdf", "verify pdf", or no explicit file path is given) → set isDownload: true and path: null.
|
|
41
|
+
- **path formatting**: Extract the file path from the step and add extra //// for path like "C:////Users/////User/////Downloads/////file.pdf" (strip surrounding quotes).
|
|
42
|
+
- **Respond with valid JSON only. don't return any other text or don't return response in list format**
|
|
43
|
+
`;
|
|
34
44
|
return prompt;
|
|
35
45
|
}
|
|
@@ -377,5 +377,5 @@ export declare class ActionExecutor implements IActionExecutor {
|
|
|
377
377
|
otp: string;
|
|
378
378
|
regex: string;
|
|
379
379
|
} | null>, returnTypeVariableName?: string): Promise<void>;
|
|
380
|
-
verifyPDF(value: string): Promise<void>;
|
|
380
|
+
verifyPDF(value: string, isDownload?: boolean, filePath?: string | null): Promise<void>;
|
|
381
381
|
}
|
|
@@ -6692,11 +6692,13 @@ class ActionExecutor {
|
|
|
6692
6692
|
throw error;
|
|
6693
6693
|
}
|
|
6694
6694
|
}
|
|
6695
|
-
async verifyPDF(value) {
|
|
6695
|
+
async verifyPDF(value, isDownload = true, filePath = null) {
|
|
6696
6696
|
try {
|
|
6697
6697
|
await (0, VerifyPDF_js_1.verifyPDF)({
|
|
6698
6698
|
driver: this.driver,
|
|
6699
6699
|
value,
|
|
6700
|
+
isDownload,
|
|
6701
|
+
filePath,
|
|
6700
6702
|
platform: this.platform,
|
|
6701
6703
|
scriptDataAppender: this.scriptDataAppender
|
|
6702
6704
|
});
|
|
@@ -6,166 +6,117 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.verifyPDF = verifyPDF;
|
|
7
7
|
const appendScriptData_js_1 = require("../../../../fireflinkData/fireflinkScript/appendScriptData.js");
|
|
8
8
|
const logData_js_1 = require("../../../../utils/logger/logData.js");
|
|
9
|
+
const pdfDownloadWatcher_js_1 = require("../../../../utils/downloadWatcher/pdfDownloadWatcher.js");
|
|
10
|
+
const pdfTextExtractor_js_1 = require("../../../../utils/textExtractor/pdfTextExtractor.js");
|
|
9
11
|
const fs_1 = __importDefault(require("fs"));
|
|
10
12
|
const path_1 = __importDefault(require("path"));
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
//Extracting
|
|
16
|
-
async function extractTextFromPdf(buffer) {
|
|
13
|
+
async function verifyPDF(args) {
|
|
14
|
+
let calculatedDuration = 2;
|
|
15
|
+
let fileName;
|
|
17
16
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const pdfDoc = await loadingTask.promise;
|
|
27
|
-
const pageTexts = [];
|
|
28
|
-
for (let i = 1; i <= pdfDoc.numPages; i++) {
|
|
29
|
-
const page = await pdfDoc.getPage(i);
|
|
30
|
-
const textContent = await page.getTextContent();
|
|
31
|
-
const pageString = textContent.items
|
|
32
|
-
.map((item) => ("str" in item ? item.str : ""))
|
|
33
|
-
.join(" ");
|
|
34
|
-
pageTexts.push(pageString);
|
|
17
|
+
let buffer;
|
|
18
|
+
if (args.isDownload) {
|
|
19
|
+
// isDownload = true → poll downloadDir until a PDF lands
|
|
20
|
+
const fileInfo = await (0, pdfDownloadWatcher_js_1.waitForDownloadComplete)(logData_js_1.downloadDir);
|
|
21
|
+
calculatedDuration = fileInfo.durationSeconds;
|
|
22
|
+
fileName = fileInfo.file.name;
|
|
23
|
+
buffer = fs_1.default.readFileSync(fileInfo.file.fullPath);
|
|
24
|
+
await logData_js_1.logger.info("VerifyPDF: found latest file", { file: fileName, dir: logData_js_1.downloadDir, durationSeconds: fileInfo.durationSeconds });
|
|
35
25
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return buffer.toString("latin1");
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
//Downloading time
|
|
44
|
-
const DOWNLOAD_TIMEOUT_MS = 30000;
|
|
45
|
-
const POLL_INTERVAL_MS = 400;
|
|
46
|
-
const STABILITY_WAIT_MS = 250;
|
|
47
|
-
async function waitForDownloadComplete(tempFileDir, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
48
|
-
const startTime = Date.now();
|
|
49
|
-
while (Date.now() - startTime < timeoutMs) {
|
|
50
|
-
if (fs_1.default.existsSync(tempFileDir)) {
|
|
51
|
-
const allEntries = fs_1.default.readdirSync(tempFileDir);
|
|
52
|
-
let hasActiveDownload = false;
|
|
53
|
-
const pdfFiles = [];
|
|
54
|
-
// Single pass: early exit if active download is present, stat only PDF files
|
|
55
|
-
for (const file of allEntries) {
|
|
56
|
-
const lower = file.toLowerCase();
|
|
57
|
-
if (lower.endsWith(".crdownload") || lower.endsWith(".part") || lower.endsWith(".tmp")) {
|
|
58
|
-
hasActiveDownload = true;
|
|
59
|
-
break; // Download still in progress, skip remaining work this cycle
|
|
60
|
-
}
|
|
61
|
-
if (lower.endsWith(".pdf")) {
|
|
62
|
-
const fullPath = path_1.default.join(tempFileDir, file);
|
|
63
|
-
try {
|
|
64
|
-
const stat = fs_1.default.statSync(fullPath);
|
|
65
|
-
if (stat.isFile()) {
|
|
66
|
-
pdfFiles.push({ name: file, fullPath, mtime: stat.mtimeMs, size: stat.size });
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
// File locked or in transition, skip for this cycle
|
|
71
|
-
}
|
|
72
|
-
}
|
|
26
|
+
else {
|
|
27
|
+
// isDownload = false → use the explicit path directly
|
|
28
|
+
if (!args.filePath) {
|
|
29
|
+
throw new Error("VerifyPDF: isDownload is false but no filePath was provided.");
|
|
73
30
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (latest.size > 0) {
|
|
78
|
-
await new Promise(res => setTimeout(res, STABILITY_WAIT_MS));
|
|
79
|
-
try {
|
|
80
|
-
const newStat = fs_1.default.statSync(latest.fullPath);
|
|
81
|
-
if (newStat.size === latest.size && newStat.size > 0) {
|
|
82
|
-
const durationSeconds = Math.max(1, Math.ceil((Date.now() - startTime) / 1000));
|
|
83
|
-
return { file: latest, durationSeconds };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
// Retry on next cycle
|
|
88
|
-
}
|
|
89
|
-
}
|
|
31
|
+
const normalizedPath = path_1.default.normalize(args.filePath);
|
|
32
|
+
if (!fs_1.default.existsSync(normalizedPath)) {
|
|
33
|
+
throw new Error(`VerifyPDF: file not found at path: ${normalizedPath}`);
|
|
90
34
|
}
|
|
35
|
+
buffer = fs_1.default.readFileSync(normalizedPath);
|
|
36
|
+
fileName = normalizedPath;
|
|
91
37
|
}
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
throw new Error(`PDF download did not complete within ${timeoutMs / 1000} seconds in directory: ${tempFileDir}. ` +
|
|
95
|
-
`Ensure the download was triggered prior to VerifyPDF.`);
|
|
96
|
-
}
|
|
97
|
-
async function verifyPDF(args) {
|
|
98
|
-
const tempFileDir = path_1.default.resolve(process.cwd(), "session", "temp_file");
|
|
99
|
-
let calculatedDuration = 2;
|
|
100
|
-
try {
|
|
101
|
-
const { file: latestFile, durationSeconds } = await waitForDownloadComplete(tempFileDir);
|
|
102
|
-
calculatedDuration = durationSeconds;
|
|
103
|
-
logData_js_1.logger.info("VerifyPDF: found latest file", { file: latestFile.name, dir: tempFileDir, durationSeconds });
|
|
104
|
-
const buffer = fs_1.default.readFileSync(latestFile.fullPath);
|
|
38
|
+
// Validate PDF header
|
|
105
39
|
const header = buffer.subarray(0, 5).toString("ascii");
|
|
106
40
|
if (!header.startsWith("%PDF")) {
|
|
107
|
-
throw new Error(`File does not have a valid PDF header (%PDF): ${
|
|
41
|
+
throw new Error(`File does not have a valid PDF header (%PDF): ${fileName}`);
|
|
108
42
|
}
|
|
43
|
+
// Optional text content verification
|
|
109
44
|
const expectedText = args.value?.trim();
|
|
110
45
|
if (expectedText) {
|
|
111
|
-
const extractedText = await extractTextFromPdf(buffer);
|
|
112
|
-
const normalizedExtracted = normalizeText(extractedText).toLowerCase();
|
|
113
|
-
const normalizedExpected = normalizeText(expectedText).toLowerCase();
|
|
46
|
+
const extractedText = await (0, pdfTextExtractor_js_1.extractTextFromPdf)(buffer);
|
|
47
|
+
const normalizedExtracted = (0, pdfTextExtractor_js_1.normalizeText)(extractedText).toLowerCase();
|
|
48
|
+
const normalizedExpected = (0, pdfTextExtractor_js_1.normalizeText)(expectedText).toLowerCase();
|
|
114
49
|
if (!normalizedExtracted.includes(normalizedExpected)) {
|
|
115
|
-
// Secondary check on raw buffer
|
|
50
|
+
// Secondary check directly on raw buffer bytes
|
|
116
51
|
const rawContent = buffer.toString("latin1").toLowerCase();
|
|
117
52
|
if (!rawContent.includes(normalizedExpected)) {
|
|
118
|
-
throw new Error(`PDF "${
|
|
53
|
+
throw new Error(`PDF "${fileName}" does not contain expected text: "${expectedText}"`);
|
|
119
54
|
}
|
|
120
55
|
}
|
|
121
56
|
}
|
|
122
|
-
logData_js_1.logger.info("VerifyPDF passed", {
|
|
123
|
-
file:
|
|
57
|
+
await logData_js_1.logger.info("VerifyPDF passed", {
|
|
58
|
+
file: fileName,
|
|
124
59
|
expectedText: expectedText || "(none — structural check only)",
|
|
125
|
-
sizeBytes: buffer.length
|
|
126
|
-
});
|
|
127
|
-
(0, appendScriptData_js_1.appendScriptData)({
|
|
128
|
-
scriptDataAppender: args.scriptDataAppender,
|
|
129
|
-
nlpName: "Sleep",
|
|
130
|
-
stepInputs: [calculatedDuration],
|
|
131
|
-
});
|
|
132
|
-
(0, appendScriptData_js_1.appendScriptData)({
|
|
133
|
-
scriptDataAppender: args.scriptDataAppender,
|
|
134
|
-
nlpName: "GetUserHomeDirectory",
|
|
135
|
-
stepInputs: [],
|
|
136
|
-
returnTypeVariableName: "userHomeDirectory"
|
|
137
|
-
});
|
|
138
|
-
(0, appendScriptData_js_1.appendScriptData)({
|
|
139
|
-
scriptDataAppender: args.scriptDataAppender,
|
|
140
|
-
nlpName: "ConcatString",
|
|
141
|
-
stepInputs: ["variable:userHomeDirectory", "/Downloads"],
|
|
142
|
-
returnTypeVariableName: "finalpath"
|
|
143
|
-
});
|
|
144
|
-
(0, appendScriptData_js_1.appendScriptData)({
|
|
145
|
-
scriptDataAppender: args.scriptDataAppender,
|
|
146
|
-
nlpName: "GetLastModifiedFile",
|
|
147
|
-
stepInputs: ["variable:finalpath"],
|
|
148
|
-
returnTypeVariableName: "lastModifiedFilePath"
|
|
149
|
-
});
|
|
150
|
-
(0, appendScriptData_js_1.appendScriptData)({
|
|
151
|
-
scriptDataAppender: args.scriptDataAppender,
|
|
152
|
-
nlpName: "VerifyPDF",
|
|
153
|
-
stepInputs: ["variable:lastModifiedFilePath", args.value ?? ""],
|
|
60
|
+
sizeBytes: buffer.length,
|
|
154
61
|
});
|
|
62
|
+
if (args.isDownload) {
|
|
63
|
+
// Download flow: emit full chain so the generated script can replay the wait
|
|
64
|
+
const downloadsFolder = process.platform === "win32" ? "\\Downloads" : "/Downloads";
|
|
65
|
+
(0, appendScriptData_js_1.appendScriptData)({
|
|
66
|
+
scriptDataAppender: args.scriptDataAppender,
|
|
67
|
+
nlpName: "Sleep",
|
|
68
|
+
stepInputs: [calculatedDuration],
|
|
69
|
+
});
|
|
70
|
+
(0, appendScriptData_js_1.appendScriptData)({
|
|
71
|
+
scriptDataAppender: args.scriptDataAppender,
|
|
72
|
+
nlpName: "GetUserHomeDirectory",
|
|
73
|
+
stepInputs: [],
|
|
74
|
+
returnTypeVariableName: "userHomeDirectory",
|
|
75
|
+
});
|
|
76
|
+
(0, appendScriptData_js_1.appendScriptData)({
|
|
77
|
+
scriptDataAppender: args.scriptDataAppender,
|
|
78
|
+
nlpName: "ConcatString",
|
|
79
|
+
stepInputs: ["variable:userHomeDirectory", downloadsFolder],
|
|
80
|
+
returnTypeVariableName: "finalpath",
|
|
81
|
+
});
|
|
82
|
+
(0, appendScriptData_js_1.appendScriptData)({
|
|
83
|
+
scriptDataAppender: args.scriptDataAppender,
|
|
84
|
+
nlpName: "GetLastModifiedFile",
|
|
85
|
+
stepInputs: ["variable:finalpath"],
|
|
86
|
+
returnTypeVariableName: "lastModifiedFilePath",
|
|
87
|
+
});
|
|
88
|
+
(0, appendScriptData_js_1.appendScriptData)({
|
|
89
|
+
scriptDataAppender: args.scriptDataAppender,
|
|
90
|
+
nlpName: "VerifyPDF",
|
|
91
|
+
stepInputs: ["variable:lastModifiedFilePath", args.value ?? ""],
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Explicit path flow: emit VerifyPDF with the literal path the user provided
|
|
96
|
+
(0, appendScriptData_js_1.appendScriptData)({
|
|
97
|
+
scriptDataAppender: args.scriptDataAppender,
|
|
98
|
+
nlpName: "VerifyPDF",
|
|
99
|
+
stepInputs: [args.filePath ?? "", args.value ?? ""],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
155
102
|
}
|
|
156
103
|
catch (error) {
|
|
157
104
|
args.scriptDataAppender.setErrorNLP("VerifyPDF");
|
|
105
|
+
await logData_js_1.logger.error("VerifyPDF action failed:", error);
|
|
158
106
|
throw new Error("VerifyPDF action failed", { cause: error });
|
|
159
107
|
}
|
|
160
108
|
finally {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
109
|
+
// Clean up temp download directory if download flow was used
|
|
110
|
+
if (args.isDownload) {
|
|
111
|
+
try {
|
|
112
|
+
if (fs_1.default.existsSync(logData_js_1.downloadDir)) {
|
|
113
|
+
fs_1.default.rmSync(logData_js_1.downloadDir, { recursive: true, force: true });
|
|
114
|
+
await logData_js_1.logger.info(`Cleaned up temp download directory: ${logData_js_1.downloadDir}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (cleanupErr) {
|
|
118
|
+
await logData_js_1.logger.error("Error cleaning up temp download directory after VerifyPDF:", cleanupErr);
|
|
165
119
|
}
|
|
166
|
-
}
|
|
167
|
-
catch (cleanupErr) {
|
|
168
|
-
logData_js_1.logger.error("Error cleaning up temp_file directory after VerifyPDF:", cleanupErr);
|
|
169
120
|
}
|
|
170
121
|
}
|
|
171
122
|
}
|
|
@@ -29,6 +29,14 @@ export declare const WebVerifySchema: z.ZodObject<{
|
|
|
29
29
|
inputText: z.ZodString;
|
|
30
30
|
elementType: z.ZodString;
|
|
31
31
|
}, z.core.$strip>;
|
|
32
|
+
export declare const WebFileVerifySchema: z.ZodObject<{
|
|
33
|
+
attributeValue: z.ZodString;
|
|
34
|
+
nlpName: z.ZodString;
|
|
35
|
+
inputText: z.ZodString;
|
|
36
|
+
elementType: z.ZodString;
|
|
37
|
+
isDownload: z.ZodBoolean;
|
|
38
|
+
path: z.ZodNullable<z.ZodString>;
|
|
39
|
+
}, z.core.$strip>;
|
|
32
40
|
export declare const WebGetSchema: z.ZodObject<{
|
|
33
41
|
attributeValue: z.ZodString;
|
|
34
42
|
nlpName: z.ZodString;
|
|
@@ -43,6 +51,15 @@ export declare const MobileVerifySchema: z.ZodObject<{
|
|
|
43
51
|
elementType: z.ZodString;
|
|
44
52
|
locator_type: z.ZodString;
|
|
45
53
|
}, z.core.$strip>;
|
|
54
|
+
export declare const MobileFileVerifySchema: z.ZodObject<{
|
|
55
|
+
attributeValue: z.ZodString;
|
|
56
|
+
nlpName: z.ZodString;
|
|
57
|
+
inputText: z.ZodString;
|
|
58
|
+
elementType: z.ZodString;
|
|
59
|
+
locator_type: z.ZodString;
|
|
60
|
+
isDownload: z.ZodBoolean;
|
|
61
|
+
path: z.ZodNullable<z.ZodString>;
|
|
62
|
+
}, z.core.$strip>;
|
|
46
63
|
export declare const MobileGetSchema: z.ZodObject<{
|
|
47
64
|
attributeValue: z.ZodString;
|
|
48
65
|
nlpName: z.ZodString;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InputContextSchema = exports.OtpExtractorSchema = exports.ErrorDescriptionSchema = exports.UserStorySchema = exports.VisionSchema = exports.DefaultInteractionSchema = exports.MouseActionSchema = exports.UploadActionSchema = exports.GetOtpActionSchema = exports.ClearTextActionSchema = exports.EnterActionSchema = exports.TapActionSchema = exports.ClickActionSchema = exports.SwitchActionSchema = exports.MobileScrollSchema = exports.WebScrollSchema = exports.DragDropActionSchema = exports.MobileCombinedSchema = exports.CombinedActionSchema = exports.WaitActionSchema = exports.MobileGetSchema = exports.MobileVerifySchema = exports.WebGetSchema = exports.WebVerifySchema = exports.AlertShema = exports.KeywordSchema = exports.StepItemSchema = void 0;
|
|
3
|
+
exports.InputContextSchema = exports.OtpExtractorSchema = exports.ErrorDescriptionSchema = exports.UserStorySchema = exports.VisionSchema = exports.DefaultInteractionSchema = exports.MouseActionSchema = exports.UploadActionSchema = exports.GetOtpActionSchema = exports.ClearTextActionSchema = exports.EnterActionSchema = exports.TapActionSchema = exports.ClickActionSchema = exports.SwitchActionSchema = exports.MobileScrollSchema = exports.WebScrollSchema = exports.DragDropActionSchema = exports.MobileCombinedSchema = exports.CombinedActionSchema = exports.WaitActionSchema = exports.MobileGetSchema = exports.MobileFileVerifySchema = exports.MobileVerifySchema = exports.WebGetSchema = exports.WebFileVerifySchema = exports.WebVerifySchema = exports.AlertShema = exports.KeywordSchema = exports.StepItemSchema = void 0;
|
|
4
4
|
exports.getSchemaForPrompt = getSchemaForPrompt;
|
|
5
5
|
const zod_1 = require("zod");
|
|
6
6
|
const promptType_js_1 = require("../../../core/types/promptType.js");
|
|
@@ -26,6 +26,14 @@ exports.WebVerifySchema = zod_1.z.object({
|
|
|
26
26
|
inputText: zod_1.z.string(),
|
|
27
27
|
elementType: zod_1.z.string(),
|
|
28
28
|
});
|
|
29
|
+
exports.WebFileVerifySchema = zod_1.z.object({
|
|
30
|
+
attributeValue: zod_1.z.string().describe("FF-inspecter attribute value, e.g. 'Fire-Flink-0'"),
|
|
31
|
+
nlpName: zod_1.z.string().describe("Exact NLP name from the approved list, e.g. 'VerifyPDF' or 'VerifyPDFExist'"),
|
|
32
|
+
inputText: zod_1.z.string().describe("The expected text to verify inside the file. Must NEVER include the file path or file name. Only the search/verification phrase."),
|
|
33
|
+
elementType: zod_1.z.string().describe("Type of element from the allowed element list, default 'link'"),
|
|
34
|
+
isDownload: zod_1.z.boolean().describe("true if verifying a file downloaded via the browser; false if verifying an existing file at a local/server file path."),
|
|
35
|
+
path: zod_1.z.string().nullable().describe("The extracted file path string if isDownload is false with extra 4 slashes //// (e.g. 'C:////Users/////User/////Downloads/////file.pdf'). Must be null if isDownload is true. Never include search/input text here."),
|
|
36
|
+
});
|
|
29
37
|
exports.WebGetSchema = zod_1.z.object({
|
|
30
38
|
attributeValue: zod_1.z.string(),
|
|
31
39
|
nlpName: zod_1.z.string(),
|
|
@@ -40,6 +48,15 @@ exports.MobileVerifySchema = zod_1.z.object({
|
|
|
40
48
|
elementType: zod_1.z.string(),
|
|
41
49
|
locator_type: zod_1.z.string(),
|
|
42
50
|
});
|
|
51
|
+
exports.MobileFileVerifySchema = zod_1.z.object({
|
|
52
|
+
attributeValue: zod_1.z.string().describe("FF-inspecter attribute value, e.g. 'Fire-Flink-0'"),
|
|
53
|
+
nlpName: zod_1.z.string().describe("Exact NLP name from the approved list, e.g. 'VerifyPDF' or 'VerifyPDFExist'"),
|
|
54
|
+
inputText: zod_1.z.string().describe("The expected text to verify inside the file. Must NEVER include the file path or file name. Only the search/verification phrase."),
|
|
55
|
+
elementType: zod_1.z.string().describe("Type of element from the allowed element list, default 'link'"),
|
|
56
|
+
locator_type: zod_1.z.string().describe("Locator type"),
|
|
57
|
+
isDownload: zod_1.z.boolean().describe("true if verifying a file downloaded via the browser; false if verifying an existing file at a local/server file path."),
|
|
58
|
+
path: zod_1.z.string().nullable().describe("The extracted file path string if isDownload is false with extra 4 slashes //// (e.g. 'C:////Users/////User/////Downloads/////file.pdf'). Must be null if isDownload is true. Never include search/input text here."),
|
|
59
|
+
});
|
|
43
60
|
exports.MobileGetSchema = zod_1.z.object({
|
|
44
61
|
attributeValue: zod_1.z.string(),
|
|
45
62
|
nlpName: zod_1.z.string(),
|
|
@@ -190,9 +207,12 @@ function getSchemaForPrompt(type, platform, args) {
|
|
|
190
207
|
if (type === promptType_js_1.PromptType.COMBINED_ACTION_EXTRACTOR_PROMPT) {
|
|
191
208
|
return platform === "web" ? exports.CombinedActionSchema : exports.MobileCombinedSchema;
|
|
192
209
|
}
|
|
193
|
-
if (type === promptType_js_1.PromptType.VERIFY_PROMPT
|
|
210
|
+
if (type === promptType_js_1.PromptType.VERIFY_PROMPT) {
|
|
194
211
|
return platform === "web" ? exports.WebVerifySchema : exports.MobileVerifySchema;
|
|
195
212
|
}
|
|
213
|
+
if (type === promptType_js_1.PromptType.FILE_VERIFY_PROMPT) {
|
|
214
|
+
return platform === "web" ? exports.WebFileVerifySchema : exports.MobileFileVerifySchema;
|
|
215
|
+
}
|
|
196
216
|
if (type === promptType_js_1.PromptType.GET_PROMPT) {
|
|
197
217
|
return platform === "web" ? exports.WebGetSchema : exports.MobileGetSchema;
|
|
198
218
|
}
|
|
@@ -920,6 +920,8 @@ export interface verifyElementIsSelectedInSpecifiedTimeInterface {
|
|
|
920
920
|
export interface VerifyPDFInterface {
|
|
921
921
|
driver: WebdriverIO.Browser;
|
|
922
922
|
value: string;
|
|
923
|
+
isDownload: boolean;
|
|
924
|
+
filePath: string | null;
|
|
923
925
|
platform: string;
|
|
924
926
|
scriptDataAppender: ScriptDataAppender;
|
|
925
927
|
}
|
|
@@ -41,6 +41,7 @@ const executor_js_1 = require("../../automation/actions/executor.js");
|
|
|
41
41
|
const logData_js_1 = require("../../utils/logger/logData.js");
|
|
42
42
|
const capability_js_1 = require("../../utils/browserCap/capability.js");
|
|
43
43
|
const path_1 = __importDefault(require("path"));
|
|
44
|
+
const systemCapabilities_js_1 = require("../../utils/browserCap/systemCapabilities.js");
|
|
44
45
|
function createActionHandlers(context) {
|
|
45
46
|
let isAndroidInitialized = false;
|
|
46
47
|
const requireExecutor = () => {
|
|
@@ -99,6 +100,14 @@ function createActionHandlers(context) {
|
|
|
99
100
|
resolvedBrowserName = "safari";
|
|
100
101
|
}
|
|
101
102
|
const capsObj = (0, capability_js_1.getCapabilities)(resolvedBrowserName, pathValue, profileValue, capsKeywords);
|
|
103
|
+
for (const value of Object.values(capsObj)) {
|
|
104
|
+
if (value && typeof value === "object" && "prefs" in value) {
|
|
105
|
+
const prefs = value.prefs;
|
|
106
|
+
for (const key of systemCapabilities_js_1.systemCapability[resolvedBrowserName]) {
|
|
107
|
+
delete prefs[key];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
102
111
|
let exactCommands = [];
|
|
103
112
|
let exactPrefs = {};
|
|
104
113
|
if (resolvedBrowserName === "chrome") {
|
|
@@ -1469,7 +1478,9 @@ function createActionHandlers(context) {
|
|
|
1469
1478
|
}),
|
|
1470
1479
|
VerifyPDF: withExecutor(async (executor, result) => {
|
|
1471
1480
|
const expectedText = result?.value ?? "";
|
|
1472
|
-
|
|
1481
|
+
const isDownload = result?.isDownload ?? true;
|
|
1482
|
+
const filePath = result?.path ?? null;
|
|
1483
|
+
await executor.verifyPDF(expectedText, isDownload, filePath);
|
|
1473
1484
|
})
|
|
1474
1485
|
};
|
|
1475
1486
|
}
|
|
@@ -3,9 +3,12 @@ import { IAutomationRunner } from "../interfaces/automationRunnerInterface.js";
|
|
|
3
3
|
import { AutomationRequest } from "../../core/interfaces/executionDetails.js";
|
|
4
4
|
export declare class AutomationRunner implements IAutomationRunner {
|
|
5
5
|
static sessionTerminationDetails: Record<string, boolean>;
|
|
6
|
+
static sessionNames: Record<string, string>;
|
|
6
7
|
private KafkaPayload;
|
|
7
8
|
static getSessionTerminationInfo(testCaseId: string): boolean;
|
|
8
9
|
static updateSessionTerminationInfo(testCaseId: string): void;
|
|
10
|
+
static sessionName(testCaseId: string, sessionName: string): void;
|
|
11
|
+
static getSessionName(testCaseId: string): string;
|
|
9
12
|
static normalizeScriptType(scriptType?: string): string;
|
|
10
13
|
private cleanup;
|
|
11
14
|
private saveSessionStepData;
|
|
@@ -43,6 +43,12 @@ class AutomationRunner {
|
|
|
43
43
|
static updateSessionTerminationInfo(testCaseId) {
|
|
44
44
|
AutomationRunner.sessionTerminationDetails[testCaseId] = true;
|
|
45
45
|
}
|
|
46
|
+
static sessionName(testCaseId, sessionName) {
|
|
47
|
+
AutomationRunner.sessionNames[testCaseId] = sessionName;
|
|
48
|
+
}
|
|
49
|
+
static getSessionName(testCaseId) {
|
|
50
|
+
return AutomationRunner.sessionNames[testCaseId];
|
|
51
|
+
}
|
|
46
52
|
static normalizeScriptType(scriptType) {
|
|
47
53
|
const rawValue = (scriptType ?? "Web").trim();
|
|
48
54
|
if (!rawValue) {
|
|
@@ -112,14 +118,14 @@ class AutomationRunner {
|
|
|
112
118
|
fireflinkServiceInstance = null;
|
|
113
119
|
delete AutomationRunner.sessionTerminationDetails[request.testCaseId];
|
|
114
120
|
try {
|
|
115
|
-
const tempFileDir = path_1.default.resolve(process.cwd(), "session", "
|
|
121
|
+
const tempFileDir = path_1.default.resolve(process.cwd(), "session", "tempFile");
|
|
116
122
|
if (fs_1.default.existsSync(tempFileDir)) {
|
|
117
123
|
fs_1.default.rmSync(tempFileDir, { recursive: true, force: true });
|
|
118
|
-
logData_js_1.logger.info(`Cleaned up
|
|
124
|
+
logData_js_1.logger.info(`Cleaned up tempFile directory: ${tempFileDir}`);
|
|
119
125
|
}
|
|
120
126
|
}
|
|
121
127
|
catch (err) {
|
|
122
|
-
logData_js_1.logger.error("Error cleaning up
|
|
128
|
+
logData_js_1.logger.error("Error cleaning up tempFile directory:", err);
|
|
123
129
|
}
|
|
124
130
|
if (global.gc) {
|
|
125
131
|
global.gc();
|
|
@@ -146,12 +152,15 @@ class AutomationRunner {
|
|
|
146
152
|
(0, logData_js_1.initLogger)(request.sessionPath);
|
|
147
153
|
request.type = request.platform;
|
|
148
154
|
request.platform = AutomationRunner.normalizeScriptType(request.platform);
|
|
149
|
-
const isWebMobile = request.type?.toLowerCase().includes("web") &&
|
|
150
|
-
request.type?.toLowerCase().includes("android")
|
|
155
|
+
const isWebMobile = (request.type?.toLowerCase().includes("web") &&
|
|
156
|
+
request.type?.toLowerCase().includes("android")) ||
|
|
157
|
+
(request.type?.toLowerCase() === "android");
|
|
151
158
|
logData_js_1.logger.info("User Given Step", request.userStory);
|
|
152
|
-
const data = JSON.parse(request.scriptName);
|
|
153
|
-
const hasMoreSteps = data
|
|
154
|
-
request.scriptName = data
|
|
159
|
+
const data = request.scriptName ? JSON.parse(request.scriptName) : {};
|
|
160
|
+
const hasMoreSteps = data?.hasMoreSteps ?? false;
|
|
161
|
+
request.scriptName = data?.automationScriptName ?? "automationScriptName";
|
|
162
|
+
if (!request.isTerminate)
|
|
163
|
+
AutomationRunner.sessionName(request.testCaseId, request.scriptName);
|
|
155
164
|
if (request.isTerminate) {
|
|
156
165
|
return AutomationRunner.updateSessionTerminationInfo(request.testCaseId);
|
|
157
166
|
}
|
|
@@ -452,6 +461,8 @@ class AutomationRunner {
|
|
|
452
461
|
manualStep: step,
|
|
453
462
|
stepsInputData,
|
|
454
463
|
returnTypeVariableName: currentReturnTypeVariableName,
|
|
464
|
+
isDownload: stepResult.response.isDownload,
|
|
465
|
+
path: stepResult.response.path,
|
|
455
466
|
...(currentVariableName ? { variableName: currentVariableName } : {}),
|
|
456
467
|
});
|
|
457
468
|
if (currentDriver && currentPlatform === "web") {
|
|
@@ -628,7 +639,7 @@ class AutomationRunner {
|
|
|
628
639
|
}
|
|
629
640
|
logData_js_1.logger.info("execution time->", new Date().getTime() - startTime);
|
|
630
641
|
const payload = {
|
|
631
|
-
scriptName: request.
|
|
642
|
+
scriptName: AutomationRunner.getSessionName(request.testCaseId) ?? "SCRIPT_NAME",
|
|
632
643
|
type: request.type,
|
|
633
644
|
scriptType: request.type,
|
|
634
645
|
projectId: request.projectId,
|
|
@@ -704,3 +715,4 @@ class AutomationRunner {
|
|
|
704
715
|
}
|
|
705
716
|
exports.AutomationRunner = AutomationRunner;
|
|
706
717
|
AutomationRunner.sessionTerminationDetails = {};
|
|
718
|
+
AutomationRunner.sessionNames = {};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.getCapabilities = getCapabilities;
|
|
7
|
-
const
|
|
4
|
+
const logData_js_1 = require("../logger/logData.js");
|
|
8
5
|
function getCapabilities(browserName, path, profile, customCaps = []) {
|
|
9
6
|
const isLinux = process.platform === 'linux';
|
|
10
7
|
const caps = customCaps.map(c => c.toLowerCase());
|
|
@@ -43,9 +40,7 @@ function getCapabilities(browserName, path, profile, customCaps = []) {
|
|
|
43
40
|
};
|
|
44
41
|
const getChromiumPrefs = () => {
|
|
45
42
|
const prefs = {};
|
|
46
|
-
|
|
47
|
-
const downloadDir = path_1.default.resolve(process.cwd(), "session", "temp_file");
|
|
48
|
-
prefs["download.default_directory"] = downloadDir;
|
|
43
|
+
prefs["download.default_directory"] = logData_js_1.downloadDir;
|
|
49
44
|
prefs["download.prompt_for_download"] = false;
|
|
50
45
|
prefs["download.directory_upgrade"] = true;
|
|
51
46
|
prefs["safebrowsing.enabled"] = true;
|
|
@@ -77,9 +72,8 @@ function getCapabilities(browserName, path, profile, customCaps = []) {
|
|
|
77
72
|
if (hasCap("disableExtensions"))
|
|
78
73
|
args.push('-safe-mode');
|
|
79
74
|
const prefs = {};
|
|
80
|
-
// Always redirect downloads to session/
|
|
81
|
-
|
|
82
|
-
prefs["browser.download.dir"] = downloadDir;
|
|
75
|
+
// Always redirect downloads to session/tempFile so VerifyPDF can reliably find them
|
|
76
|
+
prefs["browser.download.dir"] = logData_js_1.downloadDir;
|
|
83
77
|
prefs["browser.download.folderList"] = 2; // 2 = use custom download dir
|
|
84
78
|
prefs["browser.download.useDownloadDir"] = true;
|
|
85
79
|
prefs["browser.helperApps.neverAsk.saveToDisk"] = "application/pdf,application/octet-stream";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.systemCapability = void 0;
|
|
4
|
+
exports.systemCapability = {
|
|
5
|
+
chrome: [
|
|
6
|
+
"download.default_directory",
|
|
7
|
+
"download.prompt_for_download",
|
|
8
|
+
"download.directory_upgrade",
|
|
9
|
+
"safebrowsing.enabled"
|
|
10
|
+
],
|
|
11
|
+
edge: [
|
|
12
|
+
"download.default_directory",
|
|
13
|
+
"download.prompt_for_download",
|
|
14
|
+
"download.directory_upgrade",
|
|
15
|
+
"safebrowsing.enabled"
|
|
16
|
+
],
|
|
17
|
+
firefox: [
|
|
18
|
+
"browser.download.dir",
|
|
19
|
+
"browser.download.folderList",
|
|
20
|
+
"browser.download.useDownloadDir",
|
|
21
|
+
"browser.helperApps.neverAsk.saveToDisk",
|
|
22
|
+
"pdfjs.disabled"
|
|
23
|
+
],
|
|
24
|
+
safari: []
|
|
25
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.waitForDownloadComplete = waitForDownloadComplete;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const DOWNLOAD_TIMEOUT_MS = 30000;
|
|
10
|
+
const POLL_INTERVAL_MS = 400;
|
|
11
|
+
const STABILITY_WAIT_MS = 250;
|
|
12
|
+
async function waitForDownloadComplete(downloadDir, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
13
|
+
const startTime = Date.now();
|
|
14
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
15
|
+
if (fs_1.default.existsSync(downloadDir)) {
|
|
16
|
+
const allEntries = fs_1.default.readdirSync(downloadDir);
|
|
17
|
+
let hasActiveDownload = false;
|
|
18
|
+
const pdfFiles = [];
|
|
19
|
+
// Single pass: early exit if active download is present, stat only PDF files
|
|
20
|
+
for (const file of allEntries) {
|
|
21
|
+
const lower = file.toLowerCase();
|
|
22
|
+
if (lower.endsWith(".crdownload") || lower.endsWith(".part") || lower.endsWith(".tmp")) {
|
|
23
|
+
hasActiveDownload = true;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
if (lower.endsWith(".pdf")) {
|
|
27
|
+
const fullPath = path_1.default.join(downloadDir, file);
|
|
28
|
+
try {
|
|
29
|
+
const stat = fs_1.default.statSync(fullPath);
|
|
30
|
+
if (stat.isFile()) {
|
|
31
|
+
pdfFiles.push({ name: file, fullPath, mtime: stat.mtimeMs, size: stat.size });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// File locked or in transition, skip for this cycle
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!hasActiveDownload && pdfFiles.length > 0) {
|
|
40
|
+
pdfFiles.sort((a, b) => b.mtime - a.mtime);
|
|
41
|
+
const latest = pdfFiles[0];
|
|
42
|
+
if (latest.size > 0) {
|
|
43
|
+
await new Promise(res => setTimeout(res, STABILITY_WAIT_MS));
|
|
44
|
+
try {
|
|
45
|
+
const newStat = fs_1.default.statSync(latest.fullPath);
|
|
46
|
+
if (newStat.size === latest.size && newStat.size > 0) {
|
|
47
|
+
const durationSeconds = Math.max(1, Math.ceil((Date.now() - startTime) / 1000));
|
|
48
|
+
return { file: latest, durationSeconds };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Retry on next cycle
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
await new Promise(res => setTimeout(res, POLL_INTERVAL_MS));
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`PDF download did not complete within ${timeoutMs / 1000} seconds in directory: ${downloadDir}. ` +
|
|
60
|
+
`Ensure the download was triggered prior to VerifyPDF.`);
|
|
61
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
export declare let downloadDir: string;
|
|
1
2
|
export declare function initLogger(basePath: string): void;
|
|
3
|
+
export declare function ensureDirs(): Promise<void>;
|
|
2
4
|
export declare const logger: {
|
|
3
5
|
info: (...args: any[]) => Promise<void>;
|
|
4
6
|
error: (...args: any[]) => Promise<void>;
|
|
@@ -3,8 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.logger = void 0;
|
|
6
|
+
exports.logger = exports.downloadDir = void 0;
|
|
7
7
|
exports.initLogger = initLogger;
|
|
8
|
+
exports.ensureDirs = ensureDirs;
|
|
8
9
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
10
|
const path_1 = __importDefault(require("path"));
|
|
10
11
|
const js_beautify_1 = __importDefault(require("js-beautify"));
|
|
@@ -17,6 +18,7 @@ function initLogger(basePath) {
|
|
|
17
18
|
sessionPath = basePath;
|
|
18
19
|
logsDir = path_1.default.resolve(sessionPath, "logs");
|
|
19
20
|
domDir = path_1.default.resolve(sessionPath, "dom");
|
|
21
|
+
exports.downloadDir = path_1.default.resolve(sessionPath, "tempFile");
|
|
20
22
|
screenshotsDir = path_1.default.resolve(sessionPath, "screenshots");
|
|
21
23
|
logFilePath = path_1.default.join(logsDir, "ai-execution-logs.txt");
|
|
22
24
|
}
|
|
@@ -26,6 +28,7 @@ async function ensureDirs() {
|
|
|
26
28
|
await promises_1.default.mkdir(logsDir, { recursive: true });
|
|
27
29
|
await promises_1.default.mkdir(domDir, { recursive: true });
|
|
28
30
|
await promises_1.default.mkdir(screenshotsDir, { recursive: true });
|
|
31
|
+
await promises_1.default.mkdir(exports.downloadDir, { recursive: true });
|
|
29
32
|
}
|
|
30
33
|
function getISTDate() {
|
|
31
34
|
return new Intl.DateTimeFormat("sv-SE", {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeText = normalizeText;
|
|
4
|
+
exports.extractTextFromPdf = extractTextFromPdf;
|
|
5
|
+
const logData_js_1 = require("../logger/logData.js");
|
|
6
|
+
const url_1 = require("url");
|
|
7
|
+
function normalizeText(text) {
|
|
8
|
+
return text.replace(/\s+/g, " ").trim();
|
|
9
|
+
}
|
|
10
|
+
async function extractTextFromPdf(buffer) {
|
|
11
|
+
try {
|
|
12
|
+
const dynamicImport = new Function("modulePath", "return import(modulePath)");
|
|
13
|
+
const pdfjs = await dynamicImport("pdfjs-dist/legacy/build/pdf.mjs").catch(() => dynamicImport("pdfjs-dist"));
|
|
14
|
+
// Required for pdfjs-dist v4+.
|
|
15
|
+
// Assign directly without reading workerSrc first,
|
|
16
|
+
const workerPath = require.resolve("pdfjs-dist/legacy/build/pdf.worker.mjs");
|
|
17
|
+
pdfjs.GlobalWorkerOptions.workerSrc = (0, url_1.pathToFileURL)(workerPath).href;
|
|
18
|
+
const data = new Uint8Array(buffer);
|
|
19
|
+
const loadingTask = pdfjs.getDocument({
|
|
20
|
+
data,
|
|
21
|
+
useSystemFonts: true,
|
|
22
|
+
disableFontFace: true,
|
|
23
|
+
});
|
|
24
|
+
const pdfDoc = await loadingTask.promise;
|
|
25
|
+
const pageTexts = [];
|
|
26
|
+
for (let i = 1; i <= pdfDoc.numPages; i++) {
|
|
27
|
+
const page = await pdfDoc.getPage(i);
|
|
28
|
+
const textContent = await page.getTextContent();
|
|
29
|
+
const pageString = textContent.items
|
|
30
|
+
.map((item) => ("str" in item ? item.str : ""))
|
|
31
|
+
.join(" ");
|
|
32
|
+
pageTexts.push(pageString);
|
|
33
|
+
}
|
|
34
|
+
return pageTexts.join("\n");
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
logData_js_1.logger.error("pdfjs-dist extraction failed, falling back to raw buffer string scan", err);
|
|
38
|
+
return buffer.toString("latin1");
|
|
39
|
+
}
|
|
40
|
+
}
|