@devassure/tpp-engine 0.0.0-stage → 1.8.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/README.md +18 -2
- package/index.js +121 -0
- package/package.json +30 -4
- package/src/accessibility/combined-a11y-report.js +208 -0
- package/src/accessibility/tpp-a11y.js +81 -0
- package/src/actions.js +3201 -0
- package/src/api/api-auth.js +18 -0
- package/src/api/api-service.js +289 -0
- package/src/app-config.json +7 -0
- package/src/autoheal-actions.js +309 -0
- package/src/console-log/console-log-manager.js +39 -0
- package/src/console-log/console-log-utils.js +201 -0
- package/src/database/mysql/mysql-service.js +23 -0
- package/src/database/neo4j/neo4j-service.js +32 -0
- package/src/database/oracle/oracle-service.js +48 -0
- package/src/database/pg/pg-service.js +29 -0
- package/src/downloads/downloads-manager.js +86 -0
- package/src/downloads/downloads-utils.js +113 -0
- package/src/globalsetup.js +68 -0
- package/src/globalteardown.js +57 -0
- package/src/hooks/each-hook.js +45 -0
- package/src/hooks/hook-utils.js +21 -0
- package/src/library/authenticator/authenticator-utils.js +43 -0
- package/src/library/csv/csv-utils.js +297 -0
- package/src/library/date/date-utils.js +168 -0
- package/src/library/emailTesting/TokenConfig.js +46 -0
- package/src/library/emailTesting/emailTesting-utils.js +527 -0
- package/src/library/excel/excel-utils.js +922 -0
- package/src/library/file/file-action-utils.js +129 -0
- package/src/library/file/file-utils.js +127 -0
- package/src/library/json/json-utils.js +104 -0
- package/src/library/math/math-utils.js +283 -0
- package/src/library/softAssert/softAssert-utils.js +154 -0
- package/src/library/table/table-utils.js +92 -0
- package/src/library/text/text-utils.js +45 -0
- package/src/library/tpp-library.js +81 -0
- package/src/library/web/slider-utils.js +66 -0
- package/src/mobile/appium/appium-manager.js +158 -0
- package/src/mobile/device-cloud/browserstack-service.js +44 -0
- package/src/mobile/device-cloud/commands/browserstack-commands.js +20 -0
- package/src/mobile/device-cloud/commands/device-cloud-commands.js +17 -0
- package/src/mobile/device-cloud/device-cloud-service.js +29 -0
- package/src/mobile/device-cloud/pcloudy-service.js +65 -0
- package/src/mobile/mobile-actions.js +1568 -0
- package/src/mobile/recorder-utils/css-selector-generator.js +2 -0
- package/src/mobile/recorder-utils/finder.js +752 -0
- package/src/mobile/recorder-utils/mobile-recorder.js +266 -0
- package/src/mobile/web-driver-utils.js +110 -0
- package/src/models/tpp-element.js +58 -0
- package/src/models/tpp-mobile-element.js +50 -0
- package/src/network-log/network-log-manager.js +96 -0
- package/src/network-log/network-log-utils.js +361 -0
- package/src/types/elements.types.js +24 -0
- package/src/utils/ai-utils.js +48 -0
- package/src/utils/assert-utils.js +50 -0
- package/src/utils/autoheal-utils.js +9 -0
- package/src/utils/common-utils.js +120 -0
- package/src/utils/cookie-utils.js +70 -0
- package/src/utils/json-utils.js +10 -0
- package/src/utils/log-utils.js +10 -0
- package/src/utils/logger.js +76 -0
- package/src/utils/screenshot-utils.js +42 -0
- package/src/utils/token-utils.js +18 -0
- package/src/utils/vectordb-utils.js +125 -0
- package/src/visual-test/tpp-visual-test.js +122 -0
- package/src/web/web-executor-agent.js +149 -0
- package/src/web/web-executor-data-fetch.js +353 -0
- package/test-excel-utils.js +570 -0
package/README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# tpp-engine
|
|
2
|
+
NPM module which provides interface for all the automation methods.
|
|
3
|
+
This package is tightly coupled to the tpp-cli. The CLI translates the tpp files to the methods defined in the tpp-engine.
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
## To Test the engine changes:
|
|
6
|
+
```
|
|
7
|
+
npm pack
|
|
8
|
+
#Copy the path of the tgz from the project root
|
|
9
|
+
#Go to io.testpp.at/.target/<project_id>/
|
|
10
|
+
npm install <path of tgz>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## To Publish the package to private github:
|
|
14
|
+
```
|
|
15
|
+
npm login --scope=@test-plus-plus --auth-type=legacy --registry=https://npm.pkg.github.com
|
|
16
|
+
# Use the personal access token with write access
|
|
17
|
+
# Replace the GITHUB_PERSONAL_TOKEN with your token in .npmrc
|
|
18
|
+
npm publish
|
|
19
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const { test, chromium } = require('@playwright/test');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { Actions } = require('./src/actions');
|
|
4
|
+
const { NetworkLogManager } = require('./src/network-log/network-log-manager');
|
|
5
|
+
const { ConsoleLogManager } = require('./src/console-log/console-log-manager');
|
|
6
|
+
const { DownloadsManager } = require('./src/downloads/downloads-manager');
|
|
7
|
+
const { getAppiumConfig } = require('./src/mobile/appium/appium-manager');
|
|
8
|
+
const { MobileActions } = require('./src/mobile/mobile-actions');
|
|
9
|
+
const getSelectorScript = require('@test-plus-plus/tpp-locator');
|
|
10
|
+
|
|
11
|
+
const tppTest = test.extend({
|
|
12
|
+
context: async ({ context: originalContext }, use, testInfo) => {
|
|
13
|
+
let playwrightConfig;
|
|
14
|
+
if(testInfo?.config?.configFile) {
|
|
15
|
+
playwrightConfig = require(testInfo.config.configFile);
|
|
16
|
+
}
|
|
17
|
+
if(!playwrightConfig?.customConfig?.chromeExtension) {
|
|
18
|
+
await use(originalContext);
|
|
19
|
+
} else {
|
|
20
|
+
const pathToExtension = playwrightConfig.customConfig.chromeExtension;
|
|
21
|
+
const contextPath = `temp_context_data/${testInfo.title.replace(/[^a-zA-Z0-9]/g, "_")}-${Date.now()}`;
|
|
22
|
+
const context = await chromium.launchPersistentContext(contextPath, {
|
|
23
|
+
headless: false,
|
|
24
|
+
screen: testInfo?.project?.use?.viewport,
|
|
25
|
+
args: [
|
|
26
|
+
...(testInfo?.project?.use?.launchOptions?.args || []),
|
|
27
|
+
`--disable-extensions-except=${pathToExtension}`,
|
|
28
|
+
`--load-extension=${pathToExtension}`,
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await use(context);
|
|
33
|
+
//Will be run at the end of the test
|
|
34
|
+
!!contextPath && fs.rmSync(contextPath, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
actions: async ({ page, request, context }, use, testInfo) => {
|
|
38
|
+
let playwrightConfig;
|
|
39
|
+
if(testInfo?.config?.configFile) {
|
|
40
|
+
playwrightConfig = require(testInfo.config.configFile);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { cssSelector: cssSelectorGeneratorScript, finder: finderScript } = getSelectorScript();
|
|
44
|
+
for (const page of context.pages()) {
|
|
45
|
+
await page.addInitScript(finderScript);
|
|
46
|
+
await page.addInitScript(cssSelectorGeneratorScript);
|
|
47
|
+
}
|
|
48
|
+
context.on('page', async (page) => {
|
|
49
|
+
await page.addInitScript(finderScript);
|
|
50
|
+
await page.addInitScript(cssSelectorGeneratorScript);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
let webComponents;
|
|
54
|
+
if(!playwrightConfig?.customConfig?.testType || playwrightConfig.customConfig.testType === 'web') {
|
|
55
|
+
const networkLogManager = new NetworkLogManager(playwrightConfig?.customConfig?.networkLogTracing, page);
|
|
56
|
+
const consoleLogManager = new ConsoleLogManager(playwrightConfig?.customConfig?.consoleLogTracing, page);
|
|
57
|
+
const downloadsManager = new DownloadsManager(playwrightConfig?.customConfig?.downloadsConfig, page);
|
|
58
|
+
let extensionId = undefined;
|
|
59
|
+
if(playwrightConfig?.customConfig?.chromeExtension) {
|
|
60
|
+
extensionId = await getExtension(context);
|
|
61
|
+
}
|
|
62
|
+
webComponents = {
|
|
63
|
+
networkLogManager: networkLogManager,
|
|
64
|
+
consoleLogManager: consoleLogManager,
|
|
65
|
+
downloadsManager: downloadsManager,
|
|
66
|
+
extensionId: extensionId,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let mobileActions;
|
|
71
|
+
if(playwrightConfig?.customConfig?.testType === 'mobile') {
|
|
72
|
+
const projectInfo = playwrightConfig.daProjectInfo;
|
|
73
|
+
let mobileConfig = playwrightConfig.customConfig.mobileConfig || {};
|
|
74
|
+
mobileConfig.parallelIndex = testInfo.parallelIndex || 0;
|
|
75
|
+
mobileConfig.appium = getAppiumConfig();
|
|
76
|
+
mobileConfig.appium.pid = mobileConfig.appium?.pids?.at(mobileConfig.parallelIndex);
|
|
77
|
+
mobileConfig.appium.port = mobileConfig.appium?.ports?.at(mobileConfig.parallelIndex);
|
|
78
|
+
mobileConfig.device = mobileConfig.devices?.at(mobileConfig.parallelIndex);
|
|
79
|
+
mobileConfig.cloudDevice = mobileConfig.cloudDevices?.at(mobileConfig.parallelIndex);
|
|
80
|
+
mobileActions = new MobileActions(mobileConfig, testInfo.retry, testInfo.project?.retries || 0, projectInfo, playwrightConfig?.customConfig?.timeouts);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let customConfig = playwrightConfig?.customConfig || {};
|
|
84
|
+
customConfig.testInfo = {
|
|
85
|
+
retry: testInfo.retry,
|
|
86
|
+
maxRetries: testInfo.project?.retries || 0,
|
|
87
|
+
threadIndex: testInfo.parallelIndex || 0,
|
|
88
|
+
status: 'running',
|
|
89
|
+
environment: customConfig?.environment || 'default',
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const actions = new Actions(page, request, undefined, testInfo.retry, testInfo.project?.retries || 0, playwrightConfig?.customConfig, webComponents, mobileActions);
|
|
93
|
+
await use(actions);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const getExtension = async (context) => {
|
|
98
|
+
let [background] = context.serviceWorkers();
|
|
99
|
+
if (!background)
|
|
100
|
+
[background] = context.backgroundPages();
|
|
101
|
+
if (!background) {
|
|
102
|
+
try {
|
|
103
|
+
background = await context.waitForEvent('serviceworker', {timeout: 2000});
|
|
104
|
+
} catch(ex) {}
|
|
105
|
+
}
|
|
106
|
+
if (!background) {
|
|
107
|
+
try {
|
|
108
|
+
background = await context.waitForEvent('backgroundpage', {timeout: 2000});
|
|
109
|
+
} catch(ex) {}
|
|
110
|
+
}
|
|
111
|
+
if(context.pages().length > 1) {
|
|
112
|
+
await context.pages()[0].close();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if(!background) {
|
|
116
|
+
return 'TPP_EXTN_NOT_LOADED';
|
|
117
|
+
}
|
|
118
|
+
return background.url().split('/')[2];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
exports.tppTest = tppTest;
|
package/package.json
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devassure/tpp-engine",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"description": "Automation library to run TPP tests",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"author": "sandy-tpp",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@axe-core/playwright": "4.10.1",
|
|
13
|
+
"@playwright/test": "1.60.0",
|
|
14
|
+
"@test-plus-plus/tpp-locator": "1.0.9",
|
|
15
|
+
"axios": "^1.7.7",
|
|
16
|
+
"dayjs": "^1.11.19",
|
|
17
|
+
"exceljs": "^4.4.0",
|
|
18
|
+
"expect-axe-playwright": "4.1.2",
|
|
19
|
+
"fs-extra": "^11.2.0",
|
|
20
|
+
"lodash": "^4.17.21",
|
|
21
|
+
"mysql2": "^3.9.1",
|
|
22
|
+
"neo4j-driver": "^5.28.1",
|
|
23
|
+
"node-clipboardy": "^1.0.3",
|
|
24
|
+
"oracledb": "^6.9.0",
|
|
25
|
+
"otplib": "^12.0.1",
|
|
26
|
+
"pg": "^8.11.5",
|
|
27
|
+
"tree-kill": "^1.2.2",
|
|
28
|
+
"webdriverio": "^8.11.2",
|
|
29
|
+
"ws": "^8.18.1",
|
|
30
|
+
"xlsx": "^0.18.5"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { FileUtils } = require('../library/file/file-utils');
|
|
4
|
+
const fsExtra = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
const resultsDir = 'axe-core-results';
|
|
7
|
+
const logoUrl = 'https://www.devassure.io/images/DA-logo.svg';
|
|
8
|
+
|
|
9
|
+
function accumulateImpactSummary(violations, summary) {
|
|
10
|
+
violations.forEach((violation) => {
|
|
11
|
+
if (violation.impact) {
|
|
12
|
+
summary[violation.impact] = (summary[violation.impact] || 0) + 1;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function escapeHtml(html) {
|
|
18
|
+
return html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getImpactColor(impact) {
|
|
22
|
+
switch (impact) {
|
|
23
|
+
case 'critical': return '#e63946';
|
|
24
|
+
case 'serious': return '#f77f00';
|
|
25
|
+
case 'moderate': return '#dea309';
|
|
26
|
+
case 'minor': return '#70aa45';
|
|
27
|
+
default: return '#333';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function generateCombinedA11yReport() {
|
|
32
|
+
if(!fs.existsSync(resultsDir)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const resultFiles = fs.readdirSync(resultsDir).filter((file) => file.endsWith('.json'));
|
|
36
|
+
if(!resultFiles?.length) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let totalImpactSummary = { critical: 0, serious: 0, moderate: 0, minor: 0 };
|
|
41
|
+
resultFiles.forEach((file) => {
|
|
42
|
+
const filePath = path.join(resultsDir, file);
|
|
43
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
44
|
+
|
|
45
|
+
accumulateImpactSummary(data.violations, totalImpactSummary);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
let htmlContent = `
|
|
49
|
+
<html>
|
|
50
|
+
<head>
|
|
51
|
+
<title>DevAssure Accessibility Report</title>
|
|
52
|
+
<style>
|
|
53
|
+
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f4f4f9; }
|
|
54
|
+
.header { display: flex; align-items: center; justify-content: center; margin-bottom: 20px; }
|
|
55
|
+
.logo { max-width: 50px; max-height: 50px; position: absolute; left: 20px; }
|
|
56
|
+
h1 { text-align: center; color: #333; margin: 0; }
|
|
57
|
+
h3 { margin: 8px; }
|
|
58
|
+
.summary, .details { margin: 20px 0; padding: 12px; background: #fff; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); }
|
|
59
|
+
.impact-summary { display: flex; padding: 10px; }
|
|
60
|
+
.impact-item { font-size: 18px; padding: 10px; color: #fff; border-radius: 4px; text-align: center; margin-right: 24px; }
|
|
61
|
+
.impact-critical { background-color: #e63946; }
|
|
62
|
+
.impact-serious { background-color: #f77f00; }
|
|
63
|
+
.impact-moderate { background-color: #dea309; }
|
|
64
|
+
.impact-minor { background-color: #70aa45; }
|
|
65
|
+
.filter-container { display: flex; justify-content: right; margin-bottom: 20px; align-items: center; }
|
|
66
|
+
.filter-select { padding: 8px; font-size: 16px; margin-left: 10px; cursor: pointer; }
|
|
67
|
+
.expand-collapse { cursor: pointer; color: #007bff; text-decoration: underline; margin-left: 16px }
|
|
68
|
+
.expand-collapse:hover { color: #0056b3; }
|
|
69
|
+
.collapsible { cursor: pointer; display: flex; justify-content: space-between; align-items: center; padding: 4px 16px; border-radius: 4px; color: #fff; margin-top: 6px; }
|
|
70
|
+
.collapsible.default { background-color: #457b9d; }
|
|
71
|
+
.collapsible.no-violations { background-color: #4caf50; }
|
|
72
|
+
.collapsible:hover { background-color: #1d3557; }
|
|
73
|
+
.content { display: none; padding: 10px; border-left: 4px solid #ddd; background: #f1faee; margin-top: 5px; border-radius: 0 0 8px 8px; }
|
|
74
|
+
.violation { margin-top: 10px; border-top: 1px solid #ddd; padding-top: 10px; }
|
|
75
|
+
.violation h4 { margin: 5px 0; font-size: 20px; }
|
|
76
|
+
.impact { padding: 4px 8px; border: 1px solid #bbb; border-radius: 4px; width: fit-content; }
|
|
77
|
+
.element-box { font-family: Consolas, "Courier New", monospace; background-color: #e8e8e8; padding: 5px; border-radius: 4px; margin-top: 5px; color: #333; white-space: pre-wrap; }
|
|
78
|
+
.failure-message { font-family: Consolas, "Courier New", monospace; color: #e63946; }
|
|
79
|
+
</style>
|
|
80
|
+
</head>
|
|
81
|
+
<body>
|
|
82
|
+
<div class="header">
|
|
83
|
+
<img src="${logoUrl}" alt="Logo" class="logo"/>
|
|
84
|
+
<h1>DevAssure - Accessibility Report</h1>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="summary">
|
|
87
|
+
<h3>Summary</h3>
|
|
88
|
+
<div class="impact-summary">
|
|
89
|
+
<div class="impact-item impact-critical">Critical: <span>${totalImpactSummary.critical}</span></div>
|
|
90
|
+
<div class="impact-item impact-serious">Serious: <span>${totalImpactSummary.serious}</span></div>
|
|
91
|
+
<div class="impact-item impact-moderate">Moderate: <span>${totalImpactSummary.moderate}</span></div>
|
|
92
|
+
<div class="impact-item impact-minor">Minor: <span>${totalImpactSummary.minor}</span></div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="filter-container">
|
|
97
|
+
<label for="filter-select">Filter:</label>
|
|
98
|
+
<select id="filter-select" class="filter-select" onchange="applyFilter()">
|
|
99
|
+
<option value="all">All</option>
|
|
100
|
+
<option value="failures">Only Failures</option>
|
|
101
|
+
</select>
|
|
102
|
+
<span class="expand-collapse" onclick="collapseAll()">Collapse All</span>
|
|
103
|
+
<span class="expand-collapse" onclick="expandAll()">Expand All</span>
|
|
104
|
+
</div>
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
// Process each JSON file to build the report
|
|
108
|
+
resultFiles.forEach((file, index) => {
|
|
109
|
+
const filePath = path.join(resultsDir, file);
|
|
110
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
111
|
+
|
|
112
|
+
const violationCount = data.violations?.length || 0;
|
|
113
|
+
const collapsibleClass = violationCount === 0 ? 'no-violations' : 'default';
|
|
114
|
+
|
|
115
|
+
htmlContent += `
|
|
116
|
+
<div class="collapsible ${collapsibleClass}" onclick="toggleSection(this)"
|
|
117
|
+
data-violations="${violationCount}" id="collapsible-${index}">
|
|
118
|
+
<span><strong>${data.testId || file}</strong> ( ${data.url || 'Unknown URL'} )</span>
|
|
119
|
+
<span class="collapsible">${violationCount} violations</span>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="content">
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
if (!data.violations?.length) {
|
|
125
|
+
htmlContent += `<p>No violations found.</p>`;
|
|
126
|
+
} else {
|
|
127
|
+
data.violations.forEach((violation) => {
|
|
128
|
+
const impactColor = getImpactColor(violation.impact);
|
|
129
|
+
htmlContent += `
|
|
130
|
+
<div class="violation">
|
|
131
|
+
<h4 style="color: ${impactColor};">${violation.id}: ${violation.help}</h4>
|
|
132
|
+
<p class="impact">Impact:<strong> ${violation.impact}</strong></p>
|
|
133
|
+
<p>${violation.description}</p>
|
|
134
|
+
<ul>
|
|
135
|
+
${violation.nodes
|
|
136
|
+
.map(
|
|
137
|
+
(node) => `
|
|
138
|
+
<li>
|
|
139
|
+
<p>Element:</p>
|
|
140
|
+
<div class="element-box">${escapeHtml(node.html)}</div>
|
|
141
|
+
<p class="failure-message">${node.failureSummary}</p>
|
|
142
|
+
</li>
|
|
143
|
+
`
|
|
144
|
+
)
|
|
145
|
+
.join('')}
|
|
146
|
+
</ul>
|
|
147
|
+
</div>
|
|
148
|
+
`;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
htmlContent += `</div>`; // Close the collapsible content div
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Add JavaScript for collapsible sections
|
|
156
|
+
htmlContent += `
|
|
157
|
+
<script>
|
|
158
|
+
function toggleSection(element) {
|
|
159
|
+
const content = element.nextElementSibling;
|
|
160
|
+
content.style.display = content.style.display === 'block' ? 'none' : 'block';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function applyFilter() {
|
|
164
|
+
const filter = document.getElementById('filter-select').value;
|
|
165
|
+
const collapsibles = document.querySelectorAll('.collapsible');
|
|
166
|
+
|
|
167
|
+
collapsibles.forEach((collapsible) => {
|
|
168
|
+
const violations = parseInt(collapsible.getAttribute('data-violations'), 10);
|
|
169
|
+
if (filter === 'failures' && violations === 0) {
|
|
170
|
+
collapsible.style.display = 'none';
|
|
171
|
+
collapsible.nextElementSibling.style.display = 'none';
|
|
172
|
+
} else {
|
|
173
|
+
collapsible.style.display = 'flex';
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function expandAll() {
|
|
179
|
+
const contents = document.querySelectorAll('.content');
|
|
180
|
+
contents.forEach(content => content.style.display = 'block');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function collapseAll() {
|
|
184
|
+
const contents = document.querySelectorAll('.content');
|
|
185
|
+
contents.forEach(content => content.style.display = 'none');
|
|
186
|
+
}
|
|
187
|
+
</script>
|
|
188
|
+
</body>
|
|
189
|
+
</html>
|
|
190
|
+
`;
|
|
191
|
+
|
|
192
|
+
new FileUtils().writeTextContent(path.join("tpp-results", 'combined-accessibility-report.html'), htmlContent)
|
|
193
|
+
|
|
194
|
+
fsExtra.removeSync(resultsDir);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function writeAxeResultToFile(axeResult, caller, fileLabel) {
|
|
198
|
+
const fileName = `da-axe-result-${fileLabel}.json`;
|
|
199
|
+
const filePath = path.join(resultsDir, fileName);
|
|
200
|
+
const data = {
|
|
201
|
+
testId: caller,
|
|
202
|
+
...axeResult,
|
|
203
|
+
};
|
|
204
|
+
new FileUtils().writeTextContent(filePath, JSON.stringify(data, null, 4));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
exports.generateCombinedA11yReport = generateCombinedA11yReport;
|
|
208
|
+
exports.writeAxeResultToFile = writeAxeResultToFile;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const { expect } = require("@playwright/test");
|
|
2
|
+
const { default: matchers } = require("expect-axe-playwright");
|
|
3
|
+
const { generateRandomString } = require("../utils/common-utils");
|
|
4
|
+
const { default: AxeBuilder } = require("@axe-core/playwright");
|
|
5
|
+
const { writeAxeResultToFile } = require("./combined-a11y-report");
|
|
6
|
+
expect.extend(matchers);
|
|
7
|
+
|
|
8
|
+
class TppA11y {
|
|
9
|
+
|
|
10
|
+
constructor(page, caller, softAssert) {
|
|
11
|
+
this.page = page;
|
|
12
|
+
this.caller = caller;
|
|
13
|
+
this.softAssert = softAssert;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async analysePage() {
|
|
17
|
+
const reportLabel = generateRandomString();
|
|
18
|
+
let failure = false;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const fileName = `da-a11y-report-${reportLabel}.html`;
|
|
22
|
+
await expect(this.page).toPassAxe({
|
|
23
|
+
filename: fileName,
|
|
24
|
+
runOnly: {
|
|
25
|
+
type: 'tag',
|
|
26
|
+
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
} catch (e) {
|
|
30
|
+
failure = true;
|
|
31
|
+
const result = await new AxeBuilder({ page: this.page })
|
|
32
|
+
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
|
33
|
+
.include('body')
|
|
34
|
+
.analyze()
|
|
35
|
+
writeAxeResultToFile(result, this.caller, reportLabel);
|
|
36
|
+
this.handleException(e);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if(!failure) {
|
|
40
|
+
const data = {violations: [], url: this.page.url()};
|
|
41
|
+
writeAxeResultToFile(data, this.caller, reportLabel);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async analyseElement(tppElement) {
|
|
46
|
+
const locator = tppElement.locator();
|
|
47
|
+
const fileName = `da-a11y-report-${generateRandomString()}.html`;
|
|
48
|
+
try {
|
|
49
|
+
await expect(locator).toPassAxe({filename: fileName});
|
|
50
|
+
} catch (e) {
|
|
51
|
+
this.handleException(e);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
handleException(ex) {
|
|
56
|
+
console.log("Accessibility exception: ", ex);
|
|
57
|
+
if(this.softAssert?.softAssert) {
|
|
58
|
+
if(this.softAssert?.logMessage) {
|
|
59
|
+
this.softAssert.logMessage("Soft Assert - Accessibility violations present!");
|
|
60
|
+
}
|
|
61
|
+
this.softAssert.softAssert.assertions?.push({ result: false, message: this.softAssert?.assertMessage || 'Accessibility violations present!' });
|
|
62
|
+
} else {
|
|
63
|
+
throw {message: 'There are Accessibility violations present!'};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const checkA11yAttachments = (tppTest, caller) => {
|
|
70
|
+
const axeReports = tppTest.info()?.attachments?.filter(
|
|
71
|
+
attachment => attachment.name?.includes('da-a11y-report-') && attachment.name?.includes('.html')
|
|
72
|
+
);
|
|
73
|
+
if(!axeReports?.length) return;
|
|
74
|
+
axeReports.forEach((axeReport, index) => {
|
|
75
|
+
const log = JSON.stringify({reportPath: axeReport.path, label: axeReport.name, index: index + 1});
|
|
76
|
+
console.log(`[[TPP]] ${caller} :: [a11y-report] ${log}`);
|
|
77
|
+
})
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
exports.TppA11y = TppA11y;
|
|
81
|
+
exports.checkA11yAttachments = checkA11yAttachments;
|