playwright-slack-report-burak 3.0.41 → 3.1.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/dist/src/ResultsParser.js +166 -11
- package/package.json +3 -2
|
@@ -18,15 +18,47 @@ try {
|
|
|
18
18
|
AdmZip = null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// CONSTANTS
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
// Directory paths
|
|
21
26
|
const SUMMARIES_DIR = path.join('./', 'playwright-artifacts');
|
|
22
27
|
const PLAYWRIGHT_REPORT_DIR = path.join('./', 'playwright-report');
|
|
23
|
-
const FINAL_OUTPUT_DIR = path.join('./', 'playwright-report');
|
|
24
28
|
|
|
25
29
|
// Environment variables
|
|
26
30
|
const IS_CI = !!process.env.CI;
|
|
27
31
|
const GITHUB_TOKEN = process.env.SAFETYWINGTEST_GITHUB_TOKEN;
|
|
28
32
|
const GITHUB_REPOSITORY = process.env.GITHUB_REPOSITORY;
|
|
29
33
|
const GITHUB_RUN_ID = process.env.GITHUB_RUN_ID || 'local';
|
|
34
|
+
const GITHUB_RUN_ATTEMPT = process.env.GITHUB_RUN_ATTEMPT || '1';
|
|
35
|
+
|
|
36
|
+
// Google Cloud Storage configuration
|
|
37
|
+
const GCS_BUCKET_NAME = 'sw-automation-tests';
|
|
38
|
+
const GCS_SERVICE_ACCOUNT_KEY = process.env.GCS_SERVICE_ACCOUNT_KEY;
|
|
39
|
+
const GCS_REPORTS_FOLDER_PREFIX = 'playwright-reports';
|
|
40
|
+
const GCS_UPLOAD_API_BASE_URL = 'https://storage.googleapis.com/upload/storage/v1';
|
|
41
|
+
|
|
42
|
+
// Artifact patterns
|
|
43
|
+
const ARTIFACT_NAME_PATTERNS = [
|
|
44
|
+
'html-report',
|
|
45
|
+
'blob-report-node',
|
|
46
|
+
'test-results',
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// GitHub API endpoints (for artifact fetching)
|
|
50
|
+
const GITHUB_API_BASE_URL = 'https://api.github.com';
|
|
51
|
+
const GITHUB_API_ARTIFACTS_ENDPOINT = '/actions/artifacts';
|
|
52
|
+
|
|
53
|
+
// Helper functions for building GitHub API URLs and patterns
|
|
54
|
+
const getGitHubArtifactsApiUrl = () => `${GITHUB_API_BASE_URL}/repos/${GITHUB_REPOSITORY}${GITHUB_API_ARTIFACTS_ENDPOINT}`;
|
|
55
|
+
const getArtifactNamePatterns = (shardIndex) => ARTIFACT_NAME_PATTERNS.map(pattern => `${pattern}-${shardIndex}`);
|
|
56
|
+
|
|
57
|
+
// Helper functions for report paths
|
|
58
|
+
const getRunFolder = () => `${GCS_REPORTS_FOLDER_PREFIX}/${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}`;
|
|
59
|
+
const getGcsObjectPath = (relativePath) => `${getRunFolder()}/${relativePath}`;
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
30
62
|
|
|
31
63
|
class ResultsParser {
|
|
32
64
|
result;
|
|
@@ -145,7 +177,8 @@ class ResultsParser {
|
|
|
145
177
|
}
|
|
146
178
|
}
|
|
147
179
|
|
|
148
|
-
this.mergeReports();
|
|
180
|
+
await this.mergeReports();
|
|
181
|
+
await this.pushReportsToGCS();
|
|
149
182
|
}
|
|
150
183
|
return summary;
|
|
151
184
|
}
|
|
@@ -379,14 +412,8 @@ class ResultsParser {
|
|
|
379
412
|
}
|
|
380
413
|
|
|
381
414
|
async fetchArtifactFromGitHubActions(i, file, filePath) {
|
|
382
|
-
const githubApiUrl =
|
|
383
|
-
|
|
384
|
-
// Try multiple artifact naming patterns
|
|
385
|
-
const artifactNamePatterns = [
|
|
386
|
-
`html-report-${i}`,
|
|
387
|
-
`blob-report-node-${i}`,
|
|
388
|
-
`test-results-${i}`,
|
|
389
|
-
];
|
|
415
|
+
const githubApiUrl = getGitHubArtifactsApiUrl();
|
|
416
|
+
const artifactNamePatterns = getArtifactNamePatterns(i);
|
|
390
417
|
|
|
391
418
|
while (true) {
|
|
392
419
|
try {
|
|
@@ -454,7 +481,7 @@ class ResultsParser {
|
|
|
454
481
|
}
|
|
455
482
|
}
|
|
456
483
|
}
|
|
457
|
-
mergeReports() {
|
|
484
|
+
async mergeReports() {
|
|
458
485
|
let breakMergeWaiter = false;
|
|
459
486
|
try {
|
|
460
487
|
|
|
@@ -564,5 +591,133 @@ class ResultsParser {
|
|
|
564
591
|
}
|
|
565
592
|
}
|
|
566
593
|
}
|
|
594
|
+
|
|
595
|
+
async getGcsAccessToken() {
|
|
596
|
+
if (!GCS_SERVICE_ACCOUNT_KEY) {
|
|
597
|
+
throw new Error('GCS_SERVICE_ACCOUNT_KEY environment variable is required');
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
let serviceAccount;
|
|
601
|
+
try {
|
|
602
|
+
serviceAccount = JSON.parse(GCS_SERVICE_ACCOUNT_KEY);
|
|
603
|
+
} catch (e) {
|
|
604
|
+
// Try reading from file path
|
|
605
|
+
if (fs.existsSync(GCS_SERVICE_ACCOUNT_KEY)) {
|
|
606
|
+
serviceAccount = JSON.parse(fs.readFileSync(GCS_SERVICE_ACCOUNT_KEY, 'utf-8'));
|
|
607
|
+
} else {
|
|
608
|
+
throw new Error('Invalid GCS_SERVICE_ACCOUNT_KEY: must be JSON string or file path');
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Request OAuth2 token from Google
|
|
613
|
+
const jwt = require('jsonwebtoken');
|
|
614
|
+
const now = Math.floor(Date.now() / 1000);
|
|
615
|
+
const token = jwt.sign(
|
|
616
|
+
{
|
|
617
|
+
iss: serviceAccount.client_email,
|
|
618
|
+
scope: 'https://www.googleapis.com/auth/devstorage.full_control',
|
|
619
|
+
aud: 'https://oauth2.googleapis.com/token',
|
|
620
|
+
exp: now + 3600,
|
|
621
|
+
iat: now
|
|
622
|
+
},
|
|
623
|
+
serviceAccount.private_key,
|
|
624
|
+
{ algorithm: 'RS256' }
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
const tokenResponse = await axios.post('https://oauth2.googleapis.com/token', {
|
|
628
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
629
|
+
assertion: token
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
return tokenResponse.data.access_token;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
async pushReportsToGCS() {
|
|
636
|
+
// Only push in CI environment with required configuration
|
|
637
|
+
if (!IS_CI || !GCS_SERVICE_ACCOUNT_KEY) {
|
|
638
|
+
console.log('Skipping GCS push: not in CI or missing GCS_SERVICE_ACCOUNT_KEY');
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
try {
|
|
643
|
+
console.log(`Uploading playwright-report files to GCS bucket: ${GCS_BUCKET_NAME} in folder: ${getRunFolder()}`);
|
|
644
|
+
|
|
645
|
+
// Get access token
|
|
646
|
+
const accessToken = await this.getGcsAccessToken();
|
|
647
|
+
|
|
648
|
+
// Collect all files from playwright-report directory
|
|
649
|
+
const files = this.getAllFilesRecursive(PLAYWRIGHT_REPORT_DIR);
|
|
650
|
+
|
|
651
|
+
// Normalize the base directory path for path replacement
|
|
652
|
+
const baseDirPath = path.resolve(PLAYWRIGHT_REPORT_DIR);
|
|
653
|
+
|
|
654
|
+
// Upload all files to GCS
|
|
655
|
+
for (const file of files) {
|
|
656
|
+
// Get relative path from playwright-report directory
|
|
657
|
+
const relativePath = path.relative(baseDirPath, file).replace(/\\/g, '/');
|
|
658
|
+
const objectPath = getGcsObjectPath(relativePath);
|
|
659
|
+
const content = fs.readFileSync(file);
|
|
660
|
+
|
|
661
|
+
// Upload file to GCS
|
|
662
|
+
const uploadUrl = `${GCS_UPLOAD_API_BASE_URL}/b/${GCS_BUCKET_NAME}/o?uploadType=media&name=${encodeURIComponent(objectPath)}`;
|
|
663
|
+
|
|
664
|
+
await axios.post(uploadUrl, content, {
|
|
665
|
+
headers: {
|
|
666
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
667
|
+
'Content-Type': this.getContentType(file)
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
console.log(`Uploaded ${objectPath} to GCS`);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
console.log(`Successfully uploaded ${files.length} files to GCS bucket: ${GCS_BUCKET_NAME} in folder: ${getRunFolder()}`);
|
|
675
|
+
} catch (error) {
|
|
676
|
+
console.warn('Warning: Failed to push reports to GCS. This may not affect the overall process.', error.message);
|
|
677
|
+
if (error.response) {
|
|
678
|
+
console.warn('GCS API error:', error.response.data);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
getContentType(filePath) {
|
|
684
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
685
|
+
const contentTypes = {
|
|
686
|
+
'.html': 'text/html',
|
|
687
|
+
'.css': 'text/css',
|
|
688
|
+
'.js': 'application/javascript',
|
|
689
|
+
'.json': 'application/json',
|
|
690
|
+
'.png': 'image/png',
|
|
691
|
+
'.jpg': 'image/jpeg',
|
|
692
|
+
'.jpeg': 'image/jpeg',
|
|
693
|
+
'.gif': 'image/gif',
|
|
694
|
+
'.svg': 'image/svg+xml',
|
|
695
|
+
'.zip': 'application/zip',
|
|
696
|
+
'.txt': 'text/plain',
|
|
697
|
+
'.xml': 'application/xml'
|
|
698
|
+
};
|
|
699
|
+
return contentTypes[ext] || 'application/octet-stream';
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
getAllFilesRecursive(dir, fileList = []) {
|
|
703
|
+
if (!fs.existsSync(dir)) {
|
|
704
|
+
return fileList;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const files = fs.readdirSync(dir);
|
|
708
|
+
|
|
709
|
+
for (const file of files) {
|
|
710
|
+
const filePath = path.join(dir, file);
|
|
711
|
+
const stat = fs.statSync(filePath);
|
|
712
|
+
|
|
713
|
+
if (stat.isDirectory()) {
|
|
714
|
+
this.getAllFilesRecursive(filePath, fileList);
|
|
715
|
+
} else {
|
|
716
|
+
fileList.push(filePath);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
return fileList;
|
|
721
|
+
}
|
|
567
722
|
}
|
|
568
723
|
exports.default = ResultsParser;
|
package/package.json
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
"@slack/webhook": "^6.1.0",
|
|
5
5
|
"https-proxy-agent": "^7.0.1",
|
|
6
6
|
"adm-zip": "^0.5.10",
|
|
7
|
-
"axios": "^1.0.0"
|
|
7
|
+
"axios": "^1.0.0",
|
|
8
|
+
"jsonwebtoken": "^9.0.0"
|
|
8
9
|
},
|
|
9
10
|
"devDependencies": {
|
|
10
11
|
"@playwright/test": "^1.23.3",
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"lint-fix": "npx eslint . --ext .ts --fix"
|
|
33
34
|
},
|
|
34
35
|
"name": "playwright-slack-report-burak",
|
|
35
|
-
"version": "3.0
|
|
36
|
+
"version": "3.1.0",
|
|
36
37
|
"main": "index.js",
|
|
37
38
|
"types": "dist/index.d.ts",
|
|
38
39
|
"author": "Burak B. <burak.boluk@hotmail.com>",
|