UNPKG

@umbraco/playwright-testhelpers

Version:

Test helpers for making playwright tests for Umbraco solutions

457 lines 20.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ApiHelpers = void 0; const umbraco_config_1 = require("../../umbraco.config"); const ReportHelper_1 = require("./ReportHelper"); const TelemetryDataApiHelper_1 = require("./TelemetryDataApiHelper"); const LanguageApiHelper_1 = require("./LanguageApiHelper"); const DictionaryApiHelper_1 = require("./DictionaryApiHelper"); const RelationTypeApiHelper_1 = require("./RelationTypeApiHelper"); const UserGroupApiHelper_1 = require("./UserGroupApiHelper"); const TemplateApiHelper_1 = require("./TemplateApiHelper"); const AliasHelper_1 = require("./AliasHelper"); const DataTypeApiHelper_1 = require("./DataTypeApiHelper"); const UserApiHelper_1 = require("./UserApiHelper"); const TemporaryFileApiHelper_1 = require("./TemporaryFileApiHelper"); const PackageApiHelper_1 = require("./PackageApiHelper"); const ScriptApiHelper_1 = require("./ScriptApiHelper"); const PartialViewApiHelper_1 = require("./PartialViewApiHelper"); const StylesheetApiHelper_1 = require("./StylesheetApiHelper"); const fs = require("fs"); const LogViewerApiHelper_1 = require("./LogViewerApiHelper"); const DocumentTypeApiHelper_1 = require("./DocumentTypeApiHelper"); const DocumentApiHelper_1 = require("./DocumentApiHelper"); const MediaTypeApiHelper_1 = require("./MediaTypeApiHelper"); const MediaApiHelper_1 = require("./MediaApiHelper"); const ObjectTypesApiHelper_1 = require("./ObjectTypesApiHelper"); const ModelsBuilderApiHelper_1 = require("./ModelsBuilderApiHelper"); const HealthCheckApiHelper_1 = require("./HealthCheckApiHelper"); const IndexerApiHelper_1 = require("./IndexerApiHelper"); const PublishedCacheApiHelper_1 = require("./PublishedCacheApiHelper"); const RedirectManagementApiHelper_1 = require("./RedirectManagementApiHelper"); const MemberGroupApiHelper_1 = require("./MemberGroupApiHelper"); const MemberApiHelper_1 = require("./MemberApiHelper"); const MemberTypeApiHelper_1 = require("./MemberTypeApiHelper"); const DocumentBlueprintApiHelper_1 = require("./DocumentBlueprintApiHelper"); const LoginApiHelper_1 = require("./LoginApiHelper"); const WebhookApiHelper_1 = require("./WebhookApiHelper"); const MediaDeliveryApiHelper_1 = require("./differentAppSettingsHelpers/MediaDeliveryApiHelper"); const ContentDeliveryApiHelper_1 = require("./differentAppSettingsHelpers/ContentDeliveryApiHelper"); const SmtpApiHelper_1 = require("./SmtpApiHelper"); const ElementApiHelper_1 = require("./ElementApiHelper"); class ApiHelpers { baseUrl = umbraco_config_1.umbracoConfig.environment.baseUrl; page; alias; report; telemetry; language; dictionary; relationType; userGroup; template; dataType; user; temporaryFile; documentType; document; package; script; partialView; stylesheet; logViewer; mediaType; media; objectTypes; modelsBuilder; healthCheck; indexer; publishedCache; redirectManagement; memberGroup; member; memberType; documentBlueprint; login; webhook; mediaDeliveryApi; contentDeliveryApi; smtp; element; constructor(page) { this.page = page; this.alias = new AliasHelper_1.AliasHelper(); this.report = new ReportHelper_1.ReportHelper(this); this.telemetry = new TelemetryDataApiHelper_1.TelemetryDataApiHelper(this); this.language = new LanguageApiHelper_1.LanguageApiHelper(this); this.dictionary = new DictionaryApiHelper_1.DictionaryApiHelper(this); this.relationType = new RelationTypeApiHelper_1.RelationTypeApiHelper(this); this.userGroup = new UserGroupApiHelper_1.UserGroupApiHelper(this); this.template = new TemplateApiHelper_1.TemplateApiHelper(this); this.dataType = new DataTypeApiHelper_1.DataTypeApiHelper(this); this.user = new UserApiHelper_1.UserApiHelper(this, page); this.temporaryFile = new TemporaryFileApiHelper_1.TemporaryFileApiHelper(this); this.documentType = new DocumentTypeApiHelper_1.DocumentTypeApiHelper(this); this.document = new DocumentApiHelper_1.DocumentApiHelper(this); this.package = new PackageApiHelper_1.PackageApiHelper(this); this.script = new ScriptApiHelper_1.ScriptApiHelper(this); this.partialView = new PartialViewApiHelper_1.PartialViewApiHelper(this); this.stylesheet = new StylesheetApiHelper_1.StylesheetApiHelper(this); this.logViewer = new LogViewerApiHelper_1.LogViewerApiHelper(this); this.mediaType = new MediaTypeApiHelper_1.MediaTypeApiHelper(this); this.media = new MediaApiHelper_1.MediaApiHelper(this); this.objectTypes = new ObjectTypesApiHelper_1.ObjectTypesApiHelper(this); this.modelsBuilder = new ModelsBuilderApiHelper_1.ModelsBuilderApiHelper(this); this.healthCheck = new HealthCheckApiHelper_1.HealthCheckApiHelper(this); this.indexer = new IndexerApiHelper_1.IndexerApiHelper(this); this.publishedCache = new PublishedCacheApiHelper_1.PublishedCacheApiHelper(this); this.redirectManagement = new RedirectManagementApiHelper_1.RedirectManagementApiHelper(this); this.memberGroup = new MemberGroupApiHelper_1.MemberGroupApiHelper(this); this.member = new MemberApiHelper_1.MemberApiHelper(this); this.memberType = new MemberTypeApiHelper_1.MemberTypeApiHelper(this); this.documentBlueprint = new DocumentBlueprintApiHelper_1.DocumentBlueprintApiHelper(this); this.login = new LoginApiHelper_1.LoginApiHelper(this, this.page); this.webhook = new WebhookApiHelper_1.WebhookApiHelper(this, this.page); this.mediaDeliveryApi = new MediaDeliveryApiHelper_1.MediaDeliveryApiHelper(this); this.contentDeliveryApi = new ContentDeliveryApiHelper_1.ContentDeliveryApiHelper(this); this.smtp = new SmtpApiHelper_1.SmtpApiHelper(this); this.element = new ElementApiHelper_1.ElementApiHelper(this); } async getAccessToken() { const authToken = await this.getLocalStorageAuthToken(); return authToken.access_token; } async getBearerToken() { return 'Bearer ' + await this.getAccessToken(); } async getCookie() { let someStorage = await this.page.context().storageState(); let cookieString = ""; for (let cookie of someStorage.cookies) { cookieString += cookie.name + '=' + cookie.value + ';'; } return cookieString; } async getHeaders() { return { 'Authorization': await this.readLocalBearerToken(), 'Cookie': await this.readLocalCookie(), }; } async get(url, params, extraHeaders) { const headers = await this.getHeaders(); const allHeaders = { ...headers, ...extraHeaders }; const options = { headers: allHeaders, params: params, ignoreHTTPSErrors: true }; return await this.page.request.get(url, options); } async saveCodeFile(codeFile) { if (codeFile == null) { return; } return await this.post(umbraco_config_1.umbracoConfig.environment.baseUrl + '/umbraco/backoffice/UmbracoApi/CodeFile/PostSave', codeFile); } async post(url, data) { const options = { headers: await this.getHeaders(), data: data, ignoreHTTPSErrors: true }; return await this.page.request.post(url, options); } async delete(url, data) { const options = { headers: await this.getHeaders(), data: data, ignoreHTTPSErrors: true }; return await this.page.request.delete(url, options); } async put(url, data) { const options = { headers: await this.getHeaders(), data: data, ignoreHTTPSErrors: true }; return await this.page.request.put(url, options); } async postMultiPartForm(url, id, name, mimeType, filePath) { const options = { headers: await this.getHeaders(), multipart: { Id: id, File: { name: name, mimeType: mimeType, buffer: fs.readFileSync(filePath) } }, ignoreHTTPSErrors: true }; return await this.page.request.post(url, options); } async getTokenIssuedTime() { const authToken = await this.getLocalStorageAuthToken(); return Number(authToken.issued_at); } async getTokenExpireTime() { const authToken = await this.getLocalStorageAuthToken(); return Number(authToken.expires_in); } async isLoginStateValid() { // We need to get the time from the actual local storage otherwise the time would be increased for each test, and we would never refresh the login state. const tokenTimeIssued = await this.getLocalIssuedAtTokenTime(); const tokenExpireTime = await this.getTokenExpireTime(); // Should use a global value const globalTestTimeout = 45; // We want to have the date minus the globalTimeout, the reason for this is that while a test is running, the token could expire. // The refresh token lasts for 300 seconds, while the access token lasts for 60 seconds (NOT TOTALLY SURE) this is why we add 240 seconds const tokenRefreshTime = tokenTimeIssued + tokenExpireTime - (globalTestTimeout + 240); // We need the currentTimeInEpoch so we can check if the tokenRefreshTime is close to expiring. const currentTimeInEpoch = await this.currentDateToEpoch(); if (tokenRefreshTime <= currentTimeInEpoch) { return await this.refreshLoginState(umbraco_config_1.umbracoConfig.user.login, umbraco_config_1.umbracoConfig.user.password); } return; } async getRefreshToken() { const authToken = await this.getLocalStorageAuthToken(); return authToken.refresh_token; } async currentDateToEpoch() { const currentTime = new Date(Date.now()); return await this.dateToEpoch(currentTime); } async dateToEpoch(date) { const dateToEpoch = date.getTime(); // The epoch is in milliseconds, but we want it to be in seconds(Like it is in the token). const millisecondsToSeconds = dateToEpoch / 1000; // There is no need to have anything after . return Number(millisecondsToSeconds.toString().split('.')[0]); } async refreshLoginState(userEmail, userPassword) { const response = await this.page.request.post(this.baseUrl + '/umbraco/management/api/v1/security/back-office/token', { headers: { 'Content-Type': 'application/x-www-form-urlencoded', Cookie: await this.readLocalCookie(), Origin: this.baseUrl }, form: { grant_type: 'refresh_token', client_id: 'umbraco-back-office', redirect_uri: this.baseUrl + '/umbraco/oauth_complete', refresh_token: await this.getRefreshToken() }, ignoreHTTPSErrors: true }); if (response.status() === 200) { const jsonStorageCookie = response.headers()['set-cookie']; // We get multiple cookies, so we have to split them and then update each of the cookies in our localstorage let cookies = this.splitCookies(jsonStorageCookie); for (const cookie of cookies) { await this.updateCookie(cookie); } return; } console.log('Error refreshing access token.'); const updatedTokensAndCookie = await this.updateTokenAndCookie(userEmail, userPassword); console.log('Successfully retrieved new authentication tokens.'); return updatedTokensAndCookie; } splitCookies(cookieString) { return cookieString .trim() .split('\n') .filter(line => line.trim()) .filter(line => !line.includes('expires=Thu, 01 Jan 1970')); } async updateTokenAndCookie(userEmail, userPassword) { const storageStateValues = await this.login.login(userEmail, userPassword); await this.updateCookie(storageStateValues.cookie); // We get multiple set cookies, so we have to split them and then update each of the cookies in our localstorage let cookies = this.splitCookies(storageStateValues.setCookies); for (const cookie of cookies) { await this.updateCookie(cookie); } let tokens = await this.extractTokensFromSetCookie(storageStateValues.setCookies); return { cookie: storageStateValues.cookie, accessToken: tokens.accessToken, refreshToken: tokens.refreshToken, }; } async readFileContent(filePath) { try { const jsonString = fs.readFileSync(filePath, 'utf-8'); return JSON.parse(jsonString); } catch (error) { console.log('Error reading file:', error); throw error; } } async readLocalBearerToken() { const filePath = process.env.STORAGE_STAGE_PATH; if (!filePath) { return await this.getBearerToken(); } try { const data = await JSON.parse(fs.readFileSync(filePath, 'utf-8')); const localStorageItem = await this.getLocalStorageToken(data, 'umb:userAuthTokenResponse'); const parsedValue = await JSON.parse(localStorageItem.value); return `Bearer ${parsedValue.access_token}`; } catch { // If the file is not found, return the current access token from the page context return await this.getBearerToken(); } } async getLocalIssuedAtTokenTime() { const filePath = process.env.STORAGE_STAGE_PATH; if (!filePath) { return await this.getTokenIssuedTime(); } try { const data = await JSON.parse(fs.readFileSync(filePath, 'utf-8')); const localStorageItem = await this.getLocalStorageToken(data, 'umb:userAuthTokenResponse'); const value = JSON.parse(localStorageItem.value); return value.issued_at; } catch { return await this.getTokenIssuedTime(); } } async readLocalCookie() { const filePath = process.env.STORAGE_STAGE_PATH; if (!filePath) { return await this.getCookie(); } try { const data = await JSON.parse(fs.readFileSync(filePath, 'utf-8')); return await data.cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('; ') + ';'; } catch { // If the file is not found, return the current cookie from the page context return await this.getCookie(); } } async getLocalStorageToken(localStorage, tokenName) { return await localStorage.origins?.[0]?.localStorage?.find(item => item.name === tokenName); } async extractTokensFromSetCookie(setCookies) { // Extract token values from cookies const accessToken = setCookies.match(/__Host-umbAccessToken=([^;]+)/)?.[1] ?? ""; const refreshToken = setCookies.match(/__Host-umbRefreshToken=([^;]+)/)?.[1] ?? ""; return { accessToken, refreshToken }; } async getLocalStorageAuthToken() { const currentStorageState = await this.page.context().storageState(); const currentStorageToken = await this.getLocalStorageToken(currentStorageState, 'umb:userAuthTokenResponse'); return JSON.parse(currentStorageToken.value); } async updateCookie(cookieString) { try { // Parse cookie string const parts = cookieString.split(';').map(p => p.trim()); const [nameValue, ...attributes] = parts; const [name, value] = nameValue.split('='); const cookieName = name.trim(); // Get current state const storageState = await this.page.context().storageState(); const cookieIndex = storageState.cookies.findIndex(c => c.name === cookieName); if (cookieIndex === -1) { console.log(`Cookie "${cookieName}" not found`); return; } // Update cookie value storageState.cookies[cookieIndex].value = value; // Update expires if present for (const attr of attributes) { if (attr.toLowerCase().startsWith('expires=')) { const expiresDate = attr.split('=')[1]; storageState.cookies[cookieIndex].expires = Date.parse(expiresDate) / 1000; } } // Write to file if path exists const filePath = process.env.STORAGE_STAGE_PATH; if (filePath) { const fs = require('fs'); const fileData = JSON.parse(fs.readFileSync(filePath, 'utf-8')); const fileCookieIndex = fileData.cookies.findIndex(c => c.name === cookieName); if (fileCookieIndex !== -1) { fileData.cookies[fileCookieIndex] = storageState.cookies[cookieIndex]; fs.writeFileSync(filePath, JSON.stringify(fileData, null, 2)); } else { console.log(`Cookie "${cookieName}" not found in file`); } } } catch (error) { console.error('Error updating cookie:', error); } } async revokeAccessToken(cookie, accessToken) { return await this.page.request.post(this.baseUrl + '/umbraco/management/api/v1/security/back-office/revoke', { headers: { 'Content-Type': 'application/x-www-form-urlencoded', Cookie: cookie, Origin: this.baseUrl }, form: { token: accessToken, token_type_hint: 'access_token', client_id: 'umbraco-back-office' }, ignoreHTTPSErrors: true }); } async revokeRefreshToken(cookie, refreshToken) { return await this.page.request.post(this.baseUrl + '/umbraco/management/api/v1/security/back-office/revoke', { headers: { 'Content-Type': 'application/x-www-form-urlencoded', Cookie: cookie, Origin: this.baseUrl }, form: { token: refreshToken, token_type_hint: 'refresh_token', client_id: 'umbraco-back-office' }, ignoreHTTPSErrors: true }); } async loginToAdminUser(testUserCookie, testUserAccessToken, testUserRefreshToken) { await this.revokeAccessToken(testUserCookie, testUserAccessToken); await this.revokeRefreshToken(testUserCookie, testUserRefreshToken); let userCookieAndTokens; userCookieAndTokens = await this.updateTokenAndCookie(umbraco_config_1.umbracoConfig.user.login, umbraco_config_1.umbracoConfig.user.password); return userCookieAndTokens; } async getCurrentTimePlusMinute(minute = 1) { const now = new Date(); now.setMinutes(now.getMinutes() + minute); // Add one minute const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hours = String(now.getHours()).padStart(2, '0'); const minutes = String(now.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day}T${hours}:${minutes}`; } async convertDateFormat(dateString) { return new Date(dateString).toLocaleString("en-US", { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hour12: true, }); } } exports.ApiHelpers = ApiHelpers; //# sourceMappingURL=ApiHelpers.js.map