@azure/playwright 1.1.3 → 1.1.4
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/browser/common/constants.d.ts +1 -4
- package/dist/browser/common/constants.d.ts.map +1 -1
- package/dist/browser/common/constants.js +1 -4
- package/dist/browser/common/constants.js.map +1 -1
- package/dist/browser/common/types.d.ts +0 -4
- package/dist/browser/common/types.d.ts.map +1 -1
- package/dist/browser/common/types.js.map +1 -1
- package/dist/browser/reporter/playwrightReporter.d.ts +0 -1
- package/dist/browser/reporter/playwrightReporter.d.ts.map +1 -1
- package/dist/browser/reporter/playwrightReporter.js +3 -17
- package/dist/browser/reporter/playwrightReporter.js.map +1 -1
- package/dist/browser/utils/PlaywrightServiceClient.d.ts +1 -2
- package/dist/browser/utils/PlaywrightServiceClient.d.ts.map +1 -1
- package/dist/browser/utils/PlaywrightServiceClient.js +1 -26
- package/dist/browser/utils/PlaywrightServiceClient.js.map +1 -1
- package/dist/browser/utils/utils.d.ts +2 -3
- package/dist/browser/utils/utils.d.ts.map +1 -1
- package/dist/browser/utils/utils.js +8 -21
- package/dist/browser/utils/utils.js.map +1 -1
- package/dist/commonjs/common/constants.d.ts +1 -4
- package/dist/commonjs/common/constants.d.ts.map +1 -1
- package/dist/commonjs/common/constants.js +2 -5
- package/dist/commonjs/common/constants.js.map +1 -1
- package/dist/commonjs/common/types.d.ts +0 -4
- package/dist/commonjs/common/types.d.ts.map +1 -1
- package/dist/commonjs/common/types.js.map +1 -1
- package/dist/commonjs/reporter/playwrightReporter.d.ts +0 -1
- package/dist/commonjs/reporter/playwrightReporter.d.ts.map +1 -1
- package/dist/commonjs/reporter/playwrightReporter.js +2 -16
- package/dist/commonjs/reporter/playwrightReporter.js.map +1 -1
- package/dist/commonjs/tsdoc-metadata.json +1 -1
- package/dist/commonjs/utils/PlaywrightServiceClient.d.ts +1 -2
- package/dist/commonjs/utils/PlaywrightServiceClient.d.ts.map +1 -1
- package/dist/commonjs/utils/PlaywrightServiceClient.js +0 -25
- package/dist/commonjs/utils/PlaywrightServiceClient.js.map +1 -1
- package/dist/commonjs/utils/utils.d.ts +2 -3
- package/dist/commonjs/utils/utils.d.ts.map +1 -1
- package/dist/commonjs/utils/utils.js +8 -22
- package/dist/commonjs/utils/utils.js.map +1 -1
- package/dist/esm/common/constants.d.ts +1 -4
- package/dist/esm/common/constants.d.ts.map +1 -1
- package/dist/esm/common/constants.js +92 -96
- package/dist/esm/common/constants.js.map +1 -7
- package/dist/esm/common/customerConfig.js +11 -11
- package/dist/esm/common/customerConfig.js.map +1 -7
- package/dist/esm/common/entraIdAccessToken.js +77 -85
- package/dist/esm/common/entraIdAccessToken.js.map +1 -7
- package/dist/esm/common/environmentVariables.js +19 -19
- package/dist/esm/common/environmentVariables.js.map +1 -7
- package/dist/esm/common/executor.js +58 -51
- package/dist/esm/common/executor.js.map +1 -7
- package/dist/esm/common/httpService.js +29 -34
- package/dist/esm/common/httpService.js.map +1 -7
- package/dist/esm/common/logger.js +4 -4
- package/dist/esm/common/logger.js.map +1 -7
- package/dist/esm/common/messages.js +166 -166
- package/dist/esm/common/messages.js.map +1 -7
- package/dist/esm/common/playwrightServiceConfig.js +91 -91
- package/dist/esm/common/playwrightServiceConfig.js.map +1 -7
- package/dist/esm/common/state.js +7 -7
- package/dist/esm/common/state.js.map +1 -7
- package/dist/esm/common/types.d.ts +0 -4
- package/dist/esm/common/types.d.ts.map +1 -1
- package/dist/esm/common/types.js +4 -0
- package/dist/esm/common/types.js.map +1 -7
- package/dist/esm/core/global/playwright-service-global-setup.js +17 -17
- package/dist/esm/core/global/playwright-service-global-setup.js.map +1 -7
- package/dist/esm/core/global/playwright-service-global-teardown.js +13 -16
- package/dist/esm/core/global/playwright-service-global-teardown.js.map +1 -7
- package/dist/esm/core/initializePlaywrightServiceTestRun.js +21 -13
- package/dist/esm/core/initializePlaywrightServiceTestRun.js.map +1 -7
- package/dist/esm/core/playwrightService.js +200 -149
- package/dist/esm/core/playwrightService.js.map +1 -7
- package/dist/esm/core/playwrightServiceEntra.js +42 -44
- package/dist/esm/core/playwrightServiceEntra.js.map +1 -7
- package/dist/esm/core/playwrightServiceUtils.js +8 -6
- package/dist/esm/core/playwrightServiceUtils.js.map +1 -7
- package/dist/esm/index.js +9 -7
- package/dist/esm/index.js.map +1 -7
- package/dist/esm/reporter/index.js +11 -4
- package/dist/esm/reporter/index.js.map +1 -7
- package/dist/esm/reporter/playwrightReporter.d.ts +0 -1
- package/dist/esm/reporter/playwrightReporter.d.ts.map +1 -1
- package/dist/esm/reporter/playwrightReporter.js +193 -202
- package/dist/esm/reporter/playwrightReporter.js.map +1 -7
- package/dist/esm/utils/PlaywrightServiceClient.d.ts +1 -2
- package/dist/esm/utils/PlaywrightServiceClient.d.ts.map +1 -1
- package/dist/esm/utils/PlaywrightServiceClient.js +61 -121
- package/dist/esm/utils/PlaywrightServiceClient.js.map +1 -7
- package/dist/esm/utils/cIInfoProvider.js +71 -58
- package/dist/esm/utils/cIInfoProvider.js.map +1 -7
- package/dist/esm/utils/getPackageVersion.js +17 -12
- package/dist/esm/utils/getPackageVersion.js.map +1 -7
- package/dist/esm/utils/getPlaywrightVersion.js +13 -15
- package/dist/esm/utils/getPlaywrightVersion.js.map +1 -7
- package/dist/esm/utils/packageManager.js +37 -37
- package/dist/esm/utils/packageManager.js.map +1 -7
- package/dist/esm/utils/parseJwt.js +14 -15
- package/dist/esm/utils/parseJwt.js.map +1 -7
- package/dist/esm/utils/playwrightReporterStorageManager.js +333 -358
- package/dist/esm/utils/playwrightReporterStorageManager.js.map +1 -7
- package/dist/esm/utils/utils.d.ts +2 -3
- package/dist/esm/utils/utils.d.ts.map +1 -1
- package/dist/esm/utils/utils.js +338 -380
- package/dist/esm/utils/utils.js.map +1 -7
- package/dist/react-native/common/constants.d.ts +1 -4
- package/dist/react-native/common/constants.d.ts.map +1 -1
- package/dist/react-native/common/constants.js +1 -4
- package/dist/react-native/common/constants.js.map +1 -1
- package/dist/react-native/common/types.d.ts +0 -4
- package/dist/react-native/common/types.d.ts.map +1 -1
- package/dist/react-native/common/types.js.map +1 -1
- package/dist/react-native/reporter/playwrightReporter.d.ts +0 -1
- package/dist/react-native/reporter/playwrightReporter.d.ts.map +1 -1
- package/dist/react-native/reporter/playwrightReporter.js +3 -17
- package/dist/react-native/reporter/playwrightReporter.js.map +1 -1
- package/dist/react-native/utils/PlaywrightServiceClient.d.ts +1 -2
- package/dist/react-native/utils/PlaywrightServiceClient.d.ts.map +1 -1
- package/dist/react-native/utils/PlaywrightServiceClient.js +1 -26
- package/dist/react-native/utils/PlaywrightServiceClient.js.map +1 -1
- package/dist/react-native/utils/utils.d.ts +2 -3
- package/dist/react-native/utils/utils.d.ts.map +1 -1
- package/dist/react-native/utils/utils.js +8 -21
- package/dist/react-native/utils/utils.js.map +1 -1
- package/package.json +6 -6
package/dist/esm/utils/utils.js
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
ServiceEnvironmentVariable,
|
|
5
|
-
RunConfigConstants,
|
|
6
|
-
GitHubActionsConstants,
|
|
7
|
-
BrowserSessionSourceType,
|
|
8
|
-
UrlConstants,
|
|
9
|
-
UploadConstants
|
|
10
|
-
} from "../common/constants.js";
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import { Constants, InternalEnvironmentVariables, ServiceEnvironmentVariable, RunConfigConstants, GitHubActionsConstants, BrowserSessionSourceType, UrlConstants, UploadConstants, } from "../common/constants.js";
|
|
11
4
|
import { ServiceErrorMessageConstants } from "../common/messages.js";
|
|
12
5
|
import { coreLogger } from "../common/logger.js";
|
|
13
6
|
import process from "node:process";
|
|
@@ -20,415 +13,380 @@ import { exec } from "child_process";
|
|
|
20
13
|
import { getPackageVersionFromFolder } from "./getPackageVersion.js";
|
|
21
14
|
import { readdirSync, statSync } from "fs";
|
|
22
15
|
import { join, relative } from "path";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.error(error.formatWithErrorDetails(errorDetails));
|
|
40
|
-
} else {
|
|
41
|
-
console.error(error.message);
|
|
42
|
-
}
|
|
43
|
-
process.exit(1);
|
|
44
|
-
};
|
|
45
|
-
const throwErrorWithFailureMessage = (error, errorDetails) => {
|
|
46
|
-
console.log();
|
|
47
|
-
const finalMessage = error.formatWithErrorDetails && errorDetails ? error.formatWithErrorDetails(errorDetails) : error.message;
|
|
48
|
-
throw new Error(finalMessage);
|
|
16
|
+
// Re-exporting for backward compatibility
|
|
17
|
+
export { getPlaywrightVersion } from "./getPlaywrightVersion.js";
|
|
18
|
+
export { parseJwt } from "./parseJwt.js";
|
|
19
|
+
export const getPackageVersion = () => {
|
|
20
|
+
// hacky way to get package version
|
|
21
|
+
// try from dist folder first (customer perspective)
|
|
22
|
+
const distVersion = getPackageVersionFromFolder("../../../");
|
|
23
|
+
if (distVersion) {
|
|
24
|
+
return distVersion;
|
|
25
|
+
}
|
|
26
|
+
// if not found, try from src folder (internal test suite)
|
|
27
|
+
const srcVersion = getPackageVersionFromFolder("../../");
|
|
28
|
+
if (srcVersion) {
|
|
29
|
+
return srcVersion;
|
|
30
|
+
}
|
|
31
|
+
return "unknown-version";
|
|
49
32
|
};
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return null;
|
|
33
|
+
export const exitWithFailureMessage = (error, errorDetails) => {
|
|
34
|
+
console.log();
|
|
35
|
+
if (error.formatWithErrorDetails && errorDetails) {
|
|
36
|
+
console.error(error.formatWithErrorDetails(errorDetails));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.error(error.message);
|
|
40
|
+
}
|
|
41
|
+
// eslint-disable-next-line n/no-process-exit
|
|
42
|
+
process.exit(1);
|
|
63
43
|
};
|
|
64
|
-
const
|
|
65
|
-
|
|
44
|
+
export const throwErrorWithFailureMessage = (error, errorDetails) => {
|
|
45
|
+
console.log();
|
|
46
|
+
const finalMessage = error.formatWithErrorDetails && errorDetails
|
|
47
|
+
? error.formatWithErrorDetails(errorDetails)
|
|
48
|
+
: error.message;
|
|
49
|
+
throw new Error(finalMessage);
|
|
66
50
|
};
|
|
67
|
-
const
|
|
68
|
-
|
|
51
|
+
export const populateValuesFromServiceUrl = () => {
|
|
52
|
+
// Service URL format: wss://<region>.api.playwright.microsoft.com/accounts/<workspace-id>/browsers
|
|
53
|
+
const url = process.env["PLAYWRIGHT_SERVICE_URL"];
|
|
54
|
+
if (url) {
|
|
55
|
+
const parts = url.split("/");
|
|
56
|
+
if (parts.length > 2) {
|
|
57
|
+
const subdomainParts = parts[2].split(".");
|
|
58
|
+
const region = subdomainParts.length > 0 ? subdomainParts[0] : null;
|
|
59
|
+
const domain = subdomainParts.slice(2).join(".");
|
|
60
|
+
const accountId = parts[4];
|
|
61
|
+
return { region: region, domain: domain, accountId: accountId };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
69
65
|
};
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
75
|
-
return guidRegex.test(guid);
|
|
66
|
+
export const getAccessToken = () => {
|
|
67
|
+
return process.env[ServiceEnvironmentVariable.PLAYWRIGHT_SERVICE_ACCESS_TOKEN];
|
|
76
68
|
};
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
process.env[InternalEnvironmentVariables.MPT_SERVICE_RUN_ID] = runId;
|
|
80
|
-
return runId;
|
|
69
|
+
export const getServiceBaseURL = () => {
|
|
70
|
+
return process.env[ServiceEnvironmentVariable.PLAYWRIGHT_SERVICE_URL];
|
|
81
71
|
};
|
|
82
|
-
const
|
|
83
|
-
|
|
72
|
+
export const isValidGuid = (guid) => {
|
|
73
|
+
if (!guid) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
77
|
+
return guidRegex.test(guid);
|
|
84
78
|
};
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
79
|
+
export const getAndSetRunId = () => {
|
|
80
|
+
const runId = randomUUID();
|
|
81
|
+
process.env[InternalEnvironmentVariables.MPT_SERVICE_RUN_ID] = runId;
|
|
82
|
+
return runId;
|
|
90
83
|
};
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
if (!isValidRunID) {
|
|
94
|
-
const errorMessage = ServiceErrorMessageConstants.INVALID_RUN_ID_FORMAT.message;
|
|
95
|
-
throw new Error(errorMessage);
|
|
96
|
-
}
|
|
84
|
+
export const getServiceWSEndpoint = (runId, os, apiVersion) => {
|
|
85
|
+
return `${getServiceBaseURL()}?runId=${encodeURIComponent(runId)}&os=${os}&sourceType=${BrowserSessionSourceType.PLAYWRIGHT_WORKSPACES_TEST_RUN}&api-version=${apiVersion}`;
|
|
97
86
|
};
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (!accessToken) {
|
|
103
|
-
validationFailureCallback(ServiceErrorMessageConstants.NO_AUTH_ERROR_PAT_TOKEN);
|
|
87
|
+
export const validateServiceUrl = () => {
|
|
88
|
+
const serviceUrl = getServiceBaseURL();
|
|
89
|
+
if (!serviceUrl) {
|
|
90
|
+
exitWithFailureMessage(ServiceErrorMessageConstants.NO_SERVICE_URL_ERROR);
|
|
104
91
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
92
|
+
};
|
|
93
|
+
export const ValidateRunID = (runID) => {
|
|
94
|
+
const isValidRunID = isValidGuid(runID);
|
|
95
|
+
if (!isValidRunID) {
|
|
96
|
+
const errorMessage = ServiceErrorMessageConstants.INVALID_RUN_ID_FORMAT.message;
|
|
97
|
+
throw new Error(errorMessage);
|
|
108
98
|
}
|
|
109
|
-
|
|
110
|
-
|
|
99
|
+
};
|
|
100
|
+
export const validateMptPAT = (validationFailureCallback) => {
|
|
101
|
+
try {
|
|
102
|
+
const accessToken = getAccessToken();
|
|
103
|
+
const result = populateValuesFromServiceUrl();
|
|
104
|
+
if (!accessToken) {
|
|
105
|
+
validationFailureCallback(ServiceErrorMessageConstants.NO_AUTH_ERROR_PAT_TOKEN);
|
|
106
|
+
}
|
|
107
|
+
const claims = parseJwt(accessToken);
|
|
108
|
+
if (!claims.exp) {
|
|
109
|
+
validationFailureCallback(ServiceErrorMessageConstants.INVALID_MPT_PAT_ERROR);
|
|
110
|
+
}
|
|
111
|
+
if (Date.now() >= claims.exp * 1000) {
|
|
112
|
+
validationFailureCallback(ServiceErrorMessageConstants.EXPIRED_MPT_PAT_ERROR);
|
|
113
|
+
}
|
|
114
|
+
if (result.accountId !== claims.pwid) {
|
|
115
|
+
validationFailureCallback(ServiceErrorMessageConstants.WORKSPACE_MISMATCH_ERROR);
|
|
116
|
+
}
|
|
111
117
|
}
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
catch (err) {
|
|
119
|
+
coreLogger.error(err);
|
|
120
|
+
exitWithFailureMessage(ServiceErrorMessageConstants.INVALID_MPT_PAT_ERROR);
|
|
114
121
|
}
|
|
115
|
-
} catch (err) {
|
|
116
|
-
coreLogger.error(err);
|
|
117
|
-
exitWithFailureMessage(ServiceErrorMessageConstants.INVALID_MPT_PAT_ERROR);
|
|
118
|
-
}
|
|
119
122
|
};
|
|
120
123
|
const isTokenExpiringSoon = (expirationTime, currentTime) => {
|
|
121
|
-
|
|
124
|
+
return expirationTime * 1000 - currentTime <= Constants.SevenDaysInMS;
|
|
122
125
|
};
|
|
123
126
|
const warnAboutTokenExpiry = (expirationTime, currentTime) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
const daysToExpiration = Math.ceil((expirationTime * 1000 - currentTime) / Constants.OneDayInMS);
|
|
128
|
+
const expirationDate = new Date(expirationTime * 1000).toLocaleDateString();
|
|
129
|
+
const expirationWarning = `Warning: The access token used for this test run will expire in ${daysToExpiration} days on ${expirationDate}. Generate a new token from the portal to avoid failures. For a simpler, more secure solution, switch to Microsoft Entra ID and eliminate token management. https://learn.microsoft.com/entra/identity/`;
|
|
130
|
+
console.warn(expirationWarning);
|
|
128
131
|
};
|
|
129
|
-
const warnIfAccessTokenCloseToExpiry = () => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
export const warnIfAccessTokenCloseToExpiry = () => {
|
|
133
|
+
const accessToken = getAccessToken();
|
|
134
|
+
if (!accessToken) {
|
|
135
|
+
throw new Error(ServiceErrorMessageConstants.NO_AUTH_ERROR_PAT_TOKEN.message);
|
|
136
|
+
}
|
|
137
|
+
const claims = parseJwt(accessToken);
|
|
138
|
+
const currentTime = Date.now();
|
|
139
|
+
if (isTokenExpiringSoon(claims.exp, currentTime)) {
|
|
140
|
+
warnAboutTokenExpiry(claims.exp, currentTime);
|
|
141
|
+
}
|
|
139
142
|
};
|
|
140
|
-
const fetchOrValidateAccessToken = async (credential) => {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
143
|
+
export const fetchOrValidateAccessToken = async (credential) => {
|
|
144
|
+
const entraIdAccessToken = createEntraIdAccessToken(credential);
|
|
145
|
+
// Fetch a token or refresh if needed in a single call
|
|
146
|
+
if (entraIdAccessToken.doesEntraIdAccessTokenNeedRotation()) {
|
|
147
|
+
await entraIdAccessToken.fetchEntraIdAccessToken();
|
|
148
|
+
}
|
|
149
|
+
const token = getAccessToken();
|
|
150
|
+
if (!token) {
|
|
151
|
+
throw new Error(ServiceErrorMessageConstants.NO_AUTH_ERROR_ENTRA_TOKEN.message);
|
|
152
|
+
}
|
|
153
|
+
return token;
|
|
150
154
|
};
|
|
151
|
-
const getVersionInfo = (version) => {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
export const getVersionInfo = (version) => {
|
|
156
|
+
const regex = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?/;
|
|
157
|
+
const match = version.match(regex);
|
|
158
|
+
const versionInfo = {
|
|
159
|
+
major: 0,
|
|
160
|
+
minor: 0,
|
|
161
|
+
patch: 0,
|
|
162
|
+
};
|
|
163
|
+
versionInfo.major = match && match[1] ? parseInt(match[1], 10) : 0;
|
|
164
|
+
versionInfo.minor = match && match[2] ? parseInt(match[2], 10) : 0;
|
|
165
|
+
versionInfo.patch = match && match[3] ? parseInt(match[3], 10) : 0;
|
|
166
|
+
return versionInfo;
|
|
163
167
|
};
|
|
164
|
-
const validatePlaywrightVersion = () => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
export const validatePlaywrightVersion = () => {
|
|
169
|
+
const minimumSupportedVersion = Constants.MinimumSupportedPlaywrightVersion;
|
|
170
|
+
const installedVersion = getPlaywrightVersion();
|
|
171
|
+
const minimumSupportedVersionInfo = getVersionInfo(minimumSupportedVersion);
|
|
172
|
+
const installedVersionInfo = getVersionInfo(installedVersion);
|
|
173
|
+
const isInstalledVersionGreater = installedVersionInfo.major > minimumSupportedVersionInfo.major ||
|
|
174
|
+
(installedVersionInfo.major === minimumSupportedVersionInfo.major &&
|
|
175
|
+
installedVersionInfo.minor >= minimumSupportedVersionInfo.minor);
|
|
176
|
+
if (!isInstalledVersionGreater) {
|
|
177
|
+
exitWithFailureMessage(ServiceErrorMessageConstants.INVALID_PLAYWRIGHT_VERSION_ERROR);
|
|
178
|
+
}
|
|
173
179
|
};
|
|
174
|
-
const getTestRunConfig = (config) => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
180
|
+
export const getTestRunConfig = (config) => {
|
|
181
|
+
const maxWorkers = config.workers || config.metadata.actualWorkers;
|
|
182
|
+
const frameWorkVersion = config.version;
|
|
183
|
+
const testRunConfig = {
|
|
184
|
+
framework: {
|
|
185
|
+
name: RunConfigConstants.TEST_FRAMEWORK_NAME,
|
|
186
|
+
version: frameWorkVersion,
|
|
187
|
+
runnerName: RunConfigConstants.TEST_FRAMEWORK_RUNNERNAME,
|
|
188
|
+
},
|
|
189
|
+
sdkLanguage: RunConfigConstants.TEST_SDK_LANGUAGE,
|
|
190
|
+
maxWorkers: maxWorkers,
|
|
191
|
+
};
|
|
192
|
+
return testRunConfig;
|
|
187
193
|
};
|
|
188
|
-
function getTestRunApiUrl() {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
export function getTestRunApiUrl() {
|
|
195
|
+
const result = populateValuesFromServiceUrl();
|
|
196
|
+
const runId = process.env[InternalEnvironmentVariables.MPT_SERVICE_RUN_ID];
|
|
197
|
+
if (!result?.region || !result?.domain || !result?.accountId) {
|
|
198
|
+
exitWithFailureMessage(ServiceErrorMessageConstants.NO_SERVICE_URL_ERROR);
|
|
199
|
+
}
|
|
200
|
+
const baseUrl = `https://${result?.region}.${UrlConstants.ReportingApiSubdomain}.${result?.domain}/${UrlConstants.PlaywrightWorkspacesPath}/${result?.accountId}/${UrlConstants.TestRunsPath}`;
|
|
201
|
+
const url = runId ? `${baseUrl}/${runId}` : baseUrl;
|
|
202
|
+
return `${url}?api-version=${Constants.LatestAPIVersion}`;
|
|
197
203
|
}
|
|
198
|
-
function isNullOrEmpty(str) {
|
|
199
|
-
|
|
204
|
+
export function isNullOrEmpty(str) {
|
|
205
|
+
return !str || str.trim() === "";
|
|
200
206
|
}
|
|
201
207
|
async function runCommand(command) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
208
|
+
return new Promise((resolve, reject) => {
|
|
209
|
+
exec(command, (error, stdout, stderr) => {
|
|
210
|
+
if (error) {
|
|
211
|
+
reject(error);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (stderr) {
|
|
215
|
+
reject(new Error(stderr));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
resolve(stdout.trim());
|
|
219
|
+
});
|
|
213
220
|
});
|
|
214
|
-
});
|
|
215
221
|
}
|
|
216
|
-
async function getRunName(ciInfo) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
222
|
+
export async function getRunName(ciInfo) {
|
|
223
|
+
if (ciInfo.providerName === CI_PROVIDERS.GITHUB &&
|
|
224
|
+
process.env["GITHUB_EVENT_NAME"] === "pull_request") {
|
|
225
|
+
const prNumber = `${process.env["GITHUB_REF_NAME"]?.split("/")[0]}`;
|
|
226
|
+
const prLink = `${process.env["GITHUB_REPOSITORY"]}/pull/${prNumber}`;
|
|
227
|
+
return `PR# ${prNumber} on Repo: ${process.env["GITHUB_REPOSITORY"]} (${prLink})`;
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const gitVersion = await runCommand(GitHubActionsConstants.GIT_VERSION_COMMAND);
|
|
231
|
+
if (isNullOrEmpty(gitVersion)) {
|
|
232
|
+
throw new Error("Git is not installed on the machine");
|
|
233
|
+
}
|
|
234
|
+
const isInsideWorkTree = await runCommand(GitHubActionsConstants.GIT_REV_PARSE);
|
|
235
|
+
if (isInsideWorkTree !== "true") {
|
|
236
|
+
throw new Error("Not inside a git repository");
|
|
237
|
+
}
|
|
238
|
+
const gitCommitMessage = await runCommand(GitHubActionsConstants.GIT_COMMIT_MESSAGE_COMMAND);
|
|
239
|
+
return gitCommitMessage;
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
coreLogger.error(`Error in getting git commit message: ${err}.`);
|
|
243
|
+
return "";
|
|
244
|
+
}
|
|
237
245
|
}
|
|
238
|
-
function extractErrorMessage(responseBody) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
export function extractErrorMessage(responseBody) {
|
|
247
|
+
if (!responseBody) {
|
|
248
|
+
return "";
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
const errorResponse = JSON.parse(responseBody);
|
|
252
|
+
if (errorResponse.error && errorResponse.error.message) {
|
|
253
|
+
return errorResponse.error.message;
|
|
254
|
+
}
|
|
255
|
+
return responseBody;
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
return responseBody;
|
|
259
|
+
}
|
|
251
260
|
}
|
|
252
|
-
function getWorkspaceMetaDataApiUrl() {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
261
|
+
export function getWorkspaceMetaDataApiUrl() {
|
|
262
|
+
const result = populateValuesFromServiceUrl();
|
|
263
|
+
if (!result?.region || !result?.domain || !result?.accountId) {
|
|
264
|
+
exitWithFailureMessage(ServiceErrorMessageConstants.NO_SERVICE_URL_ERROR);
|
|
265
|
+
}
|
|
266
|
+
const baseUrl = `https://${result?.region}.${UrlConstants.ApiSubdomain}.${result?.domain}/${UrlConstants.PlaywrightWorkspacesPath}/${result?.accountId}`;
|
|
267
|
+
return `${baseUrl}?api-version=${Constants.LatestAPIVersion}`;
|
|
259
268
|
}
|
|
260
|
-
function getHtmlReporterOutputFolder(config) {
|
|
261
|
-
|
|
262
|
-
|
|
269
|
+
export function getHtmlReporterOutputFolder(config) {
|
|
270
|
+
const defaultFolder = "playwright-report";
|
|
271
|
+
if (!config?.reporter) {
|
|
272
|
+
return defaultFolder;
|
|
273
|
+
}
|
|
274
|
+
for (const reporter of config.reporter) {
|
|
275
|
+
if (Array.isArray(reporter)) {
|
|
276
|
+
const [reporterName, options] = reporter;
|
|
277
|
+
if (reporterName === "html" && options && typeof options === "object") {
|
|
278
|
+
return options.outputFolder || defaultFolder;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else if (typeof reporter === "string" && reporter === "html") {
|
|
282
|
+
return defaultFolder;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
263
285
|
return defaultFolder;
|
|
264
|
-
}
|
|
265
|
-
for (const reporter of config.reporter) {
|
|
266
|
-
if (Array.isArray(reporter)) {
|
|
267
|
-
const [reporterName, options] = reporter;
|
|
268
|
-
if (reporterName === "html" && options && typeof options === "object") {
|
|
269
|
-
return options.outputFolder || defaultFolder;
|
|
270
|
-
}
|
|
271
|
-
} else if (typeof reporter === "string" && reporter === "html") {
|
|
272
|
-
return defaultFolder;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return defaultFolder;
|
|
276
286
|
}
|
|
277
|
-
function getContentType(filePath) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
287
|
+
export function getContentType(filePath) {
|
|
288
|
+
const ext = filePath.toLowerCase().split(".").pop();
|
|
289
|
+
const contentTypes = {
|
|
290
|
+
html: "text/html",
|
|
291
|
+
css: "text/css",
|
|
292
|
+
js: "application/javascript",
|
|
293
|
+
json: "application/json",
|
|
294
|
+
png: "image/png",
|
|
295
|
+
jpg: "image/jpeg",
|
|
296
|
+
jpeg: "image/jpeg",
|
|
297
|
+
svg: "image/svg+xml",
|
|
298
|
+
ico: "image/x-icon",
|
|
299
|
+
txt: "text/plain",
|
|
300
|
+
ttf: "font/ttf",
|
|
301
|
+
woff: "font/woff",
|
|
302
|
+
woff2: "font/woff2",
|
|
303
|
+
webmanifest: "application/manifest+json",
|
|
304
|
+
map: "application/json",
|
|
305
|
+
xml: "application/xml",
|
|
306
|
+
pdf: "application/pdf",
|
|
307
|
+
zip: "application/zip",
|
|
308
|
+
};
|
|
309
|
+
return contentTypes[ext || ""] || "application/octet-stream";
|
|
300
310
|
}
|
|
301
|
-
function calculateOptimalConcurrency(files) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
optimalConcurrency = Math.min(
|
|
320
|
-
UploadConstants.MAX_CONCURRENCY,
|
|
321
|
-
UploadConstants.BASE_CONCURRENCY
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
return Math.floor(optimalConcurrency);
|
|
311
|
+
export function calculateOptimalConcurrency(files) {
|
|
312
|
+
const totalFiles = files.length;
|
|
313
|
+
const totalSize = files.reduce((sum, f) => sum + f.size, 0);
|
|
314
|
+
const avgFileSize = totalSize / totalFiles;
|
|
315
|
+
let optimalConcurrency;
|
|
316
|
+
if (totalFiles <= 10) {
|
|
317
|
+
optimalConcurrency = Math.min(totalFiles, 10);
|
|
318
|
+
}
|
|
319
|
+
else if (avgFileSize < UploadConstants.SMALL_FILE_THRESHOLD) {
|
|
320
|
+
optimalConcurrency = Math.min(UploadConstants.MAX_CONCURRENCY, Math.max(UploadConstants.BASE_CONCURRENCY, totalFiles / 50));
|
|
321
|
+
}
|
|
322
|
+
else if (totalFiles > 1000) {
|
|
323
|
+
optimalConcurrency = Math.min(UploadConstants.MAX_CONCURRENCY, UploadConstants.BASE_CONCURRENCY + Math.floor(totalFiles / 200));
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
optimalConcurrency = Math.min(UploadConstants.MAX_CONCURRENCY, UploadConstants.BASE_CONCURRENCY);
|
|
327
|
+
}
|
|
328
|
+
return Math.floor(optimalConcurrency);
|
|
325
329
|
}
|
|
326
|
-
function collectAllFiles(folderPath, basePath, runIdFolderPrefix) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
330
|
+
export function collectAllFiles(folderPath, basePath, runIdFolderPrefix) {
|
|
331
|
+
const files = [];
|
|
332
|
+
const stack = [folderPath];
|
|
333
|
+
while (stack.length > 0) {
|
|
334
|
+
const currentPath = stack.pop();
|
|
335
|
+
try {
|
|
336
|
+
const items = readdirSync(currentPath);
|
|
337
|
+
for (const item of items) {
|
|
338
|
+
const itemPath = join(currentPath, item);
|
|
339
|
+
const stats = statSync(itemPath);
|
|
340
|
+
if (stats.isDirectory()) {
|
|
341
|
+
stack.push(itemPath);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
let relativePath = relative(basePath, itemPath).split("\\").join("/");
|
|
345
|
+
if (runIdFolderPrefix) {
|
|
346
|
+
relativePath = `${runIdFolderPrefix}/${relativePath}`;
|
|
347
|
+
}
|
|
348
|
+
files.push({
|
|
349
|
+
fullPath: itemPath,
|
|
350
|
+
relativePath,
|
|
351
|
+
size: stats.size,
|
|
352
|
+
contentType: getContentType(itemPath),
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
continue;
|
|
349
359
|
}
|
|
350
|
-
}
|
|
351
|
-
} catch (error) {
|
|
352
|
-
continue;
|
|
353
360
|
}
|
|
354
|
-
|
|
355
|
-
return files;
|
|
361
|
+
return files;
|
|
356
362
|
}
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
function getPortalTestRunUrl(workspaceMetadata, tenantDomain) {
|
|
368
|
-
const { subscriptionId, resourceId, name } = workspaceMetadata ?? {};
|
|
369
|
-
if (!subscriptionId || !resourceId || !name) {
|
|
370
|
-
throw new Error(
|
|
371
|
-
"Missing required workspace metadata: subscriptionId, resourceId, and name are required"
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
const resourceIdParts = resourceId.split("/");
|
|
375
|
-
const resourceGroupIndex = resourceIdParts.findIndex(
|
|
376
|
-
(part) => part.toLowerCase() === UrlConstants.ResourceGroupsPath
|
|
377
|
-
);
|
|
378
|
-
if (resourceGroupIndex === -1 || resourceGroupIndex + 1 >= resourceIdParts.length) {
|
|
379
|
-
throw new Error("Invalid resourceId format: could not extract resource group name");
|
|
380
|
-
}
|
|
381
|
-
const resourceGroupName = resourceIdParts[resourceGroupIndex + 1];
|
|
382
|
-
const tenantFragment = tenantDomain ? `#@${tenantDomain}` : "#";
|
|
383
|
-
return `${UrlConstants.AzurePortalBaseUrl}/${tenantFragment}${UrlConstants.ResourcePath}${UrlConstants.SubscriptionsPath}/${encodeURIComponent(subscriptionId)}${UrlConstants.ResourceGroupsUrlPath}/${encodeURIComponent(resourceGroupName)}${UrlConstants.ProvidersPath}/${UrlConstants.LoadTestServiceProvider}/${UrlConstants.PlaywrightWorkspacesResourceType}/${encodeURIComponent(name)}/${UrlConstants.TestRunsRoute}`;
|
|
363
|
+
export function getPortalTestRunUrl(resourceId) {
|
|
364
|
+
if (!resourceId) {
|
|
365
|
+
throw new Error("Missing required parameter: resourceId is required");
|
|
366
|
+
}
|
|
367
|
+
const runId = process.env[InternalEnvironmentVariables.MPT_SERVICE_RUN_ID];
|
|
368
|
+
if (!runId) {
|
|
369
|
+
throw new Error("Run ID is required but not found in environment variables");
|
|
370
|
+
}
|
|
371
|
+
return `${UrlConstants.AzurePortalBaseUrl}/${UrlConstants.TestReportViewPath}/testRunId/${encodeURIComponent(runId)}/resourceId/${encodeURIComponent(resourceId)}`;
|
|
384
372
|
}
|
|
385
|
-
const getStorageAccountNameFromUri = (storageUri) => {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
373
|
+
export const getStorageAccountNameFromUri = (storageUri) => {
|
|
374
|
+
try {
|
|
375
|
+
if (!storageUri || typeof storageUri !== "string") {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
const url = new URL(storageUri);
|
|
379
|
+
const hostname = url.hostname;
|
|
380
|
+
// Extract storage account name from hostname pattern: {accountname}.blob.core.windows.net
|
|
381
|
+
const match = hostname.match(/^([^.]+)\.blob\.core\.windows\.net$/i);
|
|
382
|
+
if (match && match[1]) {
|
|
383
|
+
return match[1];
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
389
386
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
if (match && match[1]) {
|
|
394
|
-
return match[1];
|
|
387
|
+
catch (error) {
|
|
388
|
+
console.warn("Failed to extract storage account name from URI:", storageUri, error);
|
|
389
|
+
return null;
|
|
395
390
|
}
|
|
396
|
-
return null;
|
|
397
|
-
} catch (error) {
|
|
398
|
-
console.warn("Failed to extract storage account name from URI:", storageUri, error);
|
|
399
|
-
return null;
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
export {
|
|
403
|
-
ValidateRunID,
|
|
404
|
-
calculateOptimalConcurrency,
|
|
405
|
-
collectAllFiles,
|
|
406
|
-
exitWithFailureMessage,
|
|
407
|
-
extractErrorMessage,
|
|
408
|
-
fetchOrValidateAccessToken,
|
|
409
|
-
getAccessToken,
|
|
410
|
-
getAndSetRunId,
|
|
411
|
-
getContentType,
|
|
412
|
-
getHtmlReporterOutputFolder,
|
|
413
|
-
getPackageVersion,
|
|
414
|
-
getPlaywrightVersion2 as getPlaywrightVersion,
|
|
415
|
-
getPortalTestRunUrl,
|
|
416
|
-
getRunName,
|
|
417
|
-
getServiceBaseURL,
|
|
418
|
-
getServiceWSEndpoint,
|
|
419
|
-
getStorageAccountNameFromUri,
|
|
420
|
-
getTestRunApiUrl,
|
|
421
|
-
getTestRunConfig,
|
|
422
|
-
getVersionInfo,
|
|
423
|
-
getWorkspaceMetaDataApiUrl,
|
|
424
|
-
isNullOrEmpty,
|
|
425
|
-
isValidGuid,
|
|
426
|
-
parseJwt2 as parseJwt,
|
|
427
|
-
populateValuesFromServiceUrl,
|
|
428
|
-
resolveTenantDomain,
|
|
429
|
-
throwErrorWithFailureMessage,
|
|
430
|
-
validateMptPAT,
|
|
431
|
-
validatePlaywrightVersion,
|
|
432
|
-
validateServiceUrl,
|
|
433
|
-
warnIfAccessTokenCloseToExpiry
|
|
434
391
|
};
|
|
392
|
+
//# sourceMappingURL=utils.js.map
|