@azure/microsoft-playwright-testing
Version:
Package to integrate your Playwright test suite with Microsoft Playwright Testing service
193 lines • 9.73 kB
JavaScript
;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.validatePlaywrightVersion = exports.getVersionInfo = exports.getPackageVersion = exports.getPlaywrightVersion = exports.emitReportingUrl = exports.fetchOrValidateAccessToken = exports.warnIfAccessTokenCloseToExpiry = exports.validateMptPAT = exports.validateServiceUrl = exports.getServiceWSEndpoint = exports.getAndSetRunId = exports.getServiceBaseURL = exports.getAccessToken = exports.parseJwt = exports.populateValuesFromServiceUrl = exports.base64UrlDecode = exports.exitWithFailureMessage = void 0;
const tslib_1 = require("tslib");
const constants_1 = require("../common/constants");
const messages_1 = require("../common/messages");
const entraIdAccessToken_1 = require("../common/entraIdAccessToken");
const logger_1 = require("../common/logger");
const reporterUtils_1 = tslib_1.__importDefault(require("./reporterUtils"));
const cIInfoProvider_1 = require("./cIInfoProvider");
const packageManager_1 = require("./packageManager");
const child_process_1 = require("child_process");
const exitWithFailureMessage = (error) => {
console.log();
console.error(error.message);
// eslint-disable-next-line n/no-process-exit
process.exit(1);
};
exports.exitWithFailureMessage = exitWithFailureMessage;
const base64UrlDecode = (base64Url) => {
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
const buffer = Buffer.from(base64, "base64");
return buffer.toString("utf-8");
};
exports.base64UrlDecode = base64UrlDecode;
const populateValuesFromServiceUrl = () => {
// Service URL format: wss://<region>.api.playwright.microsoft.com/accounts/<workspace-id>/browsers
const url = process.env["PLAYWRIGHT_SERVICE_URL"];
if (!reporterUtils_1.default.isNullOrEmpty(url)) {
const parts = url.split("/");
if (parts.length > 2) {
const subdomainParts = parts[2].split(".");
const region = subdomainParts.length > 0 ? subdomainParts[0] : null;
const accountId = parts[4];
return { region: region, accountId: accountId };
}
}
return null;
};
exports.populateValuesFromServiceUrl = populateValuesFromServiceUrl;
const parseJwt = (token) => {
const parts = token.split(".");
if (parts.length !== 3) {
throw new Error("Invalid JWT token.");
}
const payload = (0, exports.base64UrlDecode)(parts[1]);
return JSON.parse(payload);
};
exports.parseJwt = parseJwt;
const getAccessToken = () => {
return process.env[constants_1.ServiceEnvironmentVariable.PLAYWRIGHT_SERVICE_ACCESS_TOKEN];
};
exports.getAccessToken = getAccessToken;
const getServiceBaseURL = () => {
return process.env[constants_1.ServiceEnvironmentVariable.PLAYWRIGHT_SERVICE_URL];
};
exports.getServiceBaseURL = getServiceBaseURL;
const getAndSetRunId = () => {
const runId = reporterUtils_1.default.getRunId(cIInfoProvider_1.CIInfoProvider.getCIInfo());
process.env[constants_1.InternalEnvironmentVariables.MPT_SERVICE_RUN_ID] = runId;
return runId;
};
exports.getAndSetRunId = getAndSetRunId;
const getServiceWSEndpoint = (runId, os) => {
return `${(0, exports.getServiceBaseURL)()}?runId=${encodeURIComponent(runId)}&os=${os}&api-version=${constants_1.API_VERSION}`;
};
exports.getServiceWSEndpoint = getServiceWSEndpoint;
const validateServiceUrl = () => {
const serviceUrl = (0, exports.getServiceBaseURL)();
if (!serviceUrl) {
(0, exports.exitWithFailureMessage)(messages_1.ServiceErrorMessageConstants.NO_SERVICE_URL_ERROR);
}
};
exports.validateServiceUrl = validateServiceUrl;
const validateMptPAT = (validationFailureCallback) => {
try {
const accessToken = (0, exports.getAccessToken)();
const result = (0, exports.populateValuesFromServiceUrl)();
if (!accessToken) {
validationFailureCallback(messages_1.ServiceErrorMessageConstants.NO_AUTH_ERROR);
}
const claims = (0, exports.parseJwt)(accessToken);
if (!claims.exp) {
validationFailureCallback(messages_1.ServiceErrorMessageConstants.INVALID_MPT_PAT_ERROR);
}
if (Date.now() >= claims.exp * 1000) {
validationFailureCallback(messages_1.ServiceErrorMessageConstants.EXPIRED_MPT_PAT_ERROR);
}
if (result.accountId !== claims.aid) {
validationFailureCallback(messages_1.ServiceErrorMessageConstants.WORKSPACE_MISMATCH_ERROR);
}
}
catch (err) {
logger_1.coreLogger.error(err);
(0, exports.exitWithFailureMessage)(messages_1.ServiceErrorMessageConstants.INVALID_MPT_PAT_ERROR);
}
};
exports.validateMptPAT = validateMptPAT;
const isTokenExpiringSoon = (expirationTime, currentTime) => {
return expirationTime * 1000 - currentTime <= constants_1.Constants.sevenDaysInMs;
};
const warnAboutTokenExpiry = (expirationTime, currentTime) => {
const daysToExpiration = Math.ceil((expirationTime * 1000 - currentTime) / constants_1.Constants.oneDayInMs);
const expirationDate = new Date(expirationTime * 1000).toLocaleDateString();
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/en-us/entra/identity/`;
console.warn(expirationWarning);
};
const warnIfAccessTokenCloseToExpiry = () => {
const accessToken = (0, exports.getAccessToken)();
const claims = (0, exports.parseJwt)(accessToken);
const currentTime = Date.now();
if (isTokenExpiringSoon(claims.exp, currentTime)) {
warnAboutTokenExpiry(claims.exp, currentTime);
}
};
exports.warnIfAccessTokenCloseToExpiry = warnIfAccessTokenCloseToExpiry;
const fetchOrValidateAccessToken = async (credential) => {
const entraIdAccessToken = new entraIdAccessToken_1.EntraIdAccessToken(credential);
if (entraIdAccessToken.token && entraIdAccessToken.doesEntraIdAccessTokenNeedRotation()) {
await entraIdAccessToken.fetchEntraIdAccessToken();
}
if (!(0, exports.getAccessToken)()) {
throw new Error(messages_1.ServiceErrorMessageConstants.NO_AUTH_ERROR.message);
}
return (0, exports.getAccessToken)();
};
exports.fetchOrValidateAccessToken = fetchOrValidateAccessToken;
const emitReportingUrl = () => {
const regex = /wss:\/\/([\w-]+)\.api\.(playwright(?:-test|-int)?\.io|playwright\.microsoft\.com)\//;
const url = (0, exports.getServiceBaseURL)();
const match = url === null || url === void 0 ? void 0 : url.match(regex);
if (match && match.length >= 3) {
const [, region, domain] = match;
process.env[constants_1.ServiceEnvironmentVariable.PLAYWRIGHT_SERVICE_REPORTING_URL] =
`https://${region}.reporting.api.${domain}`;
}
};
exports.emitReportingUrl = emitReportingUrl;
const getPlaywrightVersion = () => {
if (process.env[constants_1.InternalEnvironmentVariables.MPT_PLAYWRIGHT_VERSION]) {
return process.env[constants_1.InternalEnvironmentVariables.MPT_PLAYWRIGHT_VERSION];
}
const packageManager = (0, packageManager_1.getPackageManager)();
const command = packageManager.runCommand("playwright", "--version");
const stdout = (0, child_process_1.execSync)(command).toString().trim();
const version = packageManager.getVersionFromStdout(stdout);
process.env[constants_1.InternalEnvironmentVariables.MPT_PLAYWRIGHT_VERSION] = version;
logger_1.coreLogger.info(`Playwright version being used - ${process.env[constants_1.InternalEnvironmentVariables.MPT_PLAYWRIGHT_VERSION]}`);
return process.env[constants_1.InternalEnvironmentVariables.MPT_PLAYWRIGHT_VERSION];
};
exports.getPlaywrightVersion = getPlaywrightVersion;
const getPackageVersion = () => {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const version = require("../../package.json").version;
return version;
}
catch (error) {
console.error("Error fetching package version:", error);
return "unknown version";
}
};
exports.getPackageVersion = getPackageVersion;
const getVersionInfo = (version) => {
const regex = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?/;
const match = version.match(regex);
const versionInfo = {
major: 0,
minor: 0,
patch: 0,
};
versionInfo.major = match && match[1] ? parseInt(match[1], 10) : 0;
versionInfo.minor = match && match[2] ? parseInt(match[2], 10) : 0;
versionInfo.patch = match && match[3] ? parseInt(match[3], 10) : 0;
return versionInfo;
};
exports.getVersionInfo = getVersionInfo;
const validatePlaywrightVersion = () => {
const minimumSupportedVersion = constants_1.MINIMUM_SUPPORTED_PLAYWRIGHT_VERSION;
const installedVersion = (0, exports.getPlaywrightVersion)();
const minimumSupportedVersionInfo = (0, exports.getVersionInfo)(minimumSupportedVersion);
const installedVersionInfo = (0, exports.getVersionInfo)(installedVersion);
const isInstalledVersionGreater = installedVersionInfo.major > minimumSupportedVersionInfo.major ||
(installedVersionInfo.major === minimumSupportedVersionInfo.major &&
installedVersionInfo.minor >= minimumSupportedVersionInfo.minor);
if (!isInstalledVersionGreater) {
(0, exports.exitWithFailureMessage)(messages_1.ServiceErrorMessageConstants.INVALID_PLAYWRIGHT_VERSION_ERROR);
}
};
exports.validatePlaywrightVersion = validatePlaywrightVersion;
//# sourceMappingURL=utils.js.map