@intuned/browser-dev 2.2.3-test-build.0
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/.babelrc +21 -0
- package/.eslintignore +10 -0
- package/.eslintrc.js +39 -0
- package/LICENSE +43 -0
- package/dist/ai/export.d.js +5 -0
- package/dist/ai/export.d.ts +641 -0
- package/dist/ai/extractStructuredData.js +320 -0
- package/dist/ai/extractStructuredDataUsingAi.js +139 -0
- package/dist/ai/extractionHelpers/screenshotHelpers.js +56 -0
- package/dist/ai/extractionHelpers/validateSchema.js +148 -0
- package/dist/ai/index.d.ts +641 -0
- package/dist/ai/index.js +19 -0
- package/dist/ai/isPageLoaded.js +77 -0
- package/dist/ai/prompt.js +39 -0
- package/dist/ai/tests/testCheckAllTypesAreStrings.spec.js +137 -0
- package/dist/ai/tests/testExtractFromContent.spec.js +372 -0
- package/dist/ai/tests/testExtractStructuredData.spec.js +646 -0
- package/dist/ai/tests/testIsPageLoaded.spec.js +277 -0
- package/dist/ai/tools/index.js +48 -0
- package/dist/ai/types/errors.js +67 -0
- package/dist/ai/types/models.js +45 -0
- package/dist/ai/types/types.js +48 -0
- package/dist/ai/validators.js +167 -0
- package/dist/common/Logger/index.js +60 -0
- package/dist/common/Logger/types.js +5 -0
- package/dist/common/SdkError.js +50 -0
- package/dist/common/aiModelsValidations.js +32 -0
- package/dist/common/browser_scripts.js +2596 -0
- package/dist/common/ensureBrowserScripts.js +18 -0
- package/dist/common/extendedTest.js +148 -0
- package/dist/common/extractionHelpers.js +19 -0
- package/dist/common/formatZodError.js +18 -0
- package/dist/common/fuzzySearch/fuzzySearch.test.js +250 -0
- package/dist/common/fuzzySearch/levenshtein-search.js +298 -0
- package/dist/common/fuzzySearch/utils.js +23 -0
- package/dist/common/getModelProvider.js +18 -0
- package/dist/common/getSimplifiedHtml.js +122 -0
- package/dist/common/hashObject.js +32 -0
- package/dist/common/html2markdown/convertElementToMarkdown.js +469 -0
- package/dist/common/html2markdown/index.js +19 -0
- package/dist/common/jwtTokenManager.js +18 -0
- package/dist/common/loadRuntime.js +16 -0
- package/dist/common/locatorHelpers.js +41 -0
- package/dist/common/matching/collectStrings.js +32 -0
- package/dist/common/matching/levenshtein.js +40 -0
- package/dist/common/matching/matching.js +317 -0
- package/dist/common/matching/types.js +1 -0
- package/dist/common/noEmpty.js +9 -0
- package/dist/common/saveSnapshotWithExamples.js +60 -0
- package/dist/common/tests/testEnsureBrowserScript.spec.js +31 -0
- package/dist/common/xpathMapping.js +107 -0
- package/dist/helpers/clickUntilExhausted.js +85 -0
- package/dist/helpers/downloadFile.js +125 -0
- package/dist/helpers/export.d.js +5 -0
- package/dist/helpers/export.d.ts +1220 -0
- package/dist/helpers/extractMarkdown.js +35 -0
- package/dist/helpers/filterEmptyValues.js +54 -0
- package/dist/helpers/gotoUrl.js +98 -0
- package/dist/helpers/index.d.ts +1220 -0
- package/dist/helpers/index.js +128 -0
- package/dist/helpers/processDate.js +25 -0
- package/dist/helpers/resolveUrl.js +64 -0
- package/dist/helpers/sanitizeHtml.js +74 -0
- package/dist/helpers/saveFileToS3.js +50 -0
- package/dist/helpers/scrollToLoadContent.js +57 -0
- package/dist/helpers/tests/extendedTest.js +130 -0
- package/dist/helpers/tests/testClickUntilExhausted.spec.js +387 -0
- package/dist/helpers/tests/testDownloadFile.spec.js +204 -0
- package/dist/helpers/tests/testExtractMarkdown.spec.js +290 -0
- package/dist/helpers/tests/testFilterEmptyValues.spec.js +151 -0
- package/dist/helpers/tests/testGoToUrl.spec.js +37 -0
- package/dist/helpers/tests/testProcessDate.spec.js +13 -0
- package/dist/helpers/tests/testResolveUrl.spec.js +341 -0
- package/dist/helpers/tests/testSanitizeHtml.spec.js +330 -0
- package/dist/helpers/tests/testScrollToLoadContent.spec.js +163 -0
- package/dist/helpers/tests/testValidateDataUsingSchema.spec.js +342 -0
- package/dist/helpers/tests/testWithDomSettledWait.spec.js +164 -0
- package/dist/helpers/tests/testWithNetworkIdleWait.spec.js +114 -0
- package/dist/helpers/types/Attachment.js +115 -0
- package/dist/helpers/types/CustomTypeRegistry.js +48 -0
- package/dist/helpers/types/RunEnvironment.js +18 -0
- package/dist/helpers/types/ValidationError.js +17 -0
- package/dist/helpers/types/index.js +51 -0
- package/dist/helpers/uploadFileToS3.js +154 -0
- package/dist/helpers/utils/getS3Client.js +22 -0
- package/dist/helpers/utils/index.js +73 -0
- package/dist/helpers/utils/isDownload.js +10 -0
- package/dist/helpers/utils/isGenerateCodeMode.js +9 -0
- package/dist/helpers/utils/isLocator.js +9 -0
- package/dist/helpers/utils/jwtTokenManager.js +18 -0
- package/dist/helpers/validateDataUsingSchema.js +103 -0
- package/dist/helpers/waitForDomSettled.js +90 -0
- package/dist/helpers/withNetworkSettledWait.js +91 -0
- package/dist/index.d.js +16 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +16 -0
- package/dist/intunedServices/ApiGateway/aiApiGateway.js +99 -0
- package/dist/intunedServices/ApiGateway/factory.js +13 -0
- package/dist/intunedServices/ApiGateway/providers/Anthropic.js +26 -0
- package/dist/intunedServices/ApiGateway/providers/Gemini.js +29 -0
- package/dist/intunedServices/ApiGateway/providers/OpenAI.js +29 -0
- package/dist/intunedServices/ApiGateway/tests/testApiGateway.spec.js +224 -0
- package/dist/intunedServices/ApiGateway/types.js +11 -0
- package/dist/intunedServices/cache/cache.js +61 -0
- package/dist/intunedServices/cache/index.js +12 -0
- package/dist/intunedServices/cache/tests/testCache.spec.js +117 -0
- package/dist/optimized-extractors/common/buildExamplesPrompt.js +12 -0
- package/dist/optimized-extractors/common/buildImagesFromPage.js +55 -0
- package/dist/optimized-extractors/common/extractStructuredDataUsingClaude.js +135 -0
- package/dist/optimized-extractors/common/extractStructuredDataUsingGoogle.js +37 -0
- package/dist/optimized-extractors/common/extractStructuredDataUsingOpenAi.js +132 -0
- package/dist/optimized-extractors/common/extractStrucutredDataUsingAiInstance.js +122 -0
- package/dist/optimized-extractors/common/findTableHeaders.js +162 -0
- package/dist/optimized-extractors/common/index.js +55 -0
- package/dist/optimized-extractors/common/isTableHeaderOrFooter.js +84 -0
- package/dist/optimized-extractors/common/matching/matching.js +212 -0
- package/dist/optimized-extractors/common/matching/matching.test.js +655 -0
- package/dist/optimized-extractors/common/matching/types.js +18 -0
- package/dist/optimized-extractors/common/matching/utils.js +184 -0
- package/dist/optimized-extractors/common/utils.js +58 -0
- package/dist/optimized-extractors/export.d.js +5 -0
- package/dist/optimized-extractors/export.d.ts +397 -0
- package/dist/optimized-extractors/extractArray.js +120 -0
- package/dist/optimized-extractors/extractObject.js +104 -0
- package/dist/optimized-extractors/index.d.ts +397 -0
- package/dist/optimized-extractors/index.js +31 -0
- package/dist/optimized-extractors/listExtractionHelpers/__tests__/dynamicListExtractor.spec.js +312 -0
- package/dist/optimized-extractors/listExtractionHelpers/__tests__/findSetOfXpathsToCreateAnArrayExtractor.test.js +22 -0
- package/dist/optimized-extractors/listExtractionHelpers/__tests__/getContainerElement.test.js +21 -0
- package/dist/optimized-extractors/listExtractionHelpers/__tests__/partOfSameArrayXpath.test.js +42 -0
- package/dist/optimized-extractors/listExtractionHelpers/__tests__/verifyThatAllXpathsArePartOfSameArray.test.js +9 -0
- package/dist/optimized-extractors/listExtractionHelpers/dynamicListExtractor.js +152 -0
- package/dist/optimized-extractors/listExtractionHelpers/errors.js +46 -0
- package/dist/optimized-extractors/listExtractionHelpers/getListMatches.js +14 -0
- package/dist/optimized-extractors/listExtractionHelpers/runAiExtraction.js +240 -0
- package/dist/optimized-extractors/listExtractionHelpers/typesAndSchema.js +5 -0
- package/dist/optimized-extractors/listExtractionHelpers/utils/extractPropertiesUsingGPTFromArray.js +277 -0
- package/dist/optimized-extractors/listExtractionHelpers/utils/extractStructuredListUsingAi.js +44 -0
- package/dist/optimized-extractors/listExtractionHelpers/utils/getListContainerXpath.js +94 -0
- package/dist/optimized-extractors/listExtractionHelpers/utils/getRelativeContainerXpathSelector.js +20 -0
- package/dist/optimized-extractors/listExtractionHelpers/utils/getSimplifiedHtmlPerListItem.js +21 -0
- package/dist/optimized-extractors/listExtractionHelpers/utils/tablesUtils.js +48 -0
- package/dist/optimized-extractors/listExtractionHelpers/utils/validateOptions.js +52 -0
- package/dist/optimized-extractors/models/anthropicModel.js +23 -0
- package/dist/optimized-extractors/models/openaiModel.js +23 -0
- package/dist/optimized-extractors/objectExtractionHelpers/AIExtractors.js +73 -0
- package/dist/optimized-extractors/objectExtractionHelpers/__tests__/checksumUtils.test.js +103 -0
- package/dist/optimized-extractors/objectExtractionHelpers/__tests__/testObjectExtractorFromLocator.spec.js +107 -0
- package/dist/optimized-extractors/objectExtractionHelpers/__tests__/testObjectExtractorFromPage.spec.js +107 -0
- package/dist/optimized-extractors/objectExtractionHelpers/calculateObjectExampleHash.js +28 -0
- package/dist/optimized-extractors/objectExtractionHelpers/captureSnapshot.js +26 -0
- package/dist/optimized-extractors/objectExtractionHelpers/checksumUtils.js +32 -0
- package/dist/optimized-extractors/objectExtractionHelpers/constants.js +7 -0
- package/dist/optimized-extractors/objectExtractionHelpers/dynamicObjectExtractor.js +106 -0
- package/dist/optimized-extractors/objectExtractionHelpers/errors.js +42 -0
- package/dist/optimized-extractors/objectExtractionHelpers/findDomMatches.js +54 -0
- package/dist/optimized-extractors/objectExtractionHelpers/getSimplifiedHtml.js +122 -0
- package/dist/optimized-extractors/objectExtractionHelpers/typesAndSchemas.js +5 -0
- package/dist/optimized-extractors/objectExtractionHelpers/validateDynamicObjectExtractorOptions.js +52 -0
- package/dist/optimized-extractors/types/aiModelsValidation.js +45 -0
- package/dist/optimized-extractors/types/errors.js +42 -0
- package/dist/optimized-extractors/types/jsonSchema.d.js +5 -0
- package/dist/optimized-extractors/types/jsonSchema.d.ts +50 -0
- package/dist/optimized-extractors/types/types.js +5 -0
- package/dist/optimized-extractors/validators.js +152 -0
- package/dist/vite-env.d.js +1 -0
- package/dist/vite-env.d.ts +9 -0
- package/docs.md +14 -0
- package/generated-docs/ai/functions/extractStructuredData.mdx +255 -0
- package/generated-docs/ai/functions/isPageLoaded.mdx +88 -0
- package/generated-docs/ai/interfaces/ArraySchema.mdx +36 -0
- package/generated-docs/ai/interfaces/BasicSchema.mdx +14 -0
- package/generated-docs/ai/interfaces/BooleanSchema.mdx +28 -0
- package/generated-docs/ai/interfaces/ImageBufferContentItem.mdx +16 -0
- package/generated-docs/ai/interfaces/ImageUrlContentItem.mdx +16 -0
- package/generated-docs/ai/interfaces/NumberSchema.mdx +35 -0
- package/generated-docs/ai/interfaces/ObjectSchema.mdx +39 -0
- package/generated-docs/ai/interfaces/StringSchema.mdx +35 -0
- package/generated-docs/ai/interfaces/TextContentItem.mdx +14 -0
- package/generated-docs/ai/type-aliases/ContentItem.mdx +12 -0
- package/generated-docs/ai/type-aliases/JsonSchema.mdx +47 -0
- package/generated-docs/ai/type-aliases/SUPPORTED_MODELS.mdx +85 -0
- package/generated-docs/helpers/functions/downloadFile.mdx +99 -0
- package/generated-docs/helpers/functions/extractMarkdown.mdx +56 -0
- package/generated-docs/helpers/functions/filterEmptyValues.mdx +51 -0
- package/generated-docs/helpers/functions/goToUrl.mdx +124 -0
- package/generated-docs/helpers/functions/processDate.mdx +55 -0
- package/generated-docs/helpers/functions/resolveUrl.mdx +165 -0
- package/generated-docs/helpers/functions/sanitizeHtml.mdx +113 -0
- package/generated-docs/helpers/functions/saveFileToS3.mdx +127 -0
- package/generated-docs/helpers/functions/scrollToLoadContent.mdx +89 -0
- package/generated-docs/helpers/functions/uploadFileToS3.mdx +121 -0
- package/generated-docs/helpers/functions/validateDataUsingSchema.mdx +90 -0
- package/generated-docs/helpers/functions/waitForDomSettled.mdx +91 -0
- package/generated-docs/helpers/functions/withNetworkSettledWait.mdx +76 -0
- package/generated-docs/helpers/interfaces/Attachment.mdx +56 -0
- package/generated-docs/helpers/interfaces/S3Configs.mdx +52 -0
- package/generated-docs/helpers/interfaces/SanitizeHtmlOptions.mdx +22 -0
- package/generated-docs/helpers/type-aliases/AttachmentType.mdx +10 -0
- package/generated-docs/helpers/type-aliases/FileType.mdx +61 -0
- package/generated-docs/helpers/type-aliases/Trigger.mdx +62 -0
- package/how-to-run-tests.md +10 -0
- package/intuned-runtime-setup.md +13 -0
- package/package.json +119 -0
- package/tsconfig.eslint.json +5 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.filterAndRankMatches = filterAndRankMatches;
|
|
7
|
+
exports.isMatchExact = isMatchExact;
|
|
8
|
+
exports.matchStringsWithDomContent = matchStringsWithDomContent;
|
|
9
|
+
exports.matchStringsWithDomContentInBrowser = matchStringsWithDomContentInBrowser;
|
|
10
|
+
exports.normalizeSpacing = normalizeSpacing;
|
|
11
|
+
exports.rankMatch = rankMatch;
|
|
12
|
+
exports.removePunctuationAndSpaces = removePunctuationAndSpaces;
|
|
13
|
+
exports.replaceWithBestMatches = replaceWithBestMatches;
|
|
14
|
+
exports.selectBestMatch = selectBestMatch;
|
|
15
|
+
var _utils = require("../../helpers/utils");
|
|
16
|
+
const logger = {
|
|
17
|
+
info: message => console.info(message),
|
|
18
|
+
warning: message => console.warn(message),
|
|
19
|
+
error: message => console.error(message)
|
|
20
|
+
};
|
|
21
|
+
function normalizeSpacing({
|
|
22
|
+
text
|
|
23
|
+
}) {
|
|
24
|
+
let normalized = text.replace(/\n/g, " ").replace(/\t/g, " ");
|
|
25
|
+
normalized = normalized.split(/\s+/).join(" ");
|
|
26
|
+
return normalized.trim();
|
|
27
|
+
}
|
|
28
|
+
function isMatchExact({
|
|
29
|
+
data,
|
|
30
|
+
value
|
|
31
|
+
}) {
|
|
32
|
+
if (data === "" || value === "") {
|
|
33
|
+
return [false, ""];
|
|
34
|
+
}
|
|
35
|
+
const normalizedData = normalizeSpacing({
|
|
36
|
+
text: data
|
|
37
|
+
});
|
|
38
|
+
const normalizedValue = normalizeSpacing({
|
|
39
|
+
text: value
|
|
40
|
+
});
|
|
41
|
+
return [normalizedData.toLowerCase() === normalizedValue.toLowerCase(), normalizedValue];
|
|
42
|
+
}
|
|
43
|
+
function removePunctuationAndSpaces({
|
|
44
|
+
s
|
|
45
|
+
}) {
|
|
46
|
+
const punctuation = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
|
47
|
+
return s.split("").filter(c => !punctuation.includes(c) && c !== " ").join("");
|
|
48
|
+
}
|
|
49
|
+
function rankMatch({
|
|
50
|
+
original,
|
|
51
|
+
match
|
|
52
|
+
}) {
|
|
53
|
+
try {
|
|
54
|
+
const normalizedOriginal = normalizeSpacing({
|
|
55
|
+
text: original
|
|
56
|
+
}).toLowerCase();
|
|
57
|
+
const normalizedMatch = normalizeSpacing({
|
|
58
|
+
text: match
|
|
59
|
+
}).toLowerCase();
|
|
60
|
+
const ratio = calculateSimilarityRatio(normalizedOriginal, normalizedMatch);
|
|
61
|
+
const lenOriginal = normalizedOriginal.length;
|
|
62
|
+
if (lenOriginal > 20 && ratio > 0.85) {
|
|
63
|
+
return "HIGH";
|
|
64
|
+
}
|
|
65
|
+
const normalizedOriginalNoPunct = removePunctuationAndSpaces({
|
|
66
|
+
s: normalizedOriginal
|
|
67
|
+
});
|
|
68
|
+
const normalizedMatchNoPunct = removePunctuationAndSpaces({
|
|
69
|
+
s: normalizedMatch
|
|
70
|
+
});
|
|
71
|
+
if (normalizedOriginalNoPunct === normalizedMatchNoPunct) {
|
|
72
|
+
return "HIGH";
|
|
73
|
+
}
|
|
74
|
+
return "LOW";
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logger.warning("Error in rankMatch, falling back to simple comparison");
|
|
77
|
+
const normalizedOriginal = removePunctuationAndSpaces({
|
|
78
|
+
s: normalizeSpacing({
|
|
79
|
+
text: original
|
|
80
|
+
}).toLowerCase()
|
|
81
|
+
});
|
|
82
|
+
const normalizedMatch = removePunctuationAndSpaces({
|
|
83
|
+
s: normalizeSpacing({
|
|
84
|
+
text: match
|
|
85
|
+
}).toLowerCase()
|
|
86
|
+
});
|
|
87
|
+
if (normalizedOriginal === normalizedMatch) {
|
|
88
|
+
return "HIGH";
|
|
89
|
+
}
|
|
90
|
+
return "LOW";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function calculateSimilarityRatio(str1, str2) {
|
|
94
|
+
const longer = str1.length > str2.length ? str1 : str2;
|
|
95
|
+
const shorter = str1.length > str2.length ? str2 : str1;
|
|
96
|
+
if (longer.length === 0) {
|
|
97
|
+
return 1.0;
|
|
98
|
+
}
|
|
99
|
+
return (longer.length - editDistance(longer, shorter)) / longer.length;
|
|
100
|
+
}
|
|
101
|
+
function editDistance(str1, str2) {
|
|
102
|
+
const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
|
|
103
|
+
for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
|
|
104
|
+
for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
|
|
105
|
+
for (let j = 1; j <= str2.length; j++) {
|
|
106
|
+
for (let i = 1; i <= str1.length; i++) {
|
|
107
|
+
if (str1[i - 1] === str2[j - 1]) {
|
|
108
|
+
matrix[j][i] = matrix[j - 1][i - 1];
|
|
109
|
+
} else {
|
|
110
|
+
matrix[j][i] = Math.min(matrix[j - 1][i - 1] + 1, matrix[j][i - 1] + 1, matrix[j - 1][i] + 1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return matrix[str2.length][str1.length];
|
|
115
|
+
}
|
|
116
|
+
function selectBestMatch({
|
|
117
|
+
original,
|
|
118
|
+
matches
|
|
119
|
+
}) {
|
|
120
|
+
const exactMatches = matches.filter(match => match.match_mode !== "fuzzy");
|
|
121
|
+
if (exactMatches.length > 0) {
|
|
122
|
+
const bestMatch = exactMatches[0];
|
|
123
|
+
return {
|
|
124
|
+
matchText: bestMatch.matched_value,
|
|
125
|
+
matchXpath: bestMatch.xpath,
|
|
126
|
+
matchType: bestMatch.match_source === "direct_text_node" ? "direct-text" : bestMatch.match_source === "attribute" ? {
|
|
127
|
+
attribute: "unknown"
|
|
128
|
+
} : "all-text"
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const fuzzyMatches = matches.filter(match => match.match_mode === "fuzzy");
|
|
132
|
+
const rankedFuzzyMatches = fuzzyMatches.map(match => ({
|
|
133
|
+
match,
|
|
134
|
+
rank: rankMatch({
|
|
135
|
+
original,
|
|
136
|
+
match: match.matched_value
|
|
137
|
+
})
|
|
138
|
+
}));
|
|
139
|
+
const highRankedMatches = rankedFuzzyMatches.filter(item => item.rank === "HIGH");
|
|
140
|
+
const filteredFuzzyMatches = highRankedMatches.map(item => item.match);
|
|
141
|
+
if (filteredFuzzyMatches.length > 0) {
|
|
142
|
+
const sortedFuzzyMatches = filteredFuzzyMatches.sort((a, b) => (a.fuzzy_distance || Infinity) - (b.fuzzy_distance || Infinity));
|
|
143
|
+
const bestMatch = sortedFuzzyMatches[0];
|
|
144
|
+
return {
|
|
145
|
+
matchText: bestMatch.matched_value,
|
|
146
|
+
matchXpath: bestMatch.xpath,
|
|
147
|
+
matchType: bestMatch.match_source === "direct_text_node" ? "direct-text" : bestMatch.match_source === "attribute" ? {
|
|
148
|
+
attribute: "unknown"
|
|
149
|
+
} : "all-text"
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
async function matchStringsWithDomContent({
|
|
155
|
+
pageObject,
|
|
156
|
+
stringsList,
|
|
157
|
+
container = null
|
|
158
|
+
}) {
|
|
159
|
+
try {
|
|
160
|
+
await (0, _utils.ensureBrowserScripts)(pageObject);
|
|
161
|
+
let handle;
|
|
162
|
+
if (container) {
|
|
163
|
+
handle = container;
|
|
164
|
+
} else {
|
|
165
|
+
handle = await pageObject.locator("html").elementHandle();
|
|
166
|
+
}
|
|
167
|
+
const matches = await pageObject.evaluate(async args => {
|
|
168
|
+
const [container, searchTexts] = args;
|
|
169
|
+
try {
|
|
170
|
+
if (typeof window.__INTUNED__ !== "undefined" && typeof window.__INTUNED__.matchStringsWithDomContent === "function") {
|
|
171
|
+
return await window.__INTUNED__.matchStringsWithDomContent(container, searchTexts);
|
|
172
|
+
} else {
|
|
173
|
+
return searchTexts.reduce((acc, text) => {
|
|
174
|
+
acc[text] = [];
|
|
175
|
+
return acc;
|
|
176
|
+
}, {});
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error("Error matching strings with DOM content:", error);
|
|
180
|
+
return searchTexts.reduce((acc, text) => {
|
|
181
|
+
acc[text] = [];
|
|
182
|
+
return acc;
|
|
183
|
+
}, {});
|
|
184
|
+
}
|
|
185
|
+
}, [handle, stringsList]);
|
|
186
|
+
return matches;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
logger.warning(`Error matching strings with DOM content: ${error}`);
|
|
189
|
+
return stringsList.reduce((acc, string) => {
|
|
190
|
+
acc[string] = [];
|
|
191
|
+
return acc;
|
|
192
|
+
}, {});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function replaceWithBestMatches({
|
|
196
|
+
stringsToMatch,
|
|
197
|
+
pageObject,
|
|
198
|
+
container = null
|
|
199
|
+
}) {
|
|
200
|
+
const matchesMap = await matchStringsWithDomContent({
|
|
201
|
+
pageObject,
|
|
202
|
+
stringsList: stringsToMatch,
|
|
203
|
+
container
|
|
204
|
+
});
|
|
205
|
+
const replacements = {};
|
|
206
|
+
const xpathMapping = {};
|
|
207
|
+
for (const [string, matches] of Object.entries(matchesMap)) {
|
|
208
|
+
const bestMatch = matches.length > 0 ? selectBestMatch({
|
|
209
|
+
original: string,
|
|
210
|
+
matches
|
|
211
|
+
}) : null;
|
|
212
|
+
replacements[string] = bestMatch;
|
|
213
|
+
if (bestMatch) {
|
|
214
|
+
xpathMapping[bestMatch.matchText] = [{
|
|
215
|
+
xpath: bestMatch.matchXpath,
|
|
216
|
+
matchType: bestMatch.matchType
|
|
217
|
+
}];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
replacements,
|
|
222
|
+
xpathMapping
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
async function filterAndRankMatches(frame, matches) {
|
|
226
|
+
const filteredMatches = matches.filter(match => {
|
|
227
|
+
const xpath = match.xpath;
|
|
228
|
+
return !(xpath.includes("[name()='svg']/") || xpath.includes("/path") || xpath.includes("/style"));
|
|
229
|
+
});
|
|
230
|
+
async function getVisibility(match) {
|
|
231
|
+
try {
|
|
232
|
+
const locator = frame.locator(`xpath=${match.xpath}`);
|
|
233
|
+
return await locator.isVisible({
|
|
234
|
+
timeout: 100
|
|
235
|
+
});
|
|
236
|
+
} catch {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const visibilityResults = await Promise.all(filteredMatches.map(match => getVisibility(match)));
|
|
241
|
+
const modeOrder = {
|
|
242
|
+
full: 3,
|
|
243
|
+
partial: 2,
|
|
244
|
+
fuzzy: 1
|
|
245
|
+
};
|
|
246
|
+
const sourceOrder = {
|
|
247
|
+
direct_text_node: 3,
|
|
248
|
+
text_content: 2,
|
|
249
|
+
attribute: 1
|
|
250
|
+
};
|
|
251
|
+
function sortKey(match, visible) {
|
|
252
|
+
const modeKey = modeOrder[match.match_mode.toLowerCase()] || 0;
|
|
253
|
+
const sourceKey = sourceOrder[match.match_source.toLowerCase()] || 0;
|
|
254
|
+
const visibleKey = visible ? 1 : 0;
|
|
255
|
+
let partialScore = 0;
|
|
256
|
+
if (match.match_mode.toLowerCase() === "partial" && match.matched_source_value && match.matched_value) {
|
|
257
|
+
const extraChars = match.matched_source_value.length - match.matched_value.length;
|
|
258
|
+
if (extraChars === 0) {
|
|
259
|
+
partialScore = 1;
|
|
260
|
+
} else {
|
|
261
|
+
partialScore = 1 / extraChars;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return modeKey + sourceKey + visibleKey + partialScore;
|
|
265
|
+
}
|
|
266
|
+
const matchesWithVisibility = filteredMatches.map((match, index) => ({
|
|
267
|
+
match,
|
|
268
|
+
visible: visibilityResults[index]
|
|
269
|
+
}));
|
|
270
|
+
const sortedMatchesWithVisibility = matchesWithVisibility.sort((a, b) => sortKey(b.match, b.visible) - sortKey(a.match, a.visible));
|
|
271
|
+
const sortedMatches = sortedMatchesWithVisibility.map(item => item.match);
|
|
272
|
+
const seenXpaths = new Set();
|
|
273
|
+
const uniqueMatches = [];
|
|
274
|
+
for (const match of sortedMatches) {
|
|
275
|
+
if (seenXpaths.has(match.xpath)) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
seenXpaths.add(match.xpath);
|
|
279
|
+
uniqueMatches.push(match);
|
|
280
|
+
}
|
|
281
|
+
return uniqueMatches;
|
|
282
|
+
}
|
|
283
|
+
async function matchStringsWithDomContentInBrowser(frame, stringsList, container = null) {
|
|
284
|
+
let handle;
|
|
285
|
+
if (container) {
|
|
286
|
+
handle = await container.elementHandle();
|
|
287
|
+
} else {
|
|
288
|
+
handle = await frame.locator("html").elementHandle();
|
|
289
|
+
}
|
|
290
|
+
logger.info(`Searching for ${stringsList.length} strings in the DOM, ${stringsList}`);
|
|
291
|
+
const matches = await frame.evaluate(async args => {
|
|
292
|
+
const [container, searchTexts] = args;
|
|
293
|
+
const result = await window.__INTUNED__.matchStringsWithDomContent(container, searchTexts);
|
|
294
|
+
return result;
|
|
295
|
+
}, [handle, stringsList]);
|
|
296
|
+
let frames = [];
|
|
297
|
+
if ("childFrames" in frame) {
|
|
298
|
+
frames = frame.childFrames();
|
|
299
|
+
} else if ("frames" in frame) {
|
|
300
|
+
frames = frame.frames();
|
|
301
|
+
}
|
|
302
|
+
for (const subframe of frames) {
|
|
303
|
+
try {
|
|
304
|
+
const frameMatches = await matchStringsWithDomContentInBrowser(subframe, stringsList, null);
|
|
305
|
+
for (const [string, stringMatches] of Object.entries(frameMatches)) {
|
|
306
|
+
if (string in matches) {
|
|
307
|
+
matches[string].extend(stringMatches);
|
|
308
|
+
} else {
|
|
309
|
+
matches[string] = stringMatches;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
} catch {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return matches;
|
|
317
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.__saveSnapshotWithExamples = __saveSnapshotWithExamples;
|
|
7
|
+
exports.__saveSnapshotWithExamplesFromPage = __saveSnapshotWithExamplesFromPage;
|
|
8
|
+
var _clientS = require("@aws-sdk/client-s3");
|
|
9
|
+
var z = _interopRequireWildcard(require("zod"));
|
|
10
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
11
|
+
function shouldSkipCaptureSnapshots() {
|
|
12
|
+
return process.env.DISABLE_CAPTURE_EXAMPLE === "true";
|
|
13
|
+
}
|
|
14
|
+
async function __saveSnapshotWithExamplesFromPage(page, snapshotName, examples, s3Configs) {
|
|
15
|
+
const html = await page.content();
|
|
16
|
+
const url = page.url();
|
|
17
|
+
return __saveSnapshotWithExamples(snapshotName, examples, html, url, s3Configs);
|
|
18
|
+
}
|
|
19
|
+
async function __saveSnapshotWithExamples(snapshotName, examples, html, url, s3Configs) {
|
|
20
|
+
if (shouldSkipCaptureSnapshots()) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const origin = new URL(url).origin;
|
|
24
|
+
const defaultConfigs = {
|
|
25
|
+
accessKeyId: process.env.SNAPSHOT_S3_ACCESS_KEY_ID,
|
|
26
|
+
bucket: process.env.SNAPSHOT_S3_BUCKET,
|
|
27
|
+
folderName: process.env.SNAPSHOT_S3_FOLDER,
|
|
28
|
+
region: process.env.SNAPSHOT_S3_REGION,
|
|
29
|
+
secretAccessKey: process.env.SNAPSHOT_S3_SECRET_ACCESS_KEY
|
|
30
|
+
};
|
|
31
|
+
const schema = z.record(z.string(), z.any());
|
|
32
|
+
const parsedConfigs = schema.safeParse({
|
|
33
|
+
...defaultConfigs,
|
|
34
|
+
...s3Configs
|
|
35
|
+
});
|
|
36
|
+
if (parsedConfigs.success === false) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const configs = parsedConfigs.data;
|
|
40
|
+
const folderPath = `${configs.folderName}`;
|
|
41
|
+
const client = new _clientS.S3Client({
|
|
42
|
+
region: configs.region,
|
|
43
|
+
credentials: {
|
|
44
|
+
accessKeyId: configs.accessKeyId,
|
|
45
|
+
secretAccessKey: configs.secretAccessKey
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const snapshotJson = {
|
|
49
|
+
origin,
|
|
50
|
+
html,
|
|
51
|
+
examples
|
|
52
|
+
};
|
|
53
|
+
await client.send(new _clientS.PutObjectCommand({
|
|
54
|
+
Bucket: configs.bucket,
|
|
55
|
+
Key: `${folderPath}/${snapshotName}.json`,
|
|
56
|
+
Body: JSON.stringify(snapshotJson),
|
|
57
|
+
ContentType: "application/json"
|
|
58
|
+
}));
|
|
59
|
+
console.log("Snapshot saved successfully.", snapshotName);
|
|
60
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _extendedTest = require("../extendedTest");
|
|
4
|
+
var _ensureBrowserScripts = require("../ensureBrowserScripts");
|
|
5
|
+
var _playwright = require("playwright");
|
|
6
|
+
var _dotenv = require("dotenv");
|
|
7
|
+
(0, _dotenv.config)();
|
|
8
|
+
(0, _extendedTest.describe)("goToUrl E2E Tests", () => {
|
|
9
|
+
let browser;
|
|
10
|
+
let page;
|
|
11
|
+
(0, _extendedTest.beforeAll)(async () => {
|
|
12
|
+
browser = await _playwright.chromium.launch({
|
|
13
|
+
headless: false
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
(0, _extendedTest.afterAll)(async () => {
|
|
17
|
+
await browser.close();
|
|
18
|
+
});
|
|
19
|
+
(0, _extendedTest.beforeEach)(async () => {
|
|
20
|
+
page = await browser.newPage();
|
|
21
|
+
});
|
|
22
|
+
(0, _extendedTest.afterEach)(async () => {
|
|
23
|
+
await page.close();
|
|
24
|
+
});
|
|
25
|
+
(0, _extendedTest.describe)("ensureBrowserScripts", () => {
|
|
26
|
+
(0, _extendedTest.test)("should ensure browser scripts are loaded", async () => {
|
|
27
|
+
await (0, _ensureBrowserScripts.ensureBrowserScripts)(page);
|
|
28
|
+
(0, _extendedTest.expect)(await page.evaluate("typeof window.__INTUNED__")).toBe("object");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createXPathMapping = createXPathMapping;
|
|
7
|
+
exports.validateXPathMapping = validateXPathMapping;
|
|
8
|
+
var _ensureBrowserScripts = require("./ensureBrowserScripts");
|
|
9
|
+
async function validateXPathMapping(page, cachedMapping, prefix) {
|
|
10
|
+
try {
|
|
11
|
+
for (const [expectedText, entries] of Object.entries(cachedMapping)) {
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
const {
|
|
14
|
+
xpath,
|
|
15
|
+
matchType
|
|
16
|
+
} = entry;
|
|
17
|
+
const elementExists = await page.evaluate(({
|
|
18
|
+
xpath,
|
|
19
|
+
prefix
|
|
20
|
+
}) => {
|
|
21
|
+
const element = document.evaluate(prefix ? `${prefix}/${xpath}` : xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
|
22
|
+
return element !== null;
|
|
23
|
+
}, {
|
|
24
|
+
xpath,
|
|
25
|
+
prefix
|
|
26
|
+
});
|
|
27
|
+
if (!elementExists) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const actualText = await page.evaluate(({
|
|
31
|
+
xpath,
|
|
32
|
+
prefix,
|
|
33
|
+
matchType
|
|
34
|
+
}) => {
|
|
35
|
+
const element = document.evaluate(prefix ? `${prefix}/${xpath}` : xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
|
36
|
+
if (!element) return "";
|
|
37
|
+
if (matchType === "direct-text") {
|
|
38
|
+
let directText = "";
|
|
39
|
+
for (const child of element.childNodes) {
|
|
40
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
41
|
+
directText += child.textContent || "";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return directText.trim();
|
|
45
|
+
} else if (matchType === "all-text") {
|
|
46
|
+
var _element$textContent;
|
|
47
|
+
return ((_element$textContent = element.textContent) === null || _element$textContent === void 0 ? void 0 : _element$textContent.trim()) || "";
|
|
48
|
+
} else if (typeof matchType === "object" && matchType.attribute) {
|
|
49
|
+
return element.getAttribute(matchType.attribute) || "";
|
|
50
|
+
}
|
|
51
|
+
return "";
|
|
52
|
+
}, {
|
|
53
|
+
xpath,
|
|
54
|
+
prefix,
|
|
55
|
+
matchType
|
|
56
|
+
});
|
|
57
|
+
if (actualText !== expectedText) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function createXPathMapping(page, extractedData) {
|
|
68
|
+
const xpathMapping = {};
|
|
69
|
+
let uniqueValues = [];
|
|
70
|
+
await (0, _ensureBrowserScripts.ensureBrowserScripts)(page);
|
|
71
|
+
if (Array.isArray(extractedData)) {
|
|
72
|
+
if (extractedData.length > 0 && typeof extractedData[0] === "string") {
|
|
73
|
+
uniqueValues = [...new Set(extractedData)];
|
|
74
|
+
} else {
|
|
75
|
+
uniqueValues = [...new Set(extractedData.flatMap(obj => Object.values(obj)))];
|
|
76
|
+
}
|
|
77
|
+
} else if (extractedData && typeof extractedData === "object") {
|
|
78
|
+
uniqueValues = [...new Set(Object.values(extractedData).filter(value => value !== null))];
|
|
79
|
+
}
|
|
80
|
+
for (const value of uniqueValues) {
|
|
81
|
+
xpathMapping[value] = [];
|
|
82
|
+
const strategies = [{
|
|
83
|
+
locatorFn: () => page.getByText(value, {
|
|
84
|
+
exact: true
|
|
85
|
+
}),
|
|
86
|
+
matchType: "all-text"
|
|
87
|
+
}];
|
|
88
|
+
for (const strategy of strategies) {
|
|
89
|
+
const locators = strategy.locatorFn();
|
|
90
|
+
const count = await locators.count();
|
|
91
|
+
for (let i = 0; i < count; i++) {
|
|
92
|
+
const locator = locators.nth(i);
|
|
93
|
+
const xpath = await locator.evaluate(el => {
|
|
94
|
+
return window.__INTUNED__.getElementXPath(el);
|
|
95
|
+
});
|
|
96
|
+
const existingEntry = xpathMapping[value].find(entry => entry.xpath === xpath);
|
|
97
|
+
if (!existingEntry) {
|
|
98
|
+
xpathMapping[value].push({
|
|
99
|
+
xpath,
|
|
100
|
+
matchType: strategy.matchType
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return xpathMapping;
|
|
107
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.clickButtonUntilNoChange = exports.clickButtonAndWait = void 0;
|
|
7
|
+
var _withNetworkSettledWait = require("./withNetworkSettledWait");
|
|
8
|
+
var _Logger = require("../common/Logger");
|
|
9
|
+
const getContainerState = async container => {
|
|
10
|
+
const count = await container.count();
|
|
11
|
+
if (count > 0) {
|
|
12
|
+
return await container.locator("> *").count();
|
|
13
|
+
}
|
|
14
|
+
return await container.evaluate(element => element.scrollHeight);
|
|
15
|
+
};
|
|
16
|
+
const clickButtonAndWaitInternal = async (page, buttonLocator, clickDelay) => {
|
|
17
|
+
await buttonLocator.scrollIntoViewIfNeeded();
|
|
18
|
+
await buttonLocator.click({
|
|
19
|
+
force: true
|
|
20
|
+
});
|
|
21
|
+
await new Promise(resolve => setTimeout(resolve, clickDelay * 1000));
|
|
22
|
+
};
|
|
23
|
+
const clickButtonAndWait = async input => {
|
|
24
|
+
const {
|
|
25
|
+
page,
|
|
26
|
+
buttonLocator,
|
|
27
|
+
clickDelay = 0.5
|
|
28
|
+
} = input;
|
|
29
|
+
await (0, _withNetworkSettledWait.withNetworkSettledWait)(async () => {
|
|
30
|
+
await clickButtonAndWaitInternal(page, buttonLocator, clickDelay);
|
|
31
|
+
}, {
|
|
32
|
+
page,
|
|
33
|
+
maxInflightRequests: 0,
|
|
34
|
+
timeoutInMs: 10000
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
exports.clickButtonAndWait = clickButtonAndWait;
|
|
38
|
+
const clickButtonUntilNoChange = async input => {
|
|
39
|
+
const {
|
|
40
|
+
page,
|
|
41
|
+
buttonLocator,
|
|
42
|
+
heartbeat = undefined,
|
|
43
|
+
containerLocator = undefined,
|
|
44
|
+
maxClicks = 50,
|
|
45
|
+
clickDelay = 0.5,
|
|
46
|
+
noChangeThreshold = 0
|
|
47
|
+
} = input;
|
|
48
|
+
await (0, _withNetworkSettledWait.withNetworkSettledWait)(async () => {
|
|
49
|
+
let prevState = null;
|
|
50
|
+
if (containerLocator) {
|
|
51
|
+
prevState = await getContainerState(containerLocator);
|
|
52
|
+
_Logger.logger.info(`Initial container state: ${prevState}`);
|
|
53
|
+
}
|
|
54
|
+
const buttonCount = await buttonLocator.count();
|
|
55
|
+
_Logger.logger.info(`Button matches: ${buttonCount}`);
|
|
56
|
+
for (let i = 0; i < maxClicks; i++) {
|
|
57
|
+
heartbeat === null || heartbeat === void 0 || heartbeat();
|
|
58
|
+
const isVisible = await buttonLocator.isVisible();
|
|
59
|
+
if (!isVisible) {
|
|
60
|
+
_Logger.logger.info("Button not visible, stopping.");
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
const isEnabled = await buttonLocator.isEnabled();
|
|
64
|
+
if (!isEnabled) {
|
|
65
|
+
_Logger.logger.info("Button not enabled, stopping.");
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
await clickButtonAndWaitInternal(page, buttonLocator, clickDelay);
|
|
69
|
+
if (containerLocator) {
|
|
70
|
+
const currentState = await getContainerState(containerLocator);
|
|
71
|
+
_Logger.logger.info(`Current container state: ${currentState}`);
|
|
72
|
+
if (prevState !== null && currentState - prevState <= noChangeThreshold) {
|
|
73
|
+
_Logger.logger.info(`No significant change in container state: ${currentState} (previous: ${prevState})`);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
prevState = currentState;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}, {
|
|
80
|
+
page,
|
|
81
|
+
maxInflightRequests: 0,
|
|
82
|
+
timeoutInMs: 30000
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
exports.clickButtonUntilNoChange = clickButtonUntilNoChange;
|