@govtechsg/oobee 0.10.76 → 0.10.77
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/.github/workflows/publish.yml +8 -1
- package/INTEGRATION.md +7 -3
- package/dist/cli.js +252 -0
- package/dist/combine.js +221 -0
- package/dist/constants/cliFunctions.js +306 -0
- package/dist/constants/common.js +1669 -0
- package/dist/constants/constants.js +913 -0
- package/dist/constants/errorMeta.json +319 -0
- package/dist/constants/itemTypeDescription.js +7 -0
- package/dist/constants/oobeeAi.js +121 -0
- package/dist/constants/questions.js +151 -0
- package/dist/constants/sampleData.js +176 -0
- package/dist/crawlers/commonCrawlerFunc.js +428 -0
- package/dist/crawlers/crawlDomain.js +613 -0
- package/dist/crawlers/crawlIntelligentSitemap.js +135 -0
- package/dist/crawlers/crawlLocalFile.js +151 -0
- package/dist/crawlers/crawlSitemap.js +303 -0
- package/dist/crawlers/custom/escapeCssSelector.js +10 -0
- package/dist/crawlers/custom/evaluateAltText.js +11 -0
- package/dist/crawlers/custom/extractAndGradeText.js +44 -0
- package/dist/crawlers/custom/extractText.js +27 -0
- package/dist/crawlers/custom/findElementByCssSelector.js +36 -0
- package/dist/crawlers/custom/flagUnlabelledClickableElements.js +963 -0
- package/dist/crawlers/custom/framesCheck.js +37 -0
- package/dist/crawlers/custom/getAxeConfiguration.js +111 -0
- package/dist/crawlers/custom/gradeReadability.js +23 -0
- package/dist/crawlers/custom/utils.js +1024 -0
- package/dist/crawlers/custom/xPathToCss.js +147 -0
- package/dist/crawlers/guards/urlGuard.js +71 -0
- package/dist/crawlers/pdfScanFunc.js +276 -0
- package/dist/crawlers/runCustom.js +89 -0
- package/dist/exclusions.txt +7 -0
- package/dist/generateHtmlReport.js +144 -0
- package/dist/index.js +62 -0
- package/dist/logs.js +84 -0
- package/dist/mergeAxeResults.js +1571 -0
- package/dist/npmIndex.js +429 -0
- package/dist/proxyService.js +360 -0
- package/dist/runGenerateJustHtmlReport.js +16 -0
- package/dist/screenshotFunc/htmlScreenshotFunc.js +355 -0
- package/dist/screenshotFunc/pdfScreenshotFunc.js +645 -0
- package/dist/services/s3Uploader.js +127 -0
- package/dist/static/ejs/partials/components/allIssues/AllIssues.ejs +9 -0
- package/dist/static/ejs/partials/components/allIssues/CategoryBadges.ejs +82 -0
- package/dist/static/ejs/partials/components/allIssues/FilterBar.ejs +33 -0
- package/dist/static/ejs/partials/components/allIssues/IssuesTable.ejs +41 -0
- package/dist/static/ejs/partials/components/header/SiteInfo.ejs +119 -0
- package/dist/static/ejs/partials/components/header/aboutScanModal/AboutScanModal.ejs +15 -0
- package/dist/static/ejs/partials/components/header/aboutScanModal/ScanConfiguration.ejs +44 -0
- package/dist/static/ejs/partials/components/header/aboutScanModal/ScanDetails.ejs +142 -0
- package/dist/static/ejs/partials/components/prioritiseIssues/IssueDetailCard.ejs +36 -0
- package/dist/static/ejs/partials/components/prioritiseIssues/PrioritiseIssues.ejs +47 -0
- package/dist/static/ejs/partials/components/ruleModal/ruleOffcanvas.ejs +196 -0
- package/dist/static/ejs/partials/components/scannedPagesSegmentedTabs.ejs +48 -0
- package/dist/static/ejs/partials/components/screenshotLightbox.ejs +13 -0
- package/dist/static/ejs/partials/components/shared/InfoAlert.ejs +3 -0
- package/dist/static/ejs/partials/components/summaryScanAbout.ejs +141 -0
- package/dist/static/ejs/partials/components/summaryScanResults.ejs +16 -0
- package/dist/static/ejs/partials/components/summaryTable.ejs +20 -0
- package/dist/static/ejs/partials/components/summaryWcagCompliance.ejs +94 -0
- package/dist/static/ejs/partials/components/topTen.ejs +6 -0
- package/dist/static/ejs/partials/components/wcagCompliance/FailedCriteria.ejs +47 -0
- package/dist/static/ejs/partials/components/wcagCompliance/WcagCompliance.ejs +16 -0
- package/dist/static/ejs/partials/components/wcagCompliance/WcagGaugeBar.ejs +16 -0
- package/dist/static/ejs/partials/components/wcagCoverageDetails.ejs +18 -0
- package/dist/static/ejs/partials/footer.ejs +24 -0
- package/dist/static/ejs/partials/header.ejs +14 -0
- package/dist/static/ejs/partials/main.ejs +29 -0
- package/dist/static/ejs/partials/scripts/allIssues/AllIssues.ejs +376 -0
- package/dist/static/ejs/partials/scripts/bootstrap.ejs +8 -0
- package/dist/static/ejs/partials/scripts/categorySummary.ejs +141 -0
- package/dist/static/ejs/partials/scripts/decodeUnzipParse.ejs +3 -0
- package/dist/static/ejs/partials/scripts/header/SiteInfo.ejs +44 -0
- package/dist/static/ejs/partials/scripts/header/aboutScanModal/AboutScanModal.ejs +51 -0
- package/dist/static/ejs/partials/scripts/header/aboutScanModal/ScanConfiguration.ejs +127 -0
- package/dist/static/ejs/partials/scripts/header/aboutScanModal/ScanDetails.ejs +60 -0
- package/dist/static/ejs/partials/scripts/highlightjs.ejs +335 -0
- package/dist/static/ejs/partials/scripts/popper.ejs +7 -0
- package/dist/static/ejs/partials/scripts/prioritiseIssues/IssueDetailCard.ejs +137 -0
- package/dist/static/ejs/partials/scripts/prioritiseIssues/PrioritiseIssues.ejs +214 -0
- package/dist/static/ejs/partials/scripts/prioritiseIssues/wcagSvgMap.ejs +861 -0
- package/dist/static/ejs/partials/scripts/ruleModal/constants.ejs +957 -0
- package/dist/static/ejs/partials/scripts/ruleModal/itemCardRenderer.ejs +353 -0
- package/dist/static/ejs/partials/scripts/ruleModal/pageAccordionBuilder.ejs +468 -0
- package/dist/static/ejs/partials/scripts/ruleModal/ruleOffcanvas.ejs +306 -0
- package/dist/static/ejs/partials/scripts/ruleModal/utilities.ejs +483 -0
- package/dist/static/ejs/partials/scripts/scannedPagesSegmentedTabs.ejs +35 -0
- package/dist/static/ejs/partials/scripts/screenshotLightbox.ejs +75 -0
- package/dist/static/ejs/partials/scripts/summaryScanResults.ejs +14 -0
- package/dist/static/ejs/partials/scripts/summaryTable.ejs +78 -0
- package/dist/static/ejs/partials/scripts/topTen.ejs +61 -0
- package/dist/static/ejs/partials/scripts/utils.ejs +453 -0
- package/dist/static/ejs/partials/scripts/wcagCompliance/FailedCriteria.ejs +103 -0
- package/dist/static/ejs/partials/scripts/wcagCompliance/WcagGaugeBar.ejs +47 -0
- package/dist/static/ejs/partials/scripts/wcagCompliance.ejs +15 -0
- package/dist/static/ejs/partials/scripts/wcagCoverageDetails.ejs +75 -0
- package/dist/static/ejs/partials/styles/allIssues/AllIssues.ejs +384 -0
- package/dist/static/ejs/partials/styles/bootstrap.ejs +12391 -0
- package/dist/static/ejs/partials/styles/header/SiteInfo.ejs +121 -0
- package/dist/static/ejs/partials/styles/header/aboutScanModal/AboutScanModal.ejs +82 -0
- package/dist/static/ejs/partials/styles/header/aboutScanModal/ScanConfiguration.ejs +50 -0
- package/dist/static/ejs/partials/styles/header/aboutScanModal/ScanDetails.ejs +149 -0
- package/dist/static/ejs/partials/styles/header.ejs +7 -0
- package/dist/static/ejs/partials/styles/highlightjs.ejs +54 -0
- package/dist/static/ejs/partials/styles/prioritiseIssues/IssueDetailCard.ejs +141 -0
- package/dist/static/ejs/partials/styles/prioritiseIssues/PrioritiseIssues.ejs +204 -0
- package/dist/static/ejs/partials/styles/ruleModal/ruleOffcanvas.ejs +456 -0
- package/dist/static/ejs/partials/styles/scannedPagesSegmentedTabs.ejs +46 -0
- package/dist/static/ejs/partials/styles/shared/InfoAlert.ejs +12 -0
- package/dist/static/ejs/partials/styles/styles.ejs +1607 -0
- package/dist/static/ejs/partials/styles/summaryBootstrap.ejs +12458 -0
- package/dist/static/ejs/partials/styles/topTenCard.ejs +44 -0
- package/dist/static/ejs/partials/styles/wcagCompliance/FailedCriteria.ejs +59 -0
- package/dist/static/ejs/partials/styles/wcagCompliance/WcagGaugeBar.ejs +62 -0
- package/dist/static/ejs/partials/styles/wcagCompliance.ejs +36 -0
- package/dist/static/ejs/partials/styles/wcagCoverageDetails.ejs +33 -0
- package/dist/static/ejs/partials/summaryHeader.ejs +70 -0
- package/dist/static/ejs/partials/summaryMain.ejs +49 -0
- package/dist/static/ejs/report.ejs +226 -0
- package/dist/static/ejs/summary.ejs +47 -0
- package/dist/types/types.js +1 -0
- package/dist/utils.js +1070 -0
- package/examples/oobee-cypress-integration-js/cypress/support/e2e.js +36 -6
- package/examples/oobee-cypress-integration-js/cypress.config.js +45 -1
- package/examples/oobee-cypress-integration-ts/cypress.config.ts +47 -1
- package/examples/oobee-cypress-integration-ts/src/cypress/support/e2e.ts +36 -6
- package/examples/oobee-playwright-integration-js/oobee-playwright-demo.js +2 -1
- package/examples/oobee-playwright-integration-ts/src/oobee-playwright-demo.ts +2 -1
- package/package.json +9 -3
- package/src/constants/common.ts +2 -2
- package/src/constants/constants.ts +3 -1
- package/src/crawlers/crawlDomain.ts +1 -0
- package/src/crawlers/runCustom.ts +0 -1
- package/src/npmIndex.ts +42 -24
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// import { JSDOM } from "jsdom";
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
const screenshotMap = {}; // Map of screenshot hashkey to its buffer value and screenshot path
|
|
6
|
+
export const takeScreenshotForHTMLElements = async (violations, page, randomToken, locatorTimeout = 2000, maxScreenshots = 100) => {
|
|
7
|
+
const newViolations = [];
|
|
8
|
+
let screenshotCount = 0;
|
|
9
|
+
for (const violation of violations) {
|
|
10
|
+
if (screenshotCount >= maxScreenshots) {
|
|
11
|
+
/*
|
|
12
|
+
consoleLogger.warn(
|
|
13
|
+
`Skipping screenshots for ${violation.id} as maxScreenshots (${maxScreenshots}) exceeded. You can increase it by specifying a higher value when calling takeScreenshotForHTMLElements.`,
|
|
14
|
+
);
|
|
15
|
+
*/
|
|
16
|
+
newViolations.push(violation);
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const { id: rule } = violation;
|
|
20
|
+
// Check if rule ID is 'oobee-grading-text-contents' and skip screenshot logic
|
|
21
|
+
if (rule === 'oobee-grading-text-contents') {
|
|
22
|
+
// consoleLogger.info('Skipping screenshot for rule oobee-grading-text-contents');
|
|
23
|
+
newViolations.push(violation); // Make sure it gets added
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const newViolationNodes = [];
|
|
27
|
+
for (const node of violation.nodes) {
|
|
28
|
+
const nodeWithScreenshotPath = node;
|
|
29
|
+
const { target } = node;
|
|
30
|
+
const hasValidSelector = target.length === 1 && typeof target[0] === 'string';
|
|
31
|
+
const selector = hasValidSelector ? target[0] : null;
|
|
32
|
+
if (selector) {
|
|
33
|
+
try {
|
|
34
|
+
const locator = page.locator(selector);
|
|
35
|
+
const locators = await locator.all();
|
|
36
|
+
for (const currLocator of locators) {
|
|
37
|
+
await currLocator.scrollIntoViewIfNeeded({ timeout: locatorTimeout });
|
|
38
|
+
const isVisible = await currLocator.isVisible();
|
|
39
|
+
if (isVisible) {
|
|
40
|
+
const buffer = await currLocator.screenshot({ timeout: locatorTimeout });
|
|
41
|
+
const screenshotPath = getScreenshotPath(buffer, randomToken);
|
|
42
|
+
nodeWithScreenshotPath.screenshotPath = screenshotPath;
|
|
43
|
+
screenshotCount++;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// consoleLogger.info(`Element at ${currLocator} is not visible`);
|
|
47
|
+
}
|
|
48
|
+
break; // Stop looping after finding the first visible locator
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
// consoleLogger.info(`Unable to take element screenshot at ${selector}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
newViolationNodes.push(nodeWithScreenshotPath);
|
|
56
|
+
}
|
|
57
|
+
violation.nodes = newViolationNodes;
|
|
58
|
+
newViolations.push(violation);
|
|
59
|
+
}
|
|
60
|
+
// console.log('Processed Violations (after screenshots):', JSON.stringify(newViolations, null, 2));
|
|
61
|
+
return newViolations;
|
|
62
|
+
};
|
|
63
|
+
const generateBufferHash = (buffer) => {
|
|
64
|
+
const hash = createHash('sha256');
|
|
65
|
+
hash.update(buffer);
|
|
66
|
+
return hash.digest('hex');
|
|
67
|
+
};
|
|
68
|
+
const getScreenshotPath = (buffer, randomToken) => {
|
|
69
|
+
let hashKey = generateBufferHash(buffer);
|
|
70
|
+
const existingPath = screenshotMap[hashKey];
|
|
71
|
+
// If exists identical entry in screenshot map, get its filepath
|
|
72
|
+
if (existingPath) {
|
|
73
|
+
return existingPath;
|
|
74
|
+
}
|
|
75
|
+
// Create new entry in screenshot map
|
|
76
|
+
hashKey = generateBufferHash(buffer);
|
|
77
|
+
const path = generateScreenshotPath(hashKey);
|
|
78
|
+
screenshotMap[hashKey] = path;
|
|
79
|
+
// Save image file to local storage
|
|
80
|
+
saveImageBufferToFile(buffer, `${randomToken}/${path}`);
|
|
81
|
+
return path;
|
|
82
|
+
};
|
|
83
|
+
const generateScreenshotPath = (hashKey) => {
|
|
84
|
+
return `elemScreenshots/html/${hashKey}.jpeg`;
|
|
85
|
+
};
|
|
86
|
+
const saveImageBufferToFile = (buffer, fileName) => {
|
|
87
|
+
if (!fileName)
|
|
88
|
+
return;
|
|
89
|
+
// Find and create parent directories recursively if not exist
|
|
90
|
+
const absPath = path.resolve(fileName);
|
|
91
|
+
const dir = path.dirname(absPath);
|
|
92
|
+
fs.mkdir(dir, { recursive: true }, err => {
|
|
93
|
+
if (err)
|
|
94
|
+
console.log('Error trying to create parent directory(s):', err);
|
|
95
|
+
// Write the image buffer to file
|
|
96
|
+
fs.writeFile(absPath, buffer, err => {
|
|
97
|
+
if (err)
|
|
98
|
+
console.log('Error trying to write file:', err);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
// const hasMultipleLocators = async (locator) => await locator.count() > 1;
|
|
103
|
+
// const resolveMultipleLocators = async (page, locator, html) => {
|
|
104
|
+
// const { tag, classAttrib, hrefAttrib, textContent } = generateAttribs(html);
|
|
105
|
+
// const allLocators = await locator.all();
|
|
106
|
+
// // console.log('locator before: ', locator);
|
|
107
|
+
// allLocators.forEach(async currLocator => {
|
|
108
|
+
// console.log('curr locator: ', currLocator);
|
|
109
|
+
// let hrefIsExactMatch, containsTextContent;
|
|
110
|
+
// // if (hrefAttrib) hrefIsExactMatch = (await currLocator.getAttribute('href')) === hrefAttrib;
|
|
111
|
+
// if (textContent) containsTextContent = (await currLocator.innerText()).includes(textContent);
|
|
112
|
+
// // if (hrefAttrib && textContent) {
|
|
113
|
+
// // if (hrefIsExactMatch && containsTextContent) {
|
|
114
|
+
// // locator = currLocator;
|
|
115
|
+
// // console.log('1: ', locator);
|
|
116
|
+
// // }
|
|
117
|
+
// // } else if (hrefAttrib) {
|
|
118
|
+
// // if (hrefIsExactMatch) {
|
|
119
|
+
// // locator = currLocator;
|
|
120
|
+
// // console.log('2: ', locator);
|
|
121
|
+
// // }
|
|
122
|
+
// // } else
|
|
123
|
+
// if (textContent) {
|
|
124
|
+
// if (containsTextContent) {
|
|
125
|
+
// locator = currLocator;
|
|
126
|
+
// console.log('3: ', locator);
|
|
127
|
+
// }
|
|
128
|
+
// } else {
|
|
129
|
+
// locator = null;
|
|
130
|
+
// }
|
|
131
|
+
// })
|
|
132
|
+
// console.log('final locator: ', locator);
|
|
133
|
+
// return locator;
|
|
134
|
+
// }
|
|
135
|
+
// const generateAttribs = (html) => {
|
|
136
|
+
// const processedHTMLString = html.replaceAll('\n', '');
|
|
137
|
+
// const tagNamesRegex = /(?<=[<])\s*([a-zA-Z][^\s>/]*)\b/g;
|
|
138
|
+
// const tag = processedHTMLString.match(tagNamesRegex)[0];
|
|
139
|
+
// const dom = new JSDOM(processedHTMLString);
|
|
140
|
+
// const elem = dom.window.document.querySelector(tag);
|
|
141
|
+
// const textContent = elem.textContent.trim();
|
|
142
|
+
// const classAttrib = elem.getAttribute('class')?.trim();
|
|
143
|
+
// const hrefAttrib = (tag === 'a') ? elem.getAttribute('href') : null;
|
|
144
|
+
// console.log('text content: ', textContent);
|
|
145
|
+
// return {
|
|
146
|
+
// tag,
|
|
147
|
+
// ...(classAttrib && {classAttrib}),
|
|
148
|
+
// ...(hrefAttrib && {hrefAttrib}),
|
|
149
|
+
// ...(textContent && {textContent})
|
|
150
|
+
// }
|
|
151
|
+
// }
|
|
152
|
+
// export const takeScreenshotForHTMLElements = async (screenshotData, storagePath, browserToRun) => {
|
|
153
|
+
// const screenshotDir = `${storagePath}/screenshots`;
|
|
154
|
+
// let screenshotItems = [];
|
|
155
|
+
// let randomToken = `cloned-${Date.now()}`;
|
|
156
|
+
// const clonedDir = getClonedProfilesWithRandomToken(browserToRun, randomToken);
|
|
157
|
+
// const browser = await constants.launcher.launchPersistentContext(
|
|
158
|
+
// clonedDir,
|
|
159
|
+
// {
|
|
160
|
+
// headless: false,
|
|
161
|
+
// ...getPlaywrightLaunchOptions(browserToRun)
|
|
162
|
+
// }
|
|
163
|
+
// );
|
|
164
|
+
// for (const item of screenshotData) {
|
|
165
|
+
// const domain = item.url.replaceAll("https://", '').replaceAll('/', '_');
|
|
166
|
+
// item.htmlItems = generateSelectors(item.htmlItems);
|
|
167
|
+
// const page = await browser.newPage();
|
|
168
|
+
// await page.goto(item.url);
|
|
169
|
+
// let htmlItemsWithScreenshotPath = [];
|
|
170
|
+
// for (const htmlItem of item.htmlItems) {
|
|
171
|
+
// const { rule, category, selector } = htmlItem;
|
|
172
|
+
// const locator = await getLocators(page, selector);
|
|
173
|
+
// const screenshotFilePath = `${domain}/${category}/${rule}/${selector.tag}-${htmlItemsWithScreenshotPath.length}.png`;
|
|
174
|
+
// if (locator) {
|
|
175
|
+
// await locator.screenshot({ path: `${screenshotDir}/${screenshotFilePath}` });
|
|
176
|
+
// htmlItem.screenshotPath = `screenshots/${screenshotFilePath}`;
|
|
177
|
+
// }
|
|
178
|
+
// delete htmlItem.selector;
|
|
179
|
+
// htmlItemsWithScreenshotPath.push(htmlItem);
|
|
180
|
+
// }
|
|
181
|
+
// screenshotItems.push({url: item.url, htmlItems: htmlItemsWithScreenshotPath});
|
|
182
|
+
// await page.close();
|
|
183
|
+
// }
|
|
184
|
+
// await browser.close();
|
|
185
|
+
// deleteClonedProfiles(browserToRun)
|
|
186
|
+
// return screenshotItems;
|
|
187
|
+
// }
|
|
188
|
+
// export const processScreenshotData = (allIssues) => {
|
|
189
|
+
// const scannedUrls = allIssues.pagesScanned.map(page => page.url);
|
|
190
|
+
// const screenshotData = scannedUrls.map(scannedUrl => {
|
|
191
|
+
// let htmlItems = [];
|
|
192
|
+
// ['mustFix', 'goodToFix'].map((category) => {
|
|
193
|
+
// const ruleItems = allIssues.items[category].rules;
|
|
194
|
+
// ruleItems.map(ruleItem => {
|
|
195
|
+
// const { rule, pagesAffected } = ruleItem;
|
|
196
|
+
// pagesAffected.map(affectedPage => {
|
|
197
|
+
// const { url, items } = affectedPage;
|
|
198
|
+
// if (scannedUrl === url) {
|
|
199
|
+
// items.forEach(item => {if (item.html) htmlItems.push({html: item.html, rule, category})});
|
|
200
|
+
// }
|
|
201
|
+
// })
|
|
202
|
+
// })
|
|
203
|
+
// })
|
|
204
|
+
// return {url: scannedUrl, htmlItems};
|
|
205
|
+
// })
|
|
206
|
+
// return screenshotData;
|
|
207
|
+
// }
|
|
208
|
+
// export const getScreenshotPaths = (screenshotItems, allIssues) => {
|
|
209
|
+
// screenshotItems.forEach(screenshotItem => {
|
|
210
|
+
// const { url: ssUrl, htmlItems: ssHtmlItems } = screenshotItem;
|
|
211
|
+
// ssHtmlItems.map(ssHtmlItem => {
|
|
212
|
+
// const {
|
|
213
|
+
// category: ssCategory,
|
|
214
|
+
// rule: ssRule,
|
|
215
|
+
// html: ssHtml,
|
|
216
|
+
// screenshotPath: ssPath
|
|
217
|
+
// } = ssHtmlItem;
|
|
218
|
+
// allIssues.items[ssCategory].rules = allIssues.items[ssCategory].rules
|
|
219
|
+
// .map(ruleItem => {
|
|
220
|
+
// const { rule, pagesAffected } = ruleItem;
|
|
221
|
+
// if (rule === ssRule) {
|
|
222
|
+
// ruleItem.pagesAffected = pagesAffected.map(affectedPage => {
|
|
223
|
+
// const { url, items } = affectedPage;
|
|
224
|
+
// if (ssUrl === url) {
|
|
225
|
+
// affectedPage.items = items.map(htmlItem => {
|
|
226
|
+
// const { html } = htmlItem;
|
|
227
|
+
// if (ssHtml === html) htmlItem.screenshotPath = ssPath;
|
|
228
|
+
// return htmlItem;
|
|
229
|
+
// })
|
|
230
|
+
// }
|
|
231
|
+
// return affectedPage;
|
|
232
|
+
// })
|
|
233
|
+
// }
|
|
234
|
+
// return ruleItem;
|
|
235
|
+
// })
|
|
236
|
+
// })
|
|
237
|
+
// });
|
|
238
|
+
// }
|
|
239
|
+
// const generateSelectors = (htmlItems) => {
|
|
240
|
+
// const htmlItemsWithSelectors = htmlItems.map((htmlItem) => {
|
|
241
|
+
// const { html } = htmlItem;
|
|
242
|
+
// const processedHTMLString = html.replaceAll('\n', '');
|
|
243
|
+
// const tagnameRegex = /(?<=[<])\s*([a-zA-Z][^\s>/]*)\b/g;
|
|
244
|
+
// const tagNames = processedHTMLString.match(tagnameRegex);
|
|
245
|
+
// const dom = new JSDOM(processedHTMLString);
|
|
246
|
+
// const tag = tagNames[0]
|
|
247
|
+
// const elem = dom.window.document.querySelector(tag);
|
|
248
|
+
// const classAttrib = elem.getAttribute('class')?.trim();
|
|
249
|
+
// const idAttrib = elem.getAttribute('id');
|
|
250
|
+
// const titleAttrib = elem.getAttribute('title');
|
|
251
|
+
// const placeholderAttrib = elem.getAttribute('placeholder');
|
|
252
|
+
// const altAttrib = (tag === 'img') ? elem.getAttribute('alt') : null;
|
|
253
|
+
// const hrefAttrib = (tag === 'a') ? elem.getAttribute('href') : null;
|
|
254
|
+
// let children;
|
|
255
|
+
// if (tagNames.length > 1) {
|
|
256
|
+
// const childrenHTMLItems = Array.from(elem.children).map(child => {
|
|
257
|
+
// return {
|
|
258
|
+
// html: child.outerHTML,
|
|
259
|
+
// rule: htmlItem.rule,
|
|
260
|
+
// category: htmlItem.category
|
|
261
|
+
// }
|
|
262
|
+
// });
|
|
263
|
+
// children = generateSelectors(childrenHTMLItems);
|
|
264
|
+
// }
|
|
265
|
+
// let textContent = elem.textContent.trim();
|
|
266
|
+
// let allTextContents = [];
|
|
267
|
+
// children?.map((child) => {
|
|
268
|
+
// if (child?.selector.allTextContents) allTextContents = [...allTextContents, ...child.selector.allTextContents]
|
|
269
|
+
// })
|
|
270
|
+
// if (allTextContents.includes(textContent)) {
|
|
271
|
+
// textContent = null;
|
|
272
|
+
// } else {
|
|
273
|
+
// if (textContent) allTextContents = [textContent, ...allTextContents];
|
|
274
|
+
// }
|
|
275
|
+
// const selector = {
|
|
276
|
+
// tag,
|
|
277
|
+
// processedHTMLString,
|
|
278
|
+
// ...(textContent && {textContent}),
|
|
279
|
+
// ...(allTextContents.length > 0 && {allTextContents}),
|
|
280
|
+
// ...(classAttrib && {classAttrib}),
|
|
281
|
+
// ...(idAttrib && {idAttrib}),
|
|
282
|
+
// ...(titleAttrib && {titleAttrib}),
|
|
283
|
+
// ...(placeholderAttrib && {placeholderAttrib}),
|
|
284
|
+
// ...(altAttrib && {altAttrib}),
|
|
285
|
+
// ...(hrefAttrib && {hrefAttrib}),
|
|
286
|
+
// ...(children && {children}),
|
|
287
|
+
// }
|
|
288
|
+
// htmlItem.selector = selector;
|
|
289
|
+
// return htmlItem;
|
|
290
|
+
// })
|
|
291
|
+
// return htmlItemsWithSelectors;
|
|
292
|
+
// }
|
|
293
|
+
// const generateInitialLocator = (page, selector) => {
|
|
294
|
+
// const {
|
|
295
|
+
// tag,
|
|
296
|
+
// textContent,
|
|
297
|
+
// classAttrib,
|
|
298
|
+
// idAttrib,
|
|
299
|
+
// titleAttrib,
|
|
300
|
+
// placeholderAttrib,
|
|
301
|
+
// altAttrib,
|
|
302
|
+
// children
|
|
303
|
+
// } = selector;
|
|
304
|
+
// let locator = page.locator(tag);
|
|
305
|
+
// if (classAttrib) {
|
|
306
|
+
// const classSelector = classAttrib.replaceAll(/\s+/g, '.').replace(/^/, '.').replaceAll(':', '\\:').replaceAll('(', '\\(').replaceAll(')', '\\)');
|
|
307
|
+
// locator = locator.and(page.locator(classSelector))
|
|
308
|
+
// }
|
|
309
|
+
// if (idAttrib) locator = locator.and(page.locator(`#${idAttrib}`));
|
|
310
|
+
// if (textContent) locator = locator.and(page.getByText(textContent));
|
|
311
|
+
// if (titleAttrib) locator = locator.and(page.getByTitle(titleAttrib));
|
|
312
|
+
// if (placeholderAttrib) locator = locator.and(page.getByPlaceHolder(placeholderAttrib));
|
|
313
|
+
// if (altAttrib) locator = locator.and(page.getByAltText(altAttrib));
|
|
314
|
+
// if (children) {
|
|
315
|
+
// let currLocator = locator;
|
|
316
|
+
// for (const child of children) {
|
|
317
|
+
// const childLocator = generateInitialLocator(page, child.selector);
|
|
318
|
+
// locator = locator.and(currLocator.filter({ has: childLocator })); // figure this out tmr!
|
|
319
|
+
// }
|
|
320
|
+
// }
|
|
321
|
+
// return locator;
|
|
322
|
+
// }
|
|
323
|
+
// const resolveLocators = async (locator, classAttrib, hrefAttrib) => {
|
|
324
|
+
// const locatorCount = await locator.count();
|
|
325
|
+
// if (locatorCount > 1) {
|
|
326
|
+
// let locators = [];
|
|
327
|
+
// const allLocators = await locator.all();
|
|
328
|
+
// for (let nth = 0; nth < locatorCount; nth++) {
|
|
329
|
+
// const currLocator = allLocators[nth];
|
|
330
|
+
// const isVisible = await currLocator.isVisible();
|
|
331
|
+
// if (isVisible) {
|
|
332
|
+
// let classIsExactMatch, hrefIsExactMatch;
|
|
333
|
+
// if (classAttrib) classIsExactMatch = (await currLocator.getAttribute('class')) === classAttrib;
|
|
334
|
+
// if (hrefAttrib) hrefIsExactMatch = (await currLocator.getAttribute('href')) === hrefAttrib;
|
|
335
|
+
// if (classAttrib && hrefAttrib) {
|
|
336
|
+
// if (classIsExactMatch && hrefIsExactMatch) locators.push(currLocator);
|
|
337
|
+
// } else if (classAttrib) {
|
|
338
|
+
// if (classIsExactMatch) locators.push(currLocator);
|
|
339
|
+
// } else if (hrefAttrib) {
|
|
340
|
+
// if (hrefIsExactMatch) locators.push(currLocator);
|
|
341
|
+
// } else {
|
|
342
|
+
// locators.push(currLocator);
|
|
343
|
+
// }
|
|
344
|
+
// }
|
|
345
|
+
// }
|
|
346
|
+
// return locators.length === 1 ? locators[0] : null;
|
|
347
|
+
// } else {
|
|
348
|
+
// return locator;
|
|
349
|
+
// }
|
|
350
|
+
// }
|
|
351
|
+
// const getLocators = async (page, selector) => {
|
|
352
|
+
// const locator = generateInitialLocator(page, selector);
|
|
353
|
+
// const locators = await resolveLocators(locator, selector.classAttrib, selector.hrefAttrib);
|
|
354
|
+
// return locators;
|
|
355
|
+
// }
|