@arghajit/dummy 0.1.3 → 0.3.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
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# Playwright Pluse Report
|
|
2
2
|
|
|
3
|
-
](https://www.npmjs.com/package/@arghajit/playwright-pulse-report)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.npmjs.com/package/@arghajit/playwright-pulse-report)
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
4
9
|
_The ultimate Playwright reporter — Interactive dashboard with historical trend analytics, CI/CD-ready standalone HTML reports, and sharding support for scalable test execution._
|
|
5
10
|
|
|
6
11
|
## [Live Demo](https://pulse-report.netlify.app/)
|
|
@@ -272,8 +277,7 @@ export default defineConfig({
|
|
|
272
277
|
|
|
273
278
|
---
|
|
274
279
|
|
|
275
|
-
|
|
276
|
-
<h2>Pulse Dashboard</h2>
|
|
280
|
+

|
|
277
281
|
|
|
278
282
|
**Real-time Playwright Test Monitoring & Analysis**
|
|
279
283
|
|
|
@@ -197,7 +197,7 @@ class PlaywrightPulseReporter {
|
|
|
197
197
|
};
|
|
198
198
|
}
|
|
199
199
|
async onTestEnd(test, result) {
|
|
200
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
200
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
201
201
|
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
202
202
|
const browserDetails = this.getBrowserDetails(test);
|
|
203
203
|
const testStatus = convertStatus(result.status, test);
|
|
@@ -261,6 +261,7 @@ class PlaywrightPulseReporter {
|
|
|
261
261
|
attachments: [],
|
|
262
262
|
stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
|
|
263
263
|
stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
|
|
264
|
+
annotations: ((_k = test.annotations) === null || _k === void 0 ? void 0 : _k.length) > 0 ? test.annotations : undefined,
|
|
264
265
|
...testSpecificData,
|
|
265
266
|
};
|
|
266
267
|
for (const [index, attachment] of result.attachments.entries()) {
|
|
@@ -277,16 +278,16 @@ class PlaywrightPulseReporter {
|
|
|
277
278
|
await this._ensureDirExists(path.dirname(absoluteDestPath));
|
|
278
279
|
await fs.copyFile(attachment.path, absoluteDestPath);
|
|
279
280
|
if (attachment.contentType.startsWith("image/")) {
|
|
280
|
-
(
|
|
281
|
+
(_l = pulseResult.screenshots) === null || _l === void 0 ? void 0 : _l.push(relativeDestPath);
|
|
281
282
|
}
|
|
282
283
|
else if (attachment.contentType.startsWith("video/")) {
|
|
283
|
-
(
|
|
284
|
+
(_m = pulseResult.videoPath) === null || _m === void 0 ? void 0 : _m.push(relativeDestPath);
|
|
284
285
|
}
|
|
285
286
|
else if (attachment.name === "trace") {
|
|
286
287
|
pulseResult.tracePath = relativeDestPath;
|
|
287
288
|
}
|
|
288
289
|
else {
|
|
289
|
-
(
|
|
290
|
+
(_o = pulseResult.attachments) === null || _o === void 0 ? void 0 : _o.push({
|
|
290
291
|
name: attachment.name,
|
|
291
292
|
path: relativeDestPath,
|
|
292
293
|
contentType: attachment.contentType,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -46,6 +46,15 @@ export interface TestResult {
|
|
|
46
46
|
totalWorkers?: number;
|
|
47
47
|
configFile?: string;
|
|
48
48
|
metadata?: string;
|
|
49
|
+
annotations?: {
|
|
50
|
+
type: string;
|
|
51
|
+
description?: string;
|
|
52
|
+
location?: {
|
|
53
|
+
file: string;
|
|
54
|
+
line: number;
|
|
55
|
+
column: number;
|
|
56
|
+
};
|
|
57
|
+
}[];
|
|
49
58
|
}
|
|
50
59
|
export interface TestRun {
|
|
51
60
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arghajit/dummy",
|
|
3
3
|
"author": "Arghajit Singha",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "A Playwright reporter and dashboard for visualizing test results.",
|
|
6
6
|
"homepage": "https://playwright-pulse-report.netlify.app/",
|
|
7
7
|
"keywords": [
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"send-report",
|
|
19
19
|
"email",
|
|
20
20
|
"playwright-report",
|
|
21
|
-
"pulse"
|
|
21
|
+
"pulse",
|
|
22
|
+
"ai-failure-analysis"
|
|
22
23
|
],
|
|
23
24
|
"main": "dist/reporter/index.js",
|
|
24
25
|
"types": "dist/reporter/index.d.ts",
|
|
@@ -1713,41 +1713,86 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1713
1713
|
};
|
|
1714
1714
|
|
|
1715
1715
|
return `
|
|
1716
|
-
<div class="test-case" data-status="${
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1716
|
+
<div class="test-case" data-status="${
|
|
1717
|
+
test.status
|
|
1718
|
+
}" data-browser="${sanitizeHTML(browser)}" data-tags="${(test.tags || [])
|
|
1719
|
+
.join(",")
|
|
1720
|
+
.toLowerCase()}">
|
|
1720
1721
|
<div class="test-case-header" role="button" aria-expanded="false">
|
|
1721
1722
|
<div class="test-case-summary">
|
|
1722
1723
|
<span class="status-badge ${getStatusClass(test.status)}">${String(
|
|
1723
|
-
|
|
1724
|
-
|
|
1724
|
+
test.status
|
|
1725
|
+
).toUpperCase()}</span>
|
|
1725
1726
|
<span class="test-case-title" title="${sanitizeHTML(
|
|
1726
1727
|
test.name
|
|
1727
1728
|
)}">${sanitizeHTML(testTitle)}</span>
|
|
1728
1729
|
<span class="test-case-browser">(${sanitizeHTML(browser)})</span>
|
|
1729
1730
|
</div>
|
|
1730
1731
|
<div class="test-case-meta">
|
|
1731
|
-
${
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1732
|
+
${
|
|
1733
|
+
test.tags && test.tags.length > 0
|
|
1734
|
+
? test.tags
|
|
1735
|
+
.map((t) => `<span class="tag">${sanitizeHTML(t)}</span>`)
|
|
1736
|
+
.join(" ")
|
|
1737
|
+
: ""
|
|
1738
|
+
}
|
|
1737
1739
|
<span class="test-duration">${formatDuration(test.duration)}</span>
|
|
1738
1740
|
</div>
|
|
1739
1741
|
</div>
|
|
1740
1742
|
<div class="test-case-content" style="display: none;">
|
|
1741
1743
|
<p><strong>Full Path:</strong> ${sanitizeHTML(test.name)}</p>
|
|
1744
|
+
${
|
|
1745
|
+
test.annotations && test.annotations.length > 0
|
|
1746
|
+
? `<div class="annotations-section" style="margin: 12px 0; padding: 12px; background-color: rgba(139, 92, 246, 0.1); border: 1px solid rgba(139, 92, 246, 0.3); border-left: 4px solid #8b5cf6; border-radius: 4px;">
|
|
1747
|
+
<h4 style="margin-top: 0; margin-bottom: 10px; color: #8b5cf6; font-size: 1.1em;">📌 Annotations</h4>
|
|
1748
|
+
${test.annotations
|
|
1749
|
+
.map((annotation, idx) => {
|
|
1750
|
+
const isIssueOrBug =
|
|
1751
|
+
annotation.type === "issue" ||
|
|
1752
|
+
annotation.type === "bug";
|
|
1753
|
+
const descriptionText = annotation.description || "";
|
|
1754
|
+
const typeLabel = sanitizeHTML(annotation.type);
|
|
1755
|
+
const descriptionHtml =
|
|
1756
|
+
isIssueOrBug && descriptionText.match(/^[A-Z]+-\d+$/)
|
|
1757
|
+
? `<a href="#" class="annotation-link" data-annotation="${sanitizeHTML(
|
|
1758
|
+
descriptionText
|
|
1759
|
+
)}" style="color: #3b82f6; text-decoration: underline; cursor: pointer;">${sanitizeHTML(
|
|
1760
|
+
descriptionText
|
|
1761
|
+
)}</a>`
|
|
1762
|
+
: sanitizeHTML(descriptionText);
|
|
1763
|
+
const locationText = annotation.location
|
|
1764
|
+
? `<div style="font-size: 0.85em; color: #6b7280; margin-top: 4px;">Location: ${sanitizeHTML(
|
|
1765
|
+
annotation.location.file
|
|
1766
|
+
)}:${annotation.location.line}:${
|
|
1767
|
+
annotation.location.column
|
|
1768
|
+
}</div>`
|
|
1769
|
+
: "";
|
|
1770
|
+
return `<div style="margin-bottom: ${
|
|
1771
|
+
idx < test.annotations.length - 1 ? "10px" : "0"
|
|
1772
|
+
};">
|
|
1773
|
+
<strong style="color: #8b5cf6;">Type:</strong> <span style="background-color: rgba(139, 92, 246, 0.2); padding: 2px 8px; border-radius: 4px; font-size: 0.9em;">${typeLabel}</span>
|
|
1774
|
+
${
|
|
1775
|
+
descriptionText
|
|
1776
|
+
? `<br><strong style="color: #8b5cf6;">Description:</strong> ${descriptionHtml}`
|
|
1777
|
+
: ""
|
|
1778
|
+
}
|
|
1779
|
+
${locationText}
|
|
1780
|
+
</div>`;
|
|
1781
|
+
})
|
|
1782
|
+
.join("")}
|
|
1783
|
+
</div>`
|
|
1784
|
+
: ""
|
|
1785
|
+
}
|
|
1742
1786
|
<p><strong>Test run Worker ID:</strong> ${sanitizeHTML(
|
|
1743
1787
|
test.workerId
|
|
1744
1788
|
)} [<strong>Total No. of Workers:</strong> ${sanitizeHTML(
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
${
|
|
1748
|
-
|
|
1749
|
-
test
|
|
1750
|
-
|
|
1789
|
+
test.totalWorkers
|
|
1790
|
+
)}]</p>
|
|
1791
|
+
${
|
|
1792
|
+
test.errorMessage
|
|
1793
|
+
? `<div class="test-error-summary">${formatPlaywrightError(
|
|
1794
|
+
test.errorMessage
|
|
1795
|
+
)}
|
|
1751
1796
|
<button
|
|
1752
1797
|
class="copy-error-btn"
|
|
1753
1798
|
onclick="copyErrorToClipboard(this)"
|
|
@@ -1768,13 +1813,14 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1768
1813
|
Copy Error Prompt
|
|
1769
1814
|
</button>
|
|
1770
1815
|
</div>`
|
|
1771
|
-
|
|
1816
|
+
: ""
|
|
1772
1817
|
}
|
|
1773
|
-
${
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1818
|
+
${
|
|
1819
|
+
test.snippet
|
|
1820
|
+
? `<div class="code-section"><h4>Error Snippet</h4><pre><code>${formatPlaywrightError(
|
|
1821
|
+
test.snippet
|
|
1822
|
+
)}</code></pre></div>`
|
|
1823
|
+
: ""
|
|
1778
1824
|
}
|
|
1779
1825
|
<h4>Steps</h4>
|
|
1780
1826
|
<div class="steps-list">${generateStepsHTML(test.steps)}</div>
|
|
@@ -1793,75 +1839,86 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1793
1839
|
</div>
|
|
1794
1840
|
</div>`;
|
|
1795
1841
|
})()}
|
|
1796
|
-
${
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1842
|
+
${
|
|
1843
|
+
test.stderr && test.stderr.length > 0
|
|
1844
|
+
? `<div class="console-output-section"><h4>Console Output (stderr)</h4><pre class="console-log stderr-log" style="background-color: #2d2d2d; color: indianred; padding: 1.25em; border-radius: 0.85em; line-height: 1.2;">${formatPlaywrightError(
|
|
1845
|
+
test.stderr.map((line) => sanitizeHTML(line)).join("\n")
|
|
1846
|
+
)}</pre></div>`
|
|
1847
|
+
: ""
|
|
1801
1848
|
}
|
|
1802
|
-
${
|
|
1803
|
-
|
|
1849
|
+
${
|
|
1850
|
+
test.screenshots && test.screenshots.length > 0
|
|
1851
|
+
? `
|
|
1804
1852
|
<div class="attachments-section">
|
|
1805
1853
|
<h4>Screenshots</h4>
|
|
1806
1854
|
<div class="attachments-grid">
|
|
1807
1855
|
${test.screenshots
|
|
1808
|
-
|
|
1809
|
-
|
|
1856
|
+
.map(
|
|
1857
|
+
(screenshot, index) => `
|
|
1810
1858
|
<div class="attachment-item">
|
|
1811
|
-
<img src="${fixPath(screenshot)}" alt="Screenshot ${
|
|
1859
|
+
<img src="${fixPath(screenshot)}" alt="Screenshot ${
|
|
1860
|
+
index + 1
|
|
1861
|
+
}">
|
|
1812
1862
|
<div class="attachment-info">
|
|
1813
1863
|
<div class="trace-actions">
|
|
1814
|
-
<a href="${fixPath(
|
|
1815
|
-
|
|
1864
|
+
<a href="${fixPath(
|
|
1865
|
+
screenshot
|
|
1866
|
+
)}" target="_blank" class="view-full">View Full Image</a>
|
|
1867
|
+
<a href="${fixPath(
|
|
1868
|
+
screenshot
|
|
1869
|
+
)}" target="_blank" download="screenshot-${Date.now()}-${index}.png">Download</a>
|
|
1816
1870
|
</div>
|
|
1817
1871
|
</div>
|
|
1818
1872
|
</div>
|
|
1819
1873
|
`
|
|
1820
|
-
|
|
1821
|
-
|
|
1874
|
+
)
|
|
1875
|
+
.join("")}
|
|
1822
1876
|
</div>
|
|
1823
1877
|
</div>
|
|
1824
1878
|
`
|
|
1825
|
-
|
|
1879
|
+
: ""
|
|
1826
1880
|
}
|
|
1827
|
-
${
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1881
|
+
${
|
|
1882
|
+
test.videoPath && test.videoPath.length > 0
|
|
1883
|
+
? `<div class="attachments-section"><h4>Videos</h4><div class="attachments-grid">${test.videoPath
|
|
1884
|
+
.map((videoUrl, index) => {
|
|
1885
|
+
const fixedVideoUrl = fixPath(videoUrl);
|
|
1886
|
+
const fileExtension = String(fixedVideoUrl)
|
|
1887
|
+
.split(".")
|
|
1888
|
+
.pop()
|
|
1889
|
+
.toLowerCase();
|
|
1890
|
+
const mimeType =
|
|
1891
|
+
{
|
|
1892
|
+
mp4: "video/mp4",
|
|
1893
|
+
webm: "video/webm",
|
|
1894
|
+
ogg: "video/ogg",
|
|
1895
|
+
mov: "video/quicktime",
|
|
1896
|
+
avi: "video/x-msvideo",
|
|
1897
|
+
}[fileExtension] || "video/mp4";
|
|
1898
|
+
return `<div class="attachment-item video-item">
|
|
1899
|
+
<video controls width="100%" height="auto" title="Video ${
|
|
1900
|
+
index + 1
|
|
1901
|
+
}">
|
|
1846
1902
|
<source src="${sanitizeHTML(
|
|
1847
|
-
|
|
1848
|
-
|
|
1903
|
+
fixedVideoUrl
|
|
1904
|
+
)}" type="${mimeType}">
|
|
1849
1905
|
Your browser does not support the video tag.
|
|
1850
1906
|
</video>
|
|
1851
1907
|
<div class="attachment-info">
|
|
1852
1908
|
<div class="trace-actions">
|
|
1853
1909
|
<a href="${sanitizeHTML(
|
|
1854
|
-
|
|
1855
|
-
|
|
1910
|
+
fixedVideoUrl
|
|
1911
|
+
)}" target="_blank" download="video-${Date.now()}-${index}.${fileExtension}">Download</a>
|
|
1856
1912
|
</div>
|
|
1857
1913
|
</div>
|
|
1858
1914
|
</div>`;
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1915
|
+
})
|
|
1916
|
+
.join("")}</div></div>`
|
|
1917
|
+
: ""
|
|
1862
1918
|
}
|
|
1863
|
-
${
|
|
1864
|
-
|
|
1919
|
+
${
|
|
1920
|
+
test.tracePath
|
|
1921
|
+
? `
|
|
1865
1922
|
<div class="attachments-section">
|
|
1866
1923
|
<h4>Trace Files</h4>
|
|
1867
1924
|
<div class="attachments-grid">
|
|
@@ -1869,70 +1926,72 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1869
1926
|
<div class="trace-preview">
|
|
1870
1927
|
<span class="trace-icon">📄</span>
|
|
1871
1928
|
<span class="trace-name">${sanitizeHTML(
|
|
1872
|
-
|
|
1873
|
-
|
|
1929
|
+
path.basename(test.tracePath)
|
|
1930
|
+
)}</span>
|
|
1874
1931
|
</div>
|
|
1875
1932
|
<div class="attachment-info">
|
|
1876
1933
|
<div class="trace-actions">
|
|
1877
1934
|
<a href="${sanitizeHTML(
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1935
|
+
fixPath(test.tracePath)
|
|
1936
|
+
)}" target="_blank" download="${sanitizeHTML(
|
|
1937
|
+
path.basename(test.tracePath)
|
|
1938
|
+
)}" class="download-trace">Download Trace</a>
|
|
1882
1939
|
</div>
|
|
1883
1940
|
</div>
|
|
1884
1941
|
</div>
|
|
1885
1942
|
</div>
|
|
1886
1943
|
</div>
|
|
1887
1944
|
`
|
|
1888
|
-
|
|
1945
|
+
: ""
|
|
1889
1946
|
}
|
|
1890
|
-
${
|
|
1891
|
-
|
|
1947
|
+
${
|
|
1948
|
+
test.attachments && test.attachments.length > 0
|
|
1949
|
+
? `
|
|
1892
1950
|
<div class="attachments-section">
|
|
1893
1951
|
<h4>Other Attachments</h4>
|
|
1894
1952
|
<div class="attachments-grid">
|
|
1895
1953
|
${test.attachments
|
|
1896
|
-
|
|
1897
|
-
|
|
1954
|
+
.map(
|
|
1955
|
+
(attachment) => `
|
|
1898
1956
|
<div class="attachment-item generic-attachment">
|
|
1899
1957
|
<div class="attachment-icon">${getAttachmentIcon(
|
|
1900
|
-
|
|
1901
|
-
|
|
1958
|
+
attachment.contentType
|
|
1959
|
+
)}</div>
|
|
1902
1960
|
<div class="attachment-caption">
|
|
1903
1961
|
<span class="attachment-name" title="${sanitizeHTML(
|
|
1904
|
-
|
|
1905
|
-
|
|
1962
|
+
attachment.name
|
|
1963
|
+
)}">${sanitizeHTML(attachment.name)}</span>
|
|
1906
1964
|
<span class="attachment-type">${sanitizeHTML(
|
|
1907
|
-
|
|
1908
|
-
|
|
1965
|
+
attachment.contentType
|
|
1966
|
+
)}</span>
|
|
1909
1967
|
</div>
|
|
1910
1968
|
<div class="attachment-info">
|
|
1911
1969
|
<div class="trace-actions">
|
|
1912
1970
|
<a href="${sanitizeHTML(
|
|
1913
|
-
|
|
1914
|
-
|
|
1971
|
+
fixPath(attachment.path)
|
|
1972
|
+
)}" target="_blank" class="view-full">View</a>
|
|
1915
1973
|
<a href="${sanitizeHTML(
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1974
|
+
fixPath(attachment.path)
|
|
1975
|
+
)}" target="_blank" download="${sanitizeHTML(
|
|
1976
|
+
attachment.name
|
|
1977
|
+
)}" class="download-trace">Download</a>
|
|
1920
1978
|
</div>
|
|
1921
1979
|
</div>
|
|
1922
1980
|
</div>
|
|
1923
1981
|
`
|
|
1924
|
-
|
|
1925
|
-
|
|
1982
|
+
)
|
|
1983
|
+
.join("")}
|
|
1926
1984
|
</div>
|
|
1927
1985
|
</div>
|
|
1928
1986
|
`
|
|
1929
|
-
|
|
1987
|
+
: ""
|
|
1930
1988
|
}
|
|
1931
|
-
${
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1989
|
+
${
|
|
1990
|
+
test.codeSnippet
|
|
1991
|
+
? `<div class="code-section"><h4>Code Snippet</h4><pre><code>${formatPlaywrightError(
|
|
1992
|
+
sanitizeHTML(test.codeSnippet)
|
|
1993
|
+
)}</code></pre></div>`
|
|
1994
|
+
: ""
|
|
1936
1995
|
}
|
|
1937
1996
|
</div>
|
|
1938
1997
|
</div>`;
|
|
@@ -2345,8 +2404,8 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2345
2404
|
<h1>Playwright Pulse Report</h1>
|
|
2346
2405
|
</div>
|
|
2347
2406
|
<div class="run-info"><strong>Run Date:</strong> ${formatDate(
|
|
2348
|
-
|
|
2349
|
-
|
|
2407
|
+
runSummary.timestamp
|
|
2408
|
+
)}<br><strong>Total Duration:</strong> ${formatDuration(
|
|
2350
2409
|
runSummary.duration
|
|
2351
2410
|
)}</div>
|
|
2352
2411
|
</header>
|
|
@@ -2358,35 +2417,40 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2358
2417
|
</div>
|
|
2359
2418
|
<div id="dashboard" class="tab-content active">
|
|
2360
2419
|
<div class="dashboard-grid">
|
|
2361
|
-
<div class="summary-card"><h3>Total Tests</h3><div class="value">${
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
<div class="summary-card status-
|
|
2368
|
-
|
|
2420
|
+
<div class="summary-card"><h3>Total Tests</h3><div class="value">${
|
|
2421
|
+
runSummary.totalTests
|
|
2422
|
+
}</div></div>
|
|
2423
|
+
<div class="summary-card status-passed"><h3>Passed</h3><div class="value">${
|
|
2424
|
+
runSummary.passed
|
|
2425
|
+
}</div><div class="trend-percentage">${passPercentage}%</div></div>
|
|
2426
|
+
<div class="summary-card status-failed"><h3>Failed</h3><div class="value">${
|
|
2427
|
+
runSummary.failed
|
|
2428
|
+
}</div><div class="trend-percentage">${failPercentage}%</div></div>
|
|
2429
|
+
<div class="summary-card status-skipped"><h3>Skipped</h3><div class="value">${
|
|
2430
|
+
runSummary.skipped || 0
|
|
2431
|
+
}</div><div class="trend-percentage">${skipPercentage}%</div></div>
|
|
2369
2432
|
<div class="summary-card"><h3>Avg. Test Time</h3><div class="value">${avgTestDuration}</div></div>
|
|
2370
2433
|
<div class="summary-card"><h3>Run Duration</h3><div class="value">${formatDuration(
|
|
2371
|
-
|
|
2372
|
-
|
|
2434
|
+
runSummary.duration
|
|
2435
|
+
)}</div></div>
|
|
2373
2436
|
</div>
|
|
2374
2437
|
<div class="dashboard-bottom-row">
|
|
2375
2438
|
<div style="display: grid; gap: 20px">
|
|
2376
2439
|
${generatePieChart(
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
${
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2440
|
+
[
|
|
2441
|
+
{ label: "Passed", value: runSummary.passed },
|
|
2442
|
+
{ label: "Failed", value: runSummary.failed },
|
|
2443
|
+
{ label: "Skipped", value: runSummary.skipped || 0 },
|
|
2444
|
+
],
|
|
2445
|
+
400,
|
|
2446
|
+
390
|
|
2447
|
+
)}
|
|
2448
|
+
${
|
|
2449
|
+
runSummary.environment &&
|
|
2450
|
+
Object.keys(runSummary.environment).length > 0
|
|
2451
|
+
? generateEnvironmentDashboard(runSummary.environment)
|
|
2452
|
+
: '<div class="no-data">Environment data not available.</div>'
|
|
2453
|
+
}
|
|
2390
2454
|
</div>
|
|
2391
2455
|
${generateSuitesWidget(suitesData)}
|
|
2392
2456
|
</div>
|
|
@@ -2396,17 +2460,17 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2396
2460
|
<input type="text" id="filter-name" placeholder="Filter by test name/path..." style="border-color: black; border-style: outset;">
|
|
2397
2461
|
<select id="filter-status"><option value="">All Statuses</option><option value="passed">Passed</option><option value="failed">Failed</option><option value="skipped">Skipped</option></select>
|
|
2398
2462
|
<select id="filter-browser"><option value="">All Browsers</option>${Array.from(
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2463
|
+
new Set(
|
|
2464
|
+
(results || []).map((test) => test.browser || "unknown")
|
|
2465
|
+
)
|
|
2466
|
+
)
|
|
2467
|
+
.map(
|
|
2468
|
+
(browser) =>
|
|
2469
|
+
`<option value="${sanitizeHTML(browser)}">${sanitizeHTML(
|
|
2470
|
+
browser
|
|
2471
|
+
)}</option>`
|
|
2472
|
+
)
|
|
2473
|
+
.join("")}</select>
|
|
2410
2474
|
<button id="expand-all-tests">Expand All</button> <button id="collapse-all-tests">Collapse All</button> <button id="clear-run-summary-filters" class="clear-filters-btn">Clear Filters</button>
|
|
2411
2475
|
</div>
|
|
2412
2476
|
<div class="test-cases-list">${generateTestCasesHTML()}</div>
|
|
@@ -2415,16 +2479,18 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2415
2479
|
<h2 class="tab-main-title">Execution Trends</h2>
|
|
2416
2480
|
<div class="trend-charts-row">
|
|
2417
2481
|
<div class="trend-chart"><h3 class="chart-title-header">Test Volume & Outcome Trends</h3>
|
|
2418
|
-
${
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2482
|
+
${
|
|
2483
|
+
trendData && trendData.overall && trendData.overall.length > 0
|
|
2484
|
+
? generateTestTrendsChart(trendData)
|
|
2485
|
+
: '<div class="no-data">Overall trend data not available for test counts.</div>'
|
|
2486
|
+
}
|
|
2422
2487
|
</div>
|
|
2423
2488
|
<div class="trend-chart"><h3 class="chart-title-header">Execution Duration Trends</h3>
|
|
2424
|
-
${
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2489
|
+
${
|
|
2490
|
+
trendData && trendData.overall && trendData.overall.length > 0
|
|
2491
|
+
? generateDurationTrendChart(trendData)
|
|
2492
|
+
: '<div class="no-data">Overall trend data not available for durations.</div>'
|
|
2493
|
+
}
|
|
2428
2494
|
</div>
|
|
2429
2495
|
</div>
|
|
2430
2496
|
<h2 class="tab-main-title">Test Distribution by Worker ${infoTooltip}</h2>
|
|
@@ -2434,12 +2500,13 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2434
2500
|
</div>
|
|
2435
2501
|
</div>
|
|
2436
2502
|
<h2 class="tab-main-title">Individual Test History</h2>
|
|
2437
|
-
${
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2503
|
+
${
|
|
2504
|
+
trendData &&
|
|
2505
|
+
trendData.testRuns &&
|
|
2506
|
+
Object.keys(trendData.testRuns).length > 0
|
|
2507
|
+
? generateTestHistoryContent(trendData)
|
|
2508
|
+
: '<div class="no-data">Individual test history data not available.</div>'
|
|
2509
|
+
}
|
|
2443
2510
|
</div>
|
|
2444
2511
|
<div id="ai-failure-analyzer" class="tab-content">
|
|
2445
2512
|
${generateAIFailureAnalyzerTab(results)}
|
|
@@ -2702,6 +2769,19 @@ function getAIFix(button) {
|
|
|
2702
2769
|
}
|
|
2703
2770
|
if (expandAllBtn) expandAllBtn.addEventListener('click', () => setAllTestRunDetailsVisibility('block', 'true'));
|
|
2704
2771
|
if (collapseAllBtn) collapseAllBtn.addEventListener('click', () => setAllTestRunDetailsVisibility('none', 'false'));
|
|
2772
|
+
// --- Annotation Link Handler ---
|
|
2773
|
+
document.querySelectorAll('a.annotation-link').forEach(link => {
|
|
2774
|
+
link.addEventListener('click', (e) => {
|
|
2775
|
+
e.preventDefault();
|
|
2776
|
+
const annotationId = link.dataset.annotation;
|
|
2777
|
+
if (annotationId) {
|
|
2778
|
+
const jiraUrl = prompt('Enter your JIRA/Ticket system base URL (e.g., https://your-company.atlassian.net/browse/):', 'https://your-company.atlassian.net/browse/');
|
|
2779
|
+
if (jiraUrl) {
|
|
2780
|
+
window.open(jiraUrl + annotationId, '_blank');
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
});
|
|
2784
|
+
});
|
|
2705
2785
|
// --- Intersection Observer for Lazy Loading ---
|
|
2706
2786
|
const lazyLoadElements = document.querySelectorAll('.lazy-load-chart');
|
|
2707
2787
|
if ('IntersectionObserver' in window) {
|
|
@@ -1850,43 +1850,53 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1850
1850
|
: ""
|
|
1851
1851
|
}<button class="copy-error-btn" onclick="copyErrorToClipboard(this)">Copy Error Prompt</button></div>`
|
|
1852
1852
|
: ""
|
|
1853
|
-
}${
|
|
1854
|
-
(
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1853
|
+
}${(() => {
|
|
1854
|
+
if (!step.attachments || step.attachments.length === 0)
|
|
1855
|
+
return "";
|
|
1856
|
+
return `<div class="attachments-section"><h4>Step Attachments</h4><div class="attachments-grid">${step.attachments
|
|
1857
|
+
.map((attachment) => {
|
|
1858
|
+
try {
|
|
1859
|
+
const attachmentPath = path.resolve(
|
|
1860
|
+
DEFAULT_OUTPUT_DIR,
|
|
1861
|
+
attachment.path
|
|
1862
|
+
);
|
|
1863
|
+
if (!fsExistsSync(attachmentPath)) {
|
|
1864
|
+
return `<div class="attachment-item error">Attachment not found: ${sanitizeHTML(
|
|
1865
|
+
attachment.name
|
|
1866
|
+
)}</div>`;
|
|
1867
|
+
}
|
|
1868
|
+
const attachmentBase64 =
|
|
1869
|
+
readFileSync(attachmentPath).toString("base64");
|
|
1870
|
+
const attachmentDataUri = `data:${attachment.contentType};base64,${attachmentBase64}`;
|
|
1871
|
+
return `<div class="attachment-item generic-attachment">
|
|
1872
|
+
<div class="attachment-icon">${getAttachmentIcon(
|
|
1873
|
+
attachment.contentType
|
|
1874
|
+
)}</div>
|
|
1872
1875
|
<div class="attachment-caption">
|
|
1873
|
-
<span class="attachment-name" title="${sanitizeHTML(
|
|
1874
|
-
|
|
1876
|
+
<span class="attachment-name" title="${sanitizeHTML(
|
|
1877
|
+
attachment.name
|
|
1878
|
+
)}">${sanitizeHTML(attachment.name)}</span>
|
|
1879
|
+
<span class="attachment-type">${sanitizeHTML(
|
|
1880
|
+
attachment.contentType
|
|
1881
|
+
)}</span>
|
|
1875
1882
|
</div>
|
|
1876
1883
|
<div class="attachment-info">
|
|
1877
1884
|
<div class="trace-actions">
|
|
1878
1885
|
<a href="#" data-href="${attachmentDataUri}" class="view-full lazy-load-attachment" target="_blank">View</a>
|
|
1879
|
-
<a href="#" data-href="${attachmentDataUri}" class="lazy-load-attachment" download="${sanitizeHTML(
|
|
1886
|
+
<a href="#" data-href="${attachmentDataUri}" class="lazy-load-attachment" download="${sanitizeHTML(
|
|
1887
|
+
attachment.name
|
|
1888
|
+
)}">Download</a>
|
|
1880
1889
|
</div>
|
|
1881
1890
|
</div>
|
|
1882
1891
|
</div>`;
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1892
|
+
} catch (e) {
|
|
1893
|
+
return `<div class="attachment-item error">Failed to load attachment: ${sanitizeHTML(
|
|
1894
|
+
attachment.name
|
|
1895
|
+
)}</div>`;
|
|
1896
|
+
}
|
|
1897
|
+
})
|
|
1898
|
+
.join("")}</div></div>`;
|
|
1899
|
+
})()}${
|
|
1890
1900
|
hasNestedSteps
|
|
1891
1901
|
? `<div class="nested-steps">${generateStepsHTML(
|
|
1892
1902
|
step.steps,
|
|
@@ -1903,7 +1913,9 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1903
1913
|
test.tags || []
|
|
1904
1914
|
)
|
|
1905
1915
|
.join(",")
|
|
1906
|
-
.toLowerCase()}" data-test-id="${sanitizeHTML(
|
|
1916
|
+
.toLowerCase()}" data-test-id="${sanitizeHTML(
|
|
1917
|
+
String(test.id || testIndex)
|
|
1918
|
+
)}">
|
|
1907
1919
|
<div class="test-case-header" role="button" aria-expanded="false"><div class="test-case-summary"><span class="status-badge ${getStatusClass(
|
|
1908
1920
|
test.status
|
|
1909
1921
|
)}">${String(
|
|
@@ -1927,18 +1939,66 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1927
1939
|
<p><strong>Full Path:</strong> ${sanitizeHTML(
|
|
1928
1940
|
test.name
|
|
1929
1941
|
)}</p>
|
|
1942
|
+
${
|
|
1943
|
+
test.annotations && test.annotations.length > 0
|
|
1944
|
+
? `<div class="annotations-section" style="margin: 12px 0; padding: 12px; background-color: rgba(139, 92, 246, 0.1); border: 1px solid rgba(139, 92, 246, 0.3); border-left: 4px solid #8b5cf6; border-radius: 4px;">
|
|
1945
|
+
<h4 style="margin-top: 0; margin-bottom: 10px; color: #8b5cf6; font-size: 1.1em;">📌 Annotations</h4>
|
|
1946
|
+
${test.annotations
|
|
1947
|
+
.map((annotation, idx) => {
|
|
1948
|
+
const isIssueOrBug =
|
|
1949
|
+
annotation.type === "issue" ||
|
|
1950
|
+
annotation.type === "bug";
|
|
1951
|
+
const descriptionText =
|
|
1952
|
+
annotation.description || "";
|
|
1953
|
+
const typeLabel = sanitizeHTML(
|
|
1954
|
+
annotation.type
|
|
1955
|
+
);
|
|
1956
|
+
const descriptionHtml =
|
|
1957
|
+
isIssueOrBug &&
|
|
1958
|
+
descriptionText.match(/^[A-Z]+-\d+$/)
|
|
1959
|
+
? `<a href="#" class="annotation-link" data-annotation="${sanitizeHTML(
|
|
1960
|
+
descriptionText
|
|
1961
|
+
)}" style="color: #3b82f6; text-decoration: underline; cursor: pointer;">${sanitizeHTML(
|
|
1962
|
+
descriptionText
|
|
1963
|
+
)}</a>`
|
|
1964
|
+
: sanitizeHTML(descriptionText);
|
|
1965
|
+
const locationText = annotation.location
|
|
1966
|
+
? `<div style="font-size: 0.85em; color: #6b7280; margin-top: 4px;">Location: ${sanitizeHTML(
|
|
1967
|
+
annotation.location.file
|
|
1968
|
+
)}:${annotation.location.line}:${
|
|
1969
|
+
annotation.location.column
|
|
1970
|
+
}</div>`
|
|
1971
|
+
: "";
|
|
1972
|
+
return `<div style="margin-bottom: ${
|
|
1973
|
+
idx < test.annotations.length - 1
|
|
1974
|
+
? "10px"
|
|
1975
|
+
: "0"
|
|
1976
|
+
};">
|
|
1977
|
+
<strong style="color: #8b5cf6;">Type:</strong> <span style="background-color: rgba(139, 92, 246, 0.2); padding: 2px 8px; border-radius: 4px; font-size: 0.9em;">${typeLabel}</span>
|
|
1978
|
+
${
|
|
1979
|
+
descriptionText
|
|
1980
|
+
? `<br><strong style="color: #8b5cf6;">Description:</strong> ${descriptionHtml}`
|
|
1981
|
+
: ""
|
|
1982
|
+
}
|
|
1983
|
+
${locationText}
|
|
1984
|
+
</div>`;
|
|
1985
|
+
})
|
|
1986
|
+
.join("")}
|
|
1987
|
+
</div>`
|
|
1988
|
+
: ""
|
|
1989
|
+
}
|
|
1930
1990
|
<p><strong>Test run Worker ID:</strong> ${sanitizeHTML(
|
|
1931
1991
|
test.workerId
|
|
1932
1992
|
)} [<strong>Total No. of Workers:</strong> ${sanitizeHTML(
|
|
1933
1993
|
test.totalWorkers
|
|
1934
1994
|
)}]</p>
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1995
|
+
${
|
|
1996
|
+
test.errorMessage
|
|
1997
|
+
? `<div class="test-error-summary">${formatPlaywrightError(
|
|
1998
|
+
test.errorMessage
|
|
1999
|
+
)}<button class="copy-error-btn" onclick="copyErrorToClipboard(this)">Copy Error Prompt</button></div>`
|
|
2000
|
+
: ""
|
|
2001
|
+
}
|
|
1942
2002
|
${
|
|
1943
2003
|
test.snippet
|
|
1944
2004
|
? `<div class="code-section"><h4>Error Snippet</h4><pre><code>${formatPlaywrightError(
|
|
@@ -1970,7 +2030,9 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1970
2030
|
${
|
|
1971
2031
|
test.stderr && test.stderr.length > 0
|
|
1972
2032
|
? (() => {
|
|
1973
|
-
const logId = `stderr-log-${
|
|
2033
|
+
const logId = `stderr-log-${
|
|
2034
|
+
test.id || testIndex
|
|
2035
|
+
}`;
|
|
1974
2036
|
return `<div class="console-output-section"><h4>Console Output (stderr)</h4><pre id="${logId}" class="console-log stderr-log">${test.stderr
|
|
1975
2037
|
.map((line) => sanitizeHTML(line))
|
|
1976
2038
|
.join("\\n")}</pre></div>`;
|
|
@@ -2384,7 +2446,7 @@ aspect-ratio: 16 / 9;
|
|
|
2384
2446
|
@media (max-width: 992px) { .dashboard-bottom-row { grid-template-columns: 1fr; } .pie-chart-wrapper div[id^="pieChart-"] { max-width: 350px; margin: 0 auto; } .filters input { min-width: 180px; } .filters select { min-width: 150px; } }
|
|
2385
2447
|
@media (max-width: 768px) { body { font-size: 15px; } .container { margin: 10px; padding: 20px; } .header { flex-direction: column; align-items: flex-start; gap: 15px; } .header h1 { font-size: 1.6em; } .run-info { text-align: left; font-size:0.9em; } .tabs { margin-bottom: 25px;} .tab-button { padding: 12px 20px; font-size: 1.05em;} .dashboard-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 18px;} .summary-card .value {font-size: 2em;} .summary-card h3 {font-size: 0.95em;} .filters { flex-direction: column; padding: 18px; gap: 12px;} .filters input, .filters select, .filters button {width: 100%; box-sizing: border-box;} .test-case-header { flex-direction: column; align-items: flex-start; gap: 10px; padding: 14px; } .test-case-summary {gap: 10px;} .test-case-title {font-size: 1.05em;} .test-case-meta { flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 8px;} .attachments-grid {grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 18px;} .test-history-grid {grid-template-columns: 1fr;} .pie-chart-wrapper {min-height: auto;} .ai-failure-cards-grid { grid-template-columns: 1fr; } .ai-analyzer-stats { flex-direction: column; gap: 15px; text-align: center; } .failure-header { flex-direction: column; align-items: stretch; gap: 15px; } .failure-main-info { text-align: center; } .failure-meta { justify-content: center; } .compact-ai-btn { justify-content: center; padding: 12px 20px; } }
|
|
2386
2448
|
@media (max-width: 480px) { body {font-size: 14px;} .container {padding: 15px;} .header h1 {font-size: 1.4em;} #report-logo { height: 35px; width: 45px; } .tab-button {padding: 10px 15px; font-size: 1em;} .summary-card .value {font-size: 1.8em;} .attachments-grid {grid-template-columns: 1fr;} .step-item {padding-left: calc(var(--depth, 0) * 18px);} .test-case-content, .step-details {padding: 15px;} .trend-charts-row {gap: 20px;} .trend-chart {padding: 20px;} .stat-item .stat-number { font-size: 1.5em; } .failure-header { padding: 15px; } .failure-error-preview, .full-error-details { padding-left: 15px; padding-right: 15px; } }
|
|
2387
|
-
.trace-actions a { text-decoration: none;
|
|
2449
|
+
.trace-actions a { text-decoration: none; font-weight: 500; font-size: 0.9em; }
|
|
2388
2450
|
.generic-attachment { text-align: center; padding: 1rem; justify-content: center; }
|
|
2389
2451
|
.attachment-icon { font-size: 2.5rem; display: block; margin-bottom: 0.75rem; }
|
|
2390
2452
|
.attachment-caption { display: flex; flex-direction: column; align-items: center; justify-content: center; flex-grow: 1; }
|
|
@@ -2826,6 +2888,18 @@ aspect-ratio: 16 / 9;
|
|
|
2826
2888
|
}
|
|
2827
2889
|
return;
|
|
2828
2890
|
}
|
|
2891
|
+
const annotationLink = e.target.closest('a.annotation-link');
|
|
2892
|
+
if (annotationLink) {
|
|
2893
|
+
e.preventDefault();
|
|
2894
|
+
const annotationId = annotationLink.dataset.annotation;
|
|
2895
|
+
if (annotationId) {
|
|
2896
|
+
const jiraUrl = prompt('Enter your JIRA/Ticket system base URL (e.g., https://your-company.atlassian.net/browse/):', 'https://your-company.atlassian.net/browse/');
|
|
2897
|
+
if (jiraUrl) {
|
|
2898
|
+
window.open(jiraUrl + annotationId, '_blank');
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
return;
|
|
2902
|
+
}
|
|
2829
2903
|
const img = e.target.closest('img.lazy-load-image');
|
|
2830
2904
|
if (img && img.dataset && img.dataset.src) {
|
|
2831
2905
|
if (e.preventDefault) e.preventDefault();
|
|
@@ -2856,9 +2930,45 @@ aspect-ratio: 16 / 9;
|
|
|
2856
2930
|
const a = e.target.closest('a.lazy-load-attachment');
|
|
2857
2931
|
if (a && a.dataset && a.dataset.href) {
|
|
2858
2932
|
e.preventDefault();
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
a.
|
|
2933
|
+
|
|
2934
|
+
// Special handling for view-full links to avoid about:blank issue
|
|
2935
|
+
if (a.classList.contains('view-full')) {
|
|
2936
|
+
// Extract the data from the data URI
|
|
2937
|
+
const dataUri = a.dataset.href;
|
|
2938
|
+
const [header, base64Data] = dataUri.split(',');
|
|
2939
|
+
const mimeType = header.match(/data:([^;]+)/)[1];
|
|
2940
|
+
|
|
2941
|
+
try {
|
|
2942
|
+
// Convert base64 to blob
|
|
2943
|
+
const byteCharacters = atob(base64Data);
|
|
2944
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
2945
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
2946
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
2947
|
+
}
|
|
2948
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
2949
|
+
const blob = new Blob([byteArray], { type: mimeType });
|
|
2950
|
+
|
|
2951
|
+
// Create a URL and open it
|
|
2952
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
2953
|
+
const newWindow = window.open(blobUrl, '_blank');
|
|
2954
|
+
|
|
2955
|
+
// Clean up the URL after a delay
|
|
2956
|
+
setTimeout(() => {
|
|
2957
|
+
URL.revokeObjectURL(blobUrl);
|
|
2958
|
+
}, 1000);
|
|
2959
|
+
} catch (error) {
|
|
2960
|
+
console.error('Failed to open attachment:', error);
|
|
2961
|
+
// Fallback to original method
|
|
2962
|
+
a.href = a.dataset.href;
|
|
2963
|
+
a.removeAttribute('data-href');
|
|
2964
|
+
a.click();
|
|
2965
|
+
}
|
|
2966
|
+
} else {
|
|
2967
|
+
// For download links, use the original method
|
|
2968
|
+
a.href = a.dataset.href;
|
|
2969
|
+
a.removeAttribute('data-href');
|
|
2970
|
+
a.click();
|
|
2971
|
+
}
|
|
2862
2972
|
return;
|
|
2863
2973
|
}
|
|
2864
2974
|
});
|
package/scripts/sendReport.mjs
CHANGED
|
@@ -289,7 +289,7 @@ const sendEmail = async (credentials) => {
|
|
|
289
289
|
}
|
|
290
290
|
};
|
|
291
291
|
|
|
292
|
-
async function fetchCredentials(retries =
|
|
292
|
+
async function fetchCredentials(retries = 10) {
|
|
293
293
|
// Ensure fetch is initialized from the dynamic import before calling this
|
|
294
294
|
if (!fetch) {
|
|
295
295
|
try {
|
|
@@ -324,7 +324,7 @@ async function fetchCredentials(retries = 6) {
|
|
|
324
324
|
});
|
|
325
325
|
|
|
326
326
|
const fetchPromise = fetch(
|
|
327
|
-
"https://
|
|
327
|
+
"https://get-credentials.netlify.app/api/getcredentials",
|
|
328
328
|
{
|
|
329
329
|
method: "GET",
|
|
330
330
|
headers: {
|