From de15437faa4d81ba64ea6a918b2817a309a984e5 Mon Sep 17 00:00:00 2001 From: Andreas Zerbst <73799582+andr317c@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:24:30 +0200 Subject: [PATCH 01/22] Cherry-pick V15 QA added delivery api helpers --- lib/helpers/ApiHelpers.ts | 3 + lib/helpers/MediaApiHelper.ts | 10 +++ .../DeliveryApiHelper.ts | 63 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 lib/helpers/differentAppSettingsHelpers/DeliveryApiHelper.ts diff --git a/lib/helpers/ApiHelpers.ts b/lib/helpers/ApiHelpers.ts index 9322667e..d1d01a42 100644 --- a/lib/helpers/ApiHelpers.ts +++ b/lib/helpers/ApiHelpers.ts @@ -33,6 +33,7 @@ import {MemberTypeApiHelper} from "./MemberTypeApiHelper"; import {DocumentBlueprintApiHelper} from "./DocumentBlueprintApiHelper"; import {LoginApiHelper} from "./LoginApiHelper"; import {WebhookApiHelper} from "./WebhookApiHelper"; +import {DeliveryApiHelper} from "./differentAppSettingsHelpers/DeliveryApiHelper"; export class ApiHelpers { baseUrl: string = umbracoConfig.environment.baseUrl; @@ -69,6 +70,7 @@ export class ApiHelpers { documentBlueprint: DocumentBlueprintApiHelper; login: LoginApiHelper; webhook: WebhookApiHelper; + deliveryApi: DeliveryApiHelper; constructor(page: Page) { this.page = page; @@ -104,6 +106,7 @@ export class ApiHelpers { this.documentBlueprint = new DocumentBlueprintApiHelper(this); this.login = new LoginApiHelper(this, this.page); this.webhook = new WebhookApiHelper(this, this.page); + this.deliveryApi = new DeliveryApiHelper(this); } async getAccessToken() { diff --git a/lib/helpers/MediaApiHelper.ts b/lib/helpers/MediaApiHelper.ts index 392750a0..3e70f50a 100644 --- a/lib/helpers/MediaApiHelper.ts +++ b/lib/helpers/MediaApiHelper.ts @@ -155,6 +155,16 @@ export class MediaApiHelper { return null; } + async getMediaUrlById(id: string) { + const media = await this.get(id); + + if (media && media.urls && media.urls.length > 0) { + const mediaUrl = media.urls[0].url; + return mediaUrl.split(this.api.baseUrl).pop(); + } + return null; + } + async createDefaultMediaFile(mediaName: string) { const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryFile(); await this.ensureNameNotExists(mediaName); diff --git a/lib/helpers/differentAppSettingsHelpers/DeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/DeliveryApiHelper.ts new file mode 100644 index 00000000..5742f9ac --- /dev/null +++ b/lib/helpers/differentAppSettingsHelpers/DeliveryApiHelper.ts @@ -0,0 +1,63 @@ +import {ApiHelpers} from "../ApiHelpers"; + +export class DeliveryApiHelper { + api: ApiHelpers + + constructor(api: ApiHelpers) { + this.api = api; + } + + async getAllContentItems() { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content?skip=0&take=10000'); + return await response.json(); + } + + async getContentItemWithId(id: string) { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id); + return await response.json(); + } + + async getContentItemWithPath(path: string) { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/key/' + path); + return await response.json(); + } + + async getAllMediaChildItemsAtRootLevel() { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media?fetch=children:/&skip=0&take=10000'); + return await response.json(); + } + + async getMediaItemWithId(id: string) { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media/item/' + id); + return await response.json(); + } + + async getMediaItemWithPath(path: string) { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media/item/key/' + path); + return await response.json(); + } + + async doesContentItemWithIdContainValues(id: string, contentName: string, documentTypeName: string, properties: {dataTypeName: string; dataTypeValue: string }[]) { + const contentItem = await this.getContentItemWithId(id); + + if (contentItem.name === contentName && contentItem.contentType === documentTypeName) { + if (properties && properties.length > 0) { + for (const property of properties) { + const {dataTypeName, dataTypeValue} = property; + const contentItemPropertyValue = contentItem.properties[dataTypeName]; + if (contentItemPropertyValue !== dataTypeValue) { + return false; + } + } + } + return true; + } + return false; + } + + async doesMediaItemWithIdContainValues(id: string, mediaName: string, mediaTypeName: string, url: string) { + const mediaItem = await this.getMediaItemWithId(id); + + return mediaItem.name === mediaName && mediaItem.mediaType === mediaTypeName && mediaItem.url === url; + } +} \ No newline at end of file From bb71dcfa33733165f37a110d3b0ca65b644a7530 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 29 Aug 2025 15:27:29 +0700 Subject: [PATCH 02/22] Removed this as it was split into two other files --- .../DeliveryApiHelper.ts | 63 ------------------- 1 file changed, 63 deletions(-) delete mode 100644 lib/helpers/differentAppSettingsHelpers/DeliveryApiHelper.ts diff --git a/lib/helpers/differentAppSettingsHelpers/DeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/DeliveryApiHelper.ts deleted file mode 100644 index 5742f9ac..00000000 --- a/lib/helpers/differentAppSettingsHelpers/DeliveryApiHelper.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {ApiHelpers} from "../ApiHelpers"; - -export class DeliveryApiHelper { - api: ApiHelpers - - constructor(api: ApiHelpers) { - this.api = api; - } - - async getAllContentItems() { - const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content?skip=0&take=10000'); - return await response.json(); - } - - async getContentItemWithId(id: string) { - const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id); - return await response.json(); - } - - async getContentItemWithPath(path: string) { - const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/key/' + path); - return await response.json(); - } - - async getAllMediaChildItemsAtRootLevel() { - const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media?fetch=children:/&skip=0&take=10000'); - return await response.json(); - } - - async getMediaItemWithId(id: string) { - const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media/item/' + id); - return await response.json(); - } - - async getMediaItemWithPath(path: string) { - const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media/item/key/' + path); - return await response.json(); - } - - async doesContentItemWithIdContainValues(id: string, contentName: string, documentTypeName: string, properties: {dataTypeName: string; dataTypeValue: string }[]) { - const contentItem = await this.getContentItemWithId(id); - - if (contentItem.name === contentName && contentItem.contentType === documentTypeName) { - if (properties && properties.length > 0) { - for (const property of properties) { - const {dataTypeName, dataTypeValue} = property; - const contentItemPropertyValue = contentItem.properties[dataTypeName]; - if (contentItemPropertyValue !== dataTypeValue) { - return false; - } - } - } - return true; - } - return false; - } - - async doesMediaItemWithIdContainValues(id: string, mediaName: string, mediaTypeName: string, url: string) { - const mediaItem = await this.getMediaItemWithId(id); - - return mediaItem.name === mediaName && mediaItem.mediaType === mediaTypeName && mediaItem.url === url; - } -} \ No newline at end of file From b0aa6b4c56f34ae12894bae1f8fba0e276acac04 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 29 Aug 2025 15:28:12 +0700 Subject: [PATCH 03/22] Added api helper for media delivery API --- .../MediaDeliveryApiHelper.ts | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 lib/helpers/differentAppSettingsHelpers/MediaDeliveryApiHelper.ts diff --git a/lib/helpers/differentAppSettingsHelpers/MediaDeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/MediaDeliveryApiHelper.ts new file mode 100644 index 00000000..bfdd1807 --- /dev/null +++ b/lib/helpers/differentAppSettingsHelpers/MediaDeliveryApiHelper.ts @@ -0,0 +1,94 @@ +import {expect} from "@playwright/test"; +import {ApiHelpers} from "../ApiHelpers"; + +export class MediaDeliveryApiHelper { + api: ApiHelpers + + constructor(api: ApiHelpers) { + this.api = api; + } + + async getMediaItemWithId(id: string) { + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media/item/' + id); + } + + async getMediaItemWithPath(path: string) { + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media/item' + path); + } + + async getMediaItemsWithIds(ids: string[]) { + let query = '?'; + for (let i = 0; i < ids.length; i++) { + query += 'id=' + ids[i] + (i < ids.length - 1 ? '&' : ''); + } + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media/items' + query); + } + + async getMediaItemsFromAQuery(fetch: string, filter?: string, sort?: string, skip?: number, take?: number) { + let query = '?'; + if (!fetch) { + return null; + } + query += 'fetch=' + fetch; + if (filter) { + query += '&filter=' + filter; + } + if (sort) { + query += '&sort=' + sort; + } + if (skip !== undefined) { + query += '&skip=' + skip; + } + if (take !== undefined) { + query += '&take=' + take; + } + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/media' + query); + } + + async verifyDefaultMediaItemJson(mediaName: string, mediaItemJson, mediaPath: string, mediaTypeName: string) { + const mediaData = await this.api.media.getByName(mediaName); + + // Verify path, createDate, updateDate, id, name, and mediaType are always included in the response + expect(mediaItemJson.path).toBe(mediaPath); + expect(new Date(mediaItemJson.createDate)).toEqual(new Date(mediaData.variants[0].createDate)); + expect(new Date(mediaItemJson.updateDate)).toEqual(new Date(mediaData.variants[0].createDate)); + expect(mediaItemJson.id).toBe(mediaData.id); + expect(mediaItemJson.name).toBe(mediaName); + const mediaTypeData = await this.api.mediaType.getByName(mediaTypeName); + expect(mediaItemJson.mediaType).toBe(mediaTypeData.alias); + // Verify url, extension and the size in bytes are included for all files (not for folders). + if (mediaTypeName !== 'Folder') { + const mediaSrc = mediaData.values[0].value.src ? mediaData.values[0].value.src : ''; + expect(mediaItemJson.url).toBe(mediaSrc); + const mediaExtension = mediaData.values.find(x => x.alias === 'umbracoExtension')?.value || null; + expect(mediaItemJson.extension).toBe(mediaExtension); + const mediaBytes = mediaData.values.find(x => x.alias === 'umbracoBytes')?.value || null; + expect(mediaItemJson.bytes).toBe(mediaBytes ? Number(mediaBytes) : null); + } else { + expect(mediaItemJson.url).toBe(''); + expect(mediaItemJson.extension).toBeNull(); + expect(mediaItemJson.bytes).toBeNull(); + } + // Verify width and height (in pixels), focalPoint and crops are included for most images. + if (mediaTypeName === 'Image') { + const mediaWidth = mediaData.values.find(x => x.alias === 'umbracoWidth')?.value; + const mediaHeight = mediaData.values.find(x => x.alias === 'umbracoHeight')?.value; + expect(mediaItemJson.width).toBe(mediaWidth ? Number(mediaWidth) : null); + expect(mediaItemJson.height).toBe(mediaWidth ? Number(mediaHeight) : null); + expect(mediaItemJson.focalPoint).toBe(mediaData.values[0].value.focalPoint); + expect(mediaItemJson.crops).toEqual(mediaData.values[0].value.crops); + } else { + expect(mediaItemJson.width).toBeNull(); + expect(mediaItemJson.height).toBeNull(); + expect(mediaItemJson.focalPoint).toBeNull(); + expect(mediaItemJson.crops).toBeNull(); + } + } + + async verifyMutipleMediaItemsJson(mediaNames: string[], mediaItemsJson, mediaPaths: string[], mediaTypeNames: string[]) { + expect(mediaItemsJson.length).toBe(mediaNames.length); + for (let i = 0; i < mediaNames.length; i++) { + await this.verifyDefaultMediaItemJson(mediaNames[i], mediaItemsJson[i], mediaPaths[i], mediaTypeNames[i]); + } + } +} \ No newline at end of file From b93447a095ce8ac22ed09981b050b2ef85f758f9 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 29 Aug 2025 15:28:31 +0700 Subject: [PATCH 04/22] Added api helper for content delivery api --- .../ContentDeliveryApiHelper.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts diff --git a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts new file mode 100644 index 00000000..feebfa37 --- /dev/null +++ b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts @@ -0,0 +1,42 @@ +import {ApiHelpers} from "../ApiHelpers"; + +export class ContentDeliveryApiHelper { + api: ApiHelpers + + constructor(api: ApiHelpers) { + this.api = api; + } + + async getAllContentItems() { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content?skip=0&take=10000'); + return await response.json(); + } + + async getContentItemWithId(id: string) { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id); + return await response.json(); + } + + async getContentItemWithPath(path: string) { + const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/key/' + path); + return await response.json(); + } + + async doesContentItemWithIdContainValues(id: string, contentName: string, documentTypeName: string, properties: {dataTypeName: string; dataTypeValue: string }[]) { + const contentItem = await this.getContentItemWithId(id); + + if (contentItem.name === contentName && contentItem.contentType === documentTypeName) { + if (properties && properties.length > 0) { + for (const property of properties) { + const {dataTypeName, dataTypeValue} = property; + const contentItemPropertyValue = contentItem.properties[dataTypeName]; + if (contentItemPropertyValue !== dataTypeValue) { + return false; + } + } + } + return true; + } + return false; + } +} \ No newline at end of file From a02e593611d50ddef83cf0f6d408c917ac3ea924 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 29 Aug 2025 15:29:07 +0700 Subject: [PATCH 05/22] Updated api helper for creating other kind of temporary files --- lib/helpers/TemporaryFileApiHelper.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/helpers/TemporaryFileApiHelper.ts b/lib/helpers/TemporaryFileApiHelper.ts index 352dbd62..e4e39177 100644 --- a/lib/helpers/TemporaryFileApiHelper.ts +++ b/lib/helpers/TemporaryFileApiHelper.ts @@ -37,6 +37,18 @@ export class TemporaryFileApiHelper { return this.createTemporaryFile('Article.pdf', 'Article', 'application/pdf'); } + async createDefaultTemporaryVideoFile() { + return this.createTemporaryFile('Video.mp4', 'Video', 'video/mp4'); + } + + async createDefaultTemporaryAudioFile() { + return this.createTemporaryFile('Audio.mp3', 'Audio', 'audio/mpeg'); + } + + async createDefaultTemporarySVGFile() { + return this.createTemporaryFile('VectorGraphics.svg', 'Vector Graphics (SVG)', 'image/svg+xml'); + } + async createTemporaryFile(fileName: string, mediaTypeName: string, mimeType: string) { const mediaType = await this.api.mediaType.getByName(mediaTypeName); const crypto = require('crypto'); From 0b5d2400dce7b468a9d8685ad7b2201b87129878 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 29 Aug 2025 15:29:53 +0700 Subject: [PATCH 06/22] Added isAllowAsRoot into the function --- lib/helpers/MediaTypeApiHelper.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/helpers/MediaTypeApiHelper.ts b/lib/helpers/MediaTypeApiHelper.ts index 6ca698ab..09fbd4b0 100644 --- a/lib/helpers/MediaTypeApiHelper.ts +++ b/lib/helpers/MediaTypeApiHelper.ts @@ -160,12 +160,13 @@ export class MediaTypeApiHelper { return await this.create(mediaType); } - async createMediaTypeWithPropertyEditor(mediaTypeName: string, dataTypeName: string, dataTypeId: string, groupName: string = "GroupTest") { + async createMediaTypeWithPropertyEditor(mediaTypeName: string, dataTypeName: string, dataTypeId: string, groupName: string = "GroupTest", isAllowAsRoot: boolean = false) { const crypto = require('crypto'); const containerId = crypto.randomUUID(); const mediaType = new MediaTypeBuilder() .withName(mediaTypeName) + .withAllowedAsRoot(isAllowAsRoot) .withAlias(AliasHelper.toAlias(mediaTypeName)) .addContainer() .withName(groupName) From 33d3eaa91ae19154b21341db6908df86178b9efb Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 29 Aug 2025 15:30:20 +0700 Subject: [PATCH 07/22] Refactor api helper for media --- lib/helpers/MediaApiHelper.ts | 153 +++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 69 deletions(-) diff --git a/lib/helpers/MediaApiHelper.ts b/lib/helpers/MediaApiHelper.ts index 3e70f50a..65b91c96 100644 --- a/lib/helpers/MediaApiHelper.ts +++ b/lib/helpers/MediaApiHelper.ts @@ -1,5 +1,5 @@ import {ApiHelpers} from "./ApiHelpers"; -import {MediaBuilder} from "@umbraco/json-models-builders"; +import {AliasHelper, MediaBuilder} from "@umbraco/json-models-builders"; export class MediaApiHelper { api: ApiHelpers @@ -39,10 +39,15 @@ export class MediaApiHelper { } if (child.hasChildren) { return await this.recurseDeleteChildren(child); + } else { + return await this.delete(child.id); + } + } + if (child.hasChildren) { + const result = await this.recurseChildren(name, child.id, toDelete); + if (result) { + return result; } - return await this.delete(child.id); - } else if (child.hasChildren) { - return await this.recurseChildren(name, child.id, toDelete); } } return false; @@ -99,7 +104,10 @@ export class MediaApiHelper { if (media.variants[0].name === name) { return await this.get(media.id); } else if (media.hasChildren) { - await this.recurseChildren(name, media.id, false); + const result = await this.recurseChildren(name, media.id, false); + if (result) { + return result; + } } } return null; @@ -167,43 +175,12 @@ export class MediaApiHelper { async createDefaultMediaFile(mediaName: string) { const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryFile(); - await this.ensureNameNotExists(mediaName); - - const media = new MediaBuilder() - .withMediaTypeId(temporaryFile.mediaTypeId) - .addVariant() - .withName(mediaName) - .done() - .addValue() - .withAlias('umbracoFile') - .addValueData() - .withTemporaryFileId(temporaryFile.temporaryFileId) - .done() - .done() - .build(); - - return await this.create(media); + return await this.createDefaultMediaItem(mediaName, temporaryFile); } async createDefaultMediaFileAndParentId(mediaName: string, parentId: string) { const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryFile(); - await this.ensureNameNotExists(mediaName); - - const media = new MediaBuilder() - .withMediaTypeId(temporaryFile.mediaTypeId) - .withParentId(parentId) - .addVariant() - .withName(mediaName) - .done() - .addValue() - .withAlias('umbracoFile') - .addValueData() - .withTemporaryFileId(temporaryFile.temporaryFileId) - .done() - .done() - .build(); - - return await this.create(media); + return await this.createDefaultMediaItemWithParentId(mediaName, parentId, temporaryFile); } async createDefaultMediaFolder(mediaFolderName: string) { @@ -237,6 +214,60 @@ export class MediaApiHelper { async createDefaultMediaWithImage(mediaName: string) { const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryImageFile(); + return await this.createDefaultMediaItem(mediaName, temporaryFile); + } + + async createDefaultMediaWithArticle(mediaName: string) { + const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryArticleFile(); + return await this.createDefaultMediaItem(mediaName, temporaryFile); + } + + async createDefaultMediaWithImageAndParentId(mediaName: string, parentId: string) { + const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryImageFile(); + return await this.createDefaultMediaItemWithParentId(mediaName, parentId, temporaryFile); + } + + async getAllMediaNames(orderBy: string = 'updateDate', orderDirection: string = 'Descending') { + let mediaNames: string[] = []; + const response = await this.api.get(this.api.baseUrl + '/umbraco/management/api/v1/collection/media?id=&dataTypeId=&orderBy=' + orderBy + '&orderDirection=' + orderDirection + '&skip=0&take=100'); + const mediaItems = await response.json(); + for (const media of mediaItems.items) { + mediaNames.push(media.variants[0].name); + } + return mediaNames; + } + + async doesMediaItemHaveChildName(mediaId: string, childName: string) { + const childrenItems = await this.getChildren(mediaId); + for (const child of childrenItems) { + if (child.variants[0].name === childName) { + return true; + } + } + return false; + } + + async createDefaultMediaWithVideo(mediaName: string) { + const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryVideoFile(); + return await this.createDefaultMediaItem(mediaName, temporaryFile); + } + + async createDefaultMediaWithVideoAndParentId(mediaName: string, parentId: string) { + const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryVideoFile(); + return await this.createDefaultMediaItemWithParentId(mediaName, parentId, temporaryFile); + } + + async createDefaultMediaWithAudio(mediaName: string) { + const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryAudioFile(); + return await this.createDefaultMediaItem(mediaName, temporaryFile); + } + + async createDefaultMediaWithSVG(mediaName: string) { + const temporaryFile = await this.api.temporaryFile.createDefaultTemporarySVGFile(); + return await this.createDefaultMediaItem(mediaName, temporaryFile); + } + + private async createDefaultMediaItem(mediaName: string, temporaryFile) { await this.ensureNameNotExists(mediaName); const media = new MediaBuilder() @@ -255,12 +286,12 @@ export class MediaApiHelper { return await this.create(media); } - async createDefaultMediaWithArticle(mediaName: string) { - const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryArticleFile(); + private async createDefaultMediaItemWithParentId(mediaName: string, parentId: string, temporaryFile) { await this.ensureNameNotExists(mediaName); const media = new MediaBuilder() .withMediaTypeId(temporaryFile.mediaTypeId) + .withParentId(parentId) .addVariant() .withName(mediaName) .done() @@ -274,45 +305,29 @@ export class MediaApiHelper { return await this.create(media); } - - async createDefaultMediaWithImageAndParentId(mediaName: string, parentId: string) { - const temporaryFile = await this.api.temporaryFile.createDefaultTemporaryImageFile(); + + async createDefaultMediaWithTextstring(mediaName: string, mediaTypeName: string, textValue: string, dataTypeName: string) { await this.ensureNameNotExists(mediaName); + await this.api.mediaType.ensureNameNotExists(mediaTypeName); + const dataTypeData = await this.api.dataType.getByName(dataTypeName); + const mediaTypeId = await this.api.mediaType.createMediaTypeWithPropertyEditor(mediaTypeName, dataTypeName, dataTypeData.id, 'Test Group', true); + + if (mediaTypeId === undefined) { + throw new Error('Failed to create media type.'); + } const media = new MediaBuilder() - .withMediaTypeId(temporaryFile.mediaTypeId) - .withParentId(parentId) + .withMediaTypeId(mediaTypeId) .addVariant() .withName(mediaName) .done() .addValue() - .withAlias('umbracoFile') - .addValueData() - .withTemporaryFileId(temporaryFile.temporaryFileId) - .done() + .withAlias(AliasHelper.toAlias(dataTypeName)) + .withEntityType('media-property-value') + .withValue(textValue) .done() .build(); return await this.create(media); } - - async getAllMediaNames(orderBy: string = 'updateDate', orderDirection: string = 'Descending') { - let mediaNames: string[] = []; - const response = await this.api.get(this.api.baseUrl + '/umbraco/management/api/v1/collection/media?id=&dataTypeId=&orderBy=' + orderBy + '&orderDirection=' + orderDirection + '&skip=0&take=100'); - const mediaItems = await response.json(); - for (const media of mediaItems.items) { - mediaNames.push(media.variants[0].name); - } - return mediaNames; - } - - async doesMediaItemHaveChildName(mediaId: string, childName: string) { - const childrenItems = await this.getChildren(mediaId); - for (const child of childrenItems) { - if (child.variants[0].name === childName) { - return true; - } - } - return false; - } } \ No newline at end of file From 0902887dc27495f2ffcd3eb8a9dfcd91409fda10 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 29 Aug 2025 15:31:03 +0700 Subject: [PATCH 08/22] Fixed api helper for document --- lib/helpers/DocumentApiHelper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/helpers/DocumentApiHelper.ts b/lib/helpers/DocumentApiHelper.ts index 645d1cd5..ddc2d10f 100644 --- a/lib/helpers/DocumentApiHelper.ts +++ b/lib/helpers/DocumentApiHelper.ts @@ -103,7 +103,10 @@ export class DocumentApiHelper { } } if (child.hasChildren) { - await this.recurseChildren(name, child.id, toDelete); + const result = await this.recurseChildren(name, child.id, toDelete); + if (result) { + return result; + } } } return false; From e55979d4526c4e012f0f6b18b3fa056683464d49 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 29 Aug 2025 15:31:18 +0700 Subject: [PATCH 09/22] Imported api helpers --- lib/helpers/ApiHelpers.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/helpers/ApiHelpers.ts b/lib/helpers/ApiHelpers.ts index d1d01a42..884eab8a 100644 --- a/lib/helpers/ApiHelpers.ts +++ b/lib/helpers/ApiHelpers.ts @@ -33,7 +33,8 @@ import {MemberTypeApiHelper} from "./MemberTypeApiHelper"; import {DocumentBlueprintApiHelper} from "./DocumentBlueprintApiHelper"; import {LoginApiHelper} from "./LoginApiHelper"; import {WebhookApiHelper} from "./WebhookApiHelper"; -import {DeliveryApiHelper} from "./differentAppSettingsHelpers/DeliveryApiHelper"; +import {MediaDeliveryApiHelper} from './differentAppSettingsHelpers/MediaDeliveryApiHelper'; +import {ContentDeliveryApiHelper} from "./differentAppSettingsHelpers/ContentDeliveryApiHelper"; export class ApiHelpers { baseUrl: string = umbracoConfig.environment.baseUrl; @@ -70,7 +71,8 @@ export class ApiHelpers { documentBlueprint: DocumentBlueprintApiHelper; login: LoginApiHelper; webhook: WebhookApiHelper; - deliveryApi: DeliveryApiHelper; + mediaDeliveryApi: MediaDeliveryApiHelper; + contentDeliveryApi: ContentDeliveryApiHelper; constructor(page: Page) { this.page = page; @@ -106,7 +108,8 @@ export class ApiHelpers { this.documentBlueprint = new DocumentBlueprintApiHelper(this); this.login = new LoginApiHelper(this, this.page); this.webhook = new WebhookApiHelper(this, this.page); - this.deliveryApi = new DeliveryApiHelper(this); + this.mediaDeliveryApi = new MediaDeliveryApiHelper(this); + this.contentDeliveryApi = new ContentDeliveryApiHelper(this); } async getAccessToken() { From 218ff6af083796c376237279f8e701941fad443b Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 5 Sep 2025 16:55:36 +0700 Subject: [PATCH 10/22] Added api helper for content delivery api --- .../ContentDeliveryApiHelper.ts | 103 ++++++++++++++---- 1 file changed, 84 insertions(+), 19 deletions(-) diff --git a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts index feebfa37..8a38ac0b 100644 --- a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts @@ -1,4 +1,5 @@ -import {ApiHelpers} from "../ApiHelpers"; +import { expect } from "@playwright/test"; +import {ApiHelpers} from "../ApiHelpers"; export class ContentDeliveryApiHelper { api: ApiHelpers @@ -13,30 +14,94 @@ export class ContentDeliveryApiHelper { } async getContentItemWithId(id: string) { - const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id); - return await response.json(); + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id); } - async getContentItemWithPath(path: string) { - const response = await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/key/' + path); - return await response.json(); + async getContentItemWithRoute(route: string) { + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item' + route); + } + + async getContentItemsWithIds(ids: string[]) { + let query = '?'; + for (let i = 0; i < ids.length; i++) { + query += 'id=' + ids[i] + (i < ids.length - 1 ? '&' : ''); + } + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/items' + query); + } + + async getContentItemsFromAQuery(fetch?: string, filter?: string, sort?: string, skip?: number, take?: number) { + let query = ''; + if (fetch) { + query += ' fetch=' + fetch; + } + if (filter) { + query += ' filter=' + filter; + } + if (sort) { + query += ' sort=' + sort; + } + if (skip !== undefined) { + query += ' skip=' + skip; + } + if (take !== undefined) { + query += ' take=' + take; + } + if (query !== '') { + query = '?' + query.trim().replace(' ', '&'); + } + + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content' + query); + } + + async verifyBasicPropertiesForContentItem(contentName: string, contentItemJson) { + const contentData = await this.api.document.getByName(contentName); + + // Verify name, createDate, updateDate, id and contentType are always included in the response + expect(contentItemJson.name).toBe(contentName); + expect(new Date(contentItemJson.createDate)).toEqual(new Date(contentData.variants[0].createDate)); + expect(new Date(contentItemJson.createDate)).toEqual(new Date(contentData.variants[0].createDate)); + expect(contentItemJson.id).toBe(contentData.id); + const contentTypeData = await this.api.documentType.get(contentData.documentType.id); + expect(contentItemJson.contentType).toBe(contentTypeData.alias); + + // Verify route property + const contentUrl = await this.api.document.getDocumentUrl(contentData.id); + expect(contentItemJson.route.path).toBe(contentUrl); } - async doesContentItemWithIdContainValues(id: string, contentName: string, documentTypeName: string, properties: {dataTypeName: string; dataTypeValue: string }[]) { - const contentItem = await this.getContentItemWithId(id); + async verifyEditorialPropertiesForContentItem(contentName: string, contentItemJson, isVariesByCulture: boolean = false) { + const contentData = await this.api.document.getByName(contentName); + let expectedProperties = {}; - if (contentItem.name === contentName && contentItem.contentType === documentTypeName) { - if (properties && properties.length > 0) { - for (const property of properties) { - const {dataTypeName, dataTypeValue} = property; - const contentItemPropertyValue = contentItem.properties[dataTypeName]; - if (contentItemPropertyValue !== dataTypeValue) { - return false; - } - } + if (!isVariesByCulture) { + for (const property of contentData.values) { + expectedProperties[property.alias] = property.value; } - return true; + expect(contentItemJson.properties).toEqual(expectedProperties); } - return false; + } + + async verifyCulturePropertyForContentItem(contentName: string, contentItemJson, isVariesByCulture: boolean = false) { + const contentData = await this.api.document.getByName(contentName); + + if (!isVariesByCulture) { + expect(contentItemJson.cultures).toEqual({}); + } else { + expect(Object.keys(contentItemJson.cultures).length).toBe(contentData.variants.length); + } + } + + async verifyEditorialPropertiesForContentItemWithMultiURLPicker(contentName: string, contentItemJson, pickerType: string) { + const contentData = await this.api.document.getByName(contentName); + const multiUrlPickerProperty = contentData.values.find((p) => p.alias === 'multiUrlPicker'); + const expectedMultiUrlPickerValue = multiUrlPickerProperty.value; + const actualMultiUrlPickerValue = contentItemJson.properties['multiUrlPicker']; + // Verify that the multi URL picker property has the expected structure and values + expect(actualMultiUrlPickerValue[0].queryString).toBe(expectedMultiUrlPickerValue[0].queryString); + expect(actualMultiUrlPickerValue[0].title).toBe(expectedMultiUrlPickerValue[0].name); + expect(actualMultiUrlPickerValue[0].target).toBe(expectedMultiUrlPickerValue[0].target); + expect(actualMultiUrlPickerValue[0].destinationId).toBe(expectedMultiUrlPickerValue[0].unique); + expect(actualMultiUrlPickerValue[0].route.path).toBe(expectedMultiUrlPickerValue[0].url); + expect(actualMultiUrlPickerValue[0].linkType).toBe(pickerType); } } \ No newline at end of file From 6c7d55bae002fa521fce299bafbb8a985c2c88df Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Tue, 9 Sep 2025 08:12:23 +0200 Subject: [PATCH 11/22] Cleaned up --- .../differentAppSettingsHelpers/ContentDeliveryApiHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts index a59e534f..70ef767c 100644 --- a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts @@ -1,4 +1,4 @@ -import {expect} from "@playwright/test"; +import {expect} from "@playwright/test"; import {ApiHelpers} from "../ApiHelpers"; export class ContentDeliveryApiHelper { From 001bde20c7503bb97f3dc65ec79875db5c6d5ac4 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Tue, 9 Sep 2025 08:36:06 +0200 Subject: [PATCH 12/22] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9915881..7978f136 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.43", + "version": "16.0.44", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.43", + "version": "16.0.44", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.39", diff --git a/package.json b/package.json index 9d5e0afe..9407bf9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.43", + "version": "16.0.44", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [ From b8dbd5e7547773b6fcdaf8067cc9c2509c30bd60 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Tue, 9 Sep 2025 10:43:32 +0200 Subject: [PATCH 13/22] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7978f136..98f0904e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.44", + "version": "16.0.45", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.44", + "version": "16.0.45", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.39", diff --git a/package.json b/package.json index 9407bf9b..4a8e0ba6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.44", + "version": "16.0.45", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [ From 4a31520f41c4996c8649832ea574455a1ce0c744 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Wed, 10 Sep 2025 13:01:00 +0200 Subject: [PATCH 14/22] Updated api helper to add header to get request --- lib/helpers/ApiHelpers.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/helpers/ApiHelpers.ts b/lib/helpers/ApiHelpers.ts index 884eab8a..e8c18475 100644 --- a/lib/helpers/ApiHelpers.ts +++ b/lib/helpers/ApiHelpers.ts @@ -137,9 +137,11 @@ export class ApiHelpers { } } - async get(url: string, params?: { [key: string]: string | number | boolean; }) { + async get(url: string, params?: { [key: string]: string | number | boolean; }, extraHeaders?: { [key: string]: string; }) { + const headers = await this.getHeaders(); + const allHeaders = { ...headers, ...extraHeaders }; const options = { - headers: await this.getHeaders(), + headers: allHeaders, params: params, ignoreHTTPSErrors: true } From be89c5b945a91f01edf6b167a1686dffd1d0d52b Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Wed, 10 Sep 2025 13:01:22 +0200 Subject: [PATCH 15/22] Added api helper for content delivery api --- .../ContentDeliveryApiHelper.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts index 70ef767c..c5665aea 100644 --- a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts @@ -13,8 +13,8 @@ export class ContentDeliveryApiHelper { return await response.json(); } - async getContentItemWithId(id: string) { - return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id); + async getContentItemWithId(id: string, extraHeaders?: { [key: string]: string; }) { + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id, undefined, extraHeaders); } async getContentItemWithRoute(route: string) { @@ -63,10 +63,17 @@ export class ContentDeliveryApiHelper { expect(contentItemJson.id).toBe(contentData.id); const contentTypeData = await this.api.documentType.get(contentData.documentType.id); expect(contentItemJson.contentType).toBe(contentTypeData.alias); + } + async verifyRoutePropertyForContentItem(contentName: string, contentItemJson, expectedRoutePath?: string) { // Verify route property - const contentUrl = await this.api.document.getDocumentUrl(contentData.id); - expect(contentItemJson.route.path).toBe(contentUrl); + if (expectedRoutePath !== undefined) { + expect(contentItemJson.route.path).toBe(expectedRoutePath); + } else { + const contentData = await this.api.document.getByName(contentName); + const contentUrl = await this.api.document.getDocumentUrl(contentData.id); + expect(contentItemJson.route.path).toBe(contentUrl); + } } async verifyEditorialPropertiesForContentItem(contentName: string, contentItemJson, isVariesByCulture: boolean = false) { From 4f6401f31eb84624915400fb4adec2bc1ae28cbc Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Wed, 10 Sep 2025 13:01:38 +0200 Subject: [PATCH 16/22] Updated createDocumentWithTwoCulturesAndTextContent --- lib/helpers/DocumentApiHelper.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/helpers/DocumentApiHelper.ts b/lib/helpers/DocumentApiHelper.ts index 12a39bde..dd394f47 100644 --- a/lib/helpers/DocumentApiHelper.ts +++ b/lib/helpers/DocumentApiHelper.ts @@ -1464,7 +1464,7 @@ export class DocumentApiHelper { return false; } - async createDocumentWithTwoCulturesAndTextContent(documentName: string, documentTypeId: string, textContent: string, dataTypeName: string, firstCulture: string, secondCulture: string) { + async createDocumentWithTwoCulturesAndTextContent(documentName: string, documentTypeId: string, textContent: string, dataTypeName: string, firstCulture: string, secondCulture: string, firstDomainName: string = '/testfirstdomain', secondDomainName: string = '/testseconddomain') { await this.ensureNameNotExists(documentName); const document = new DocumentBuilder() @@ -1486,11 +1486,11 @@ export class DocumentApiHelper { const domainData = new DocumentDomainBuilder() .addDomain() - .withDomainName('/testfirstdomain') + .withDomainName(firstDomainName) .withIsoCode(firstCulture) .done() .addDomain() - .withDomainName('/testseconddomain') + .withDomainName(secondDomainName) .withIsoCode(secondCulture) .done() .build(); From dd086d2af97a6c7c9074ace75a9b1eb5838c427b Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Wed, 17 Sep 2025 12:11:11 +0700 Subject: [PATCH 17/22] Updated api helper for document to create variant document with variant property --- lib/helpers/DocumentApiHelper.ts | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/helpers/DocumentApiHelper.ts b/lib/helpers/DocumentApiHelper.ts index b83679c0..e8ba345b 100644 --- a/lib/helpers/DocumentApiHelper.ts +++ b/lib/helpers/DocumentApiHelper.ts @@ -1589,4 +1589,39 @@ export class DocumentApiHelper { // Create document return await this.create(document); } + + async createVariantDocumentWithVariantProperty(documentName: string, documentTypeId: string, dataTypeName: string, propertyVariants: {culture: string, value}[]) { + await this.ensureNameNotExists(documentName); + + const documentDataBuilder = new DocumentBuilder() + .withDocumentTypeId(documentTypeId); + + for (const property of propertyVariants) { + documentDataBuilder + .addVariant() + .withName(property.culture === 'en-US' ? documentName : documentName + ' - ' + property.culture) + .withCulture(property.culture) + .done() + .addValue() + .withAlias(AliasHelper.toAlias(dataTypeName)) + .withValue(property.value) + .withCulture(property.culture) + .done(); + } + const document = documentDataBuilder.build(); + + return await this.create(document); + } + + async updateDomainsForVariantDocument(documentId: string, domains: {domainName: string, isoCode: string}[]) { + const domainDataBuilder = new DocumentDomainBuilder(); + for (const domain of domains) { + domainDataBuilder.addDomain() + .withDomainName(domain.domainName) + .withIsoCode(domain.isoCode) + .done(); + } + const domainData = domainDataBuilder.build(); + return await this.updateDomains(documentId, domainData); + } } \ No newline at end of file From 8418ba339a639196c6b272bd83f6c2b3e09ee2cc Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Mon, 29 Sep 2025 11:57:46 +0700 Subject: [PATCH 18/22] Added api helper for document to add textstring value to invariant document --- lib/helpers/DocumentApiHelper.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/helpers/DocumentApiHelper.ts b/lib/helpers/DocumentApiHelper.ts index e8ba345b..39b2c60d 100644 --- a/lib/helpers/DocumentApiHelper.ts +++ b/lib/helpers/DocumentApiHelper.ts @@ -1624,4 +1624,18 @@ export class DocumentApiHelper { const domainData = domainDataBuilder.build(); return await this.updateDomains(documentId, domainData); } + + async addTextstringValueToInvariantDocument(documentId: string, dataTypeName: string, textValue: string) { + const documentData = await this.get(documentId); + const textValueAlias = AliasHelper.toAlias(dataTypeName); + documentData.values.push({ + alias: textValueAlias, + value: textValue, + culture: null, + segment: null, + editorAlias: 'Umbraco.Textbox', + entityType: 'document-property-value' + }); + return await this.update(documentId, documentData); + } } \ No newline at end of file From 31ecde6a133f355c5d849a665f1d8c59a029718d Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Tue, 30 Sep 2025 14:05:20 +0700 Subject: [PATCH 19/22] Updated api helper for content delivery api with property expansion --- .../ContentDeliveryApiHelper.ts | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts index f222ff84..251d7897 100644 --- a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts @@ -13,8 +13,19 @@ export class ContentDeliveryApiHelper { return await response.json(); } - async getContentItemWithId(id: string, extraHeaders?: { [key: string]: string; }) { - return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id, undefined, extraHeaders); + async getContentItemWithId(id: string, extraHeaders?: { [key: string]: string; }, expand?: string, fields?: string) { + if (expand || fields) { + let query = '?'; + if (expand) { + query += 'expand=' + expand; + } + if (fields) { + query += (query.length > 1 ? '&' : '') + 'fields=' + fields; + } + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id + query, undefined, extraHeaders); + } else { + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item/' + id, undefined, extraHeaders); + } } async getContentItemWithRoute(route: string) { @@ -111,4 +122,19 @@ export class ContentDeliveryApiHelper { expect(actualMultiUrlPickerValue[0].route.path).toBe(expectedMultiUrlPickerValue[0].url); expect(actualMultiUrlPickerValue[0].linkType).toBe(pickerType); } + + async verifyPropertiesForMediaItem(mediaName: string, mediaItemJson) { + const mediaData = await this.api.media.getByName(mediaName); + + expect(mediaItemJson.name).toBe(mediaName); + expect(mediaItemJson.focalPoint).toEqual(mediaData.values[0].value.focalPoint); + expect(mediaItemJson.crops).toEqual(mediaData.values[0].value.crops); + expect(mediaItemJson.id).toEqual(mediaData.id); + const expectedWidthValue = mediaData.values.find((p) => p.alias === 'umbracoWidth').value; + expect(mediaItemJson.width).toEqual(Number(expectedWidthValue)); + const expectedHeightValue = mediaData.values.find((p) => p.alias === 'umbracoHeight').value; + expect(mediaItemJson.height).toEqual(Number(expectedHeightValue)); + const expectedBytesValue = mediaData.values.find((p) => p.alias === 'umbracoBytes').value; + expect(mediaItemJson.bytes).toEqual(Number(expectedBytesValue)); + } } \ No newline at end of file From 0594bb4edf99acd306494141222df70392d83f0e Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 3 Oct 2025 16:52:36 +0700 Subject: [PATCH 20/22] Updated verifyEditorialPropertiesForInvariantContentItem --- .../ContentDeliveryApiHelper.ts | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts index 251d7897..250e25fd 100644 --- a/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/ContentDeliveryApiHelper.ts @@ -28,19 +28,19 @@ export class ContentDeliveryApiHelper { } } - async getContentItemWithRoute(route: string) { - return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item' + route); + async getContentItemWithRoute(route: string, extraHeaders?: { [key: string]: string; }) { + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/item' + route, undefined, extraHeaders); } - async getContentItemsWithIds(ids: string[]) { + async getContentItemsWithIds(ids: string[], extraHeaders?: { [key: string]: string; }) { let query = '?'; for (let i = 0; i < ids.length; i++) { query += 'id=' + ids[i] + (i < ids.length - 1 ? '&' : ''); } - return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/items' + query); + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content/items' + query, undefined, extraHeaders); } - async getContentItemsFromAQuery(fetch?: string, filter?: string, sort?: string, skip?: number, take?: number) { + async getContentItemsFromAQuery(extraHeaders?: { [key: string]: string; }, fetch?: string, filter?: string, sort?: string, skip?: number, take?: number) { let query = ''; if (fetch) { query += ' fetch=' + fetch; @@ -61,7 +61,7 @@ export class ContentDeliveryApiHelper { query = '?' + query.trim().replace(' ', '&'); } - return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content' + query); + return await this.api.get(this.api.baseUrl + '/umbraco/delivery/api/v2/content' + query, undefined, extraHeaders); } async verifyBasicPropertiesForContentItem(contentName: string, contentItemJson) { @@ -87,16 +87,14 @@ export class ContentDeliveryApiHelper { } } - async verifyEditorialPropertiesForContentItem(contentName: string, contentItemJson, isVariesByCulture: boolean = false) { + async verifyEditorialPropertiesForInvariantContentItem(contentName: string, contentItemJson) { const contentData = await this.api.document.getByName(contentName); let expectedProperties = {}; - if (!isVariesByCulture) { - for (const property of contentData.values) { - expectedProperties[property.alias] = property.value; - } - expect(contentItemJson.properties).toEqual(expectedProperties); + for (const property of contentData.values) { + expectedProperties[property.alias] = property.value; } + expect(contentItemJson.properties).toEqual(expectedProperties); } async verifyCulturePropertyForContentItem(contentName: string, contentItemJson, isVariesByCulture: boolean = false) { From 1919eec8b216cde2e56d5679e44fbb786eb46162 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 3 Oct 2025 16:57:22 +0700 Subject: [PATCH 21/22] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e702fa9c..978c480f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.49", + "version": "16.0.50", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.49", + "version": "16.0.50", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.40", diff --git a/package.json b/package.json index b0884604..994200f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.49", + "version": "16.0.50", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [ From da79b99c2ce0cf7fe556cb7eac733977050df5bd Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Tue, 7 Oct 2025 15:39:54 +0700 Subject: [PATCH 22/22] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 978c480f..ae16ca1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.50", + "version": "16.0.51", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.50", + "version": "16.0.51", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.40", diff --git a/package.json b/package.json index 994200f4..eef25440 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "16.0.50", + "version": "16.0.51", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [