diff --git a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs index d002cc2111..5b36de1bfa 100644 --- a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs +++ b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs @@ -69,13 +69,13 @@ public class SecuritySettings /// Gets or sets a value for the user password settings. /// [Obsolete("This no longer works. You can now inject this by using IOptions instead, scheduled for removal in v13")] - public UserPasswordConfigurationSettings? UserPassword { get; set; } + public UserPasswordConfigurationSettings? UserPassword { get; set; } = new(); /// /// Gets or sets a value for the member password settings. /// [Obsolete("This no longer works. You can now inject this by using IOptions instead, scheduled for removal in v13")] - public MemberPasswordConfigurationSettings? MemberPassword { get; set; } + public MemberPasswordConfigurationSettings? MemberPassword { get; set; } = new(); /// /// Gets or sets a value indicating whether to bypass the two factor requirement in Umbraco when using external login diff --git a/src/Umbraco.Core/Services/AuditService.cs b/src/Umbraco.Core/Services/AuditService.cs index 046c5fff3d..1b059238a1 100644 --- a/src/Umbraco.Core/Services/AuditService.cs +++ b/src/Umbraco.Core/Services/AuditService.cs @@ -1,4 +1,6 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; @@ -10,6 +12,8 @@ namespace Umbraco.Cms.Core.Services.Implement; public sealed class AuditService : RepositoryService, IAuditService { private readonly IAuditEntryRepository _auditEntryRepository; + private readonly IUserService _userService; + private readonly IEntityRepository _entityRepository; private readonly IAuditRepository _auditRepository; private readonly Lazy _isAvailable; @@ -18,14 +22,36 @@ public sealed class AuditService : RepositoryService, IAuditService ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IAuditRepository auditRepository, - IAuditEntryRepository auditEntryRepository) + IAuditEntryRepository auditEntryRepository, + IUserService userService, + IEntityRepository entityRepository) : base(provider, loggerFactory, eventMessagesFactory) { _auditRepository = auditRepository; _auditEntryRepository = auditEntryRepository; + _userService = userService; + _entityRepository = entityRepository; _isAvailable = new Lazy(DetermineIsAvailable); } + [Obsolete("Use constructor that also takes IUserService & IEntityRepository instead, scheduled for removal in v13")] + public AuditService( + ICoreScopeProvider provider, + ILoggerFactory loggerFactory, + IEventMessagesFactory eventMessagesFactory, + IAuditRepository auditRepository, + IAuditEntryRepository auditEntryRepository) + : this( + provider, + loggerFactory, + eventMessagesFactory, + auditRepository, + auditEntryRepository, + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) + { + } + public void Add(AuditType type, int userId, int objectId, string? entityType, string comment, string? parameters = null) { using (ICoreScope scope = ScopeProvider.CreateCoreScope()) diff --git a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CompilationOptionsProvider.cs b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CompilationOptionsProvider.cs index 4f2a421981..0dac2084bd 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CompilationOptionsProvider.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CompilationOptionsProvider.cs @@ -195,8 +195,7 @@ internal class CompilationOptionsProvider if (string.IsNullOrEmpty(dependencyContextOptions.LanguageVersion)) { - // If the user does not specify a LanguageVersion, assume CSharp 8.0. This matches the language version Razor 3.0 targets by default. - parseOptions = parseOptions.WithLanguageVersion(LanguageVersion.CSharp8); + parseOptions = parseOptions.WithLanguageVersion(LanguageVersion.Latest); } else if (LanguageVersionFacts.TryParse(dependencyContextOptions.LanguageVersion, out var languageVersion)) { diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/customViews/Image.html b/tests/Umbraco.Tests.AcceptanceTest/fixtures/customViews/Image.html new file mode 100644 index 0000000000..f8f57d7acc --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/fixtures/customViews/Image.html @@ -0,0 +1,89 @@ + + + + + \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/Body.css b/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/Body.css new file mode 100644 index 0000000000..eadaa01275 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/Body.css @@ -0,0 +1,7 @@ +.body{ + font-family: 'Lato'; + font-weight: 200; + font-size: 22px; + margin-top: 0.3em; + color: blue; +} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/StylesheetBlockGrid.css b/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/StylesheetBlockGrid.css new file mode 100644 index 0000000000..b50d4c27a6 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/StylesheetBlockGrid.css @@ -0,0 +1,51 @@ +.umb-block-grid__layout-container { + position: relative; + display: grid; + grid-template-columns: repeat(var(--umb-block-grid--grid-columns, 1), minmax(0, 1fr)); + grid-auto-flow: row; + grid-auto-rows: minmax(50px, -webkit-min-content); + grid-auto-rows: minmax(50px, min-content); + + -moz-column-gap: var(--umb-block-grid--column-gap, 0); + + column-gap: var(--umb-block-grid--column-gap, 0); + row-gap: var(--umb-block-grid--row-gap, 0); +} +.umb-block-grid__layout-item { + position: relative; + /* For small devices we scale columnSpan by three, to make everything bigger than 1/3 take full width: */ + grid-column-end: span min(calc(var(--umb-block-grid--item-column-span, 1) * 3), var(--umb-block-grid--grid-columns)); + grid-row: span var(--umb-block-grid--item-row-span, 1); +} + + +.umb-block-grid__area-container, .umb-block-grid__block--view::part(area-container) { + position: relative; + display: grid; + grid-template-columns: repeat(var(--umb-block-grid--area-grid-columns, var(--umb-block-grid--grid-columns, 1)), minmax(0, 1fr)); + grid-auto-flow: row; + grid-auto-rows: minmax(50px, -webkit-min-content); + grid-auto-rows: minmax(50px, min-content); + + -moz-column-gap: var(--umb-block-grid--areas-column-gap, 0); + + column-gap: var(--umb-block-grid--areas-column-gap, 0); + row-gap: var(--umb-block-grid--areas-row-gap, 0); +} +.umb-block-grid__area { + position: relative; + height: 100%; + display: flex; + flex-direction: column; + /* For small devices we scale columnSpan by three, to make everything bigger than 1/3 take full width: */ + grid-column-end: span min(calc(var(--umb-block-grid--area-column-span, 1) * 3), var(--umb-block-grid--area-grid-columns)); + grid-row: span var(--umb-block-grid--area-row-span, 1); +} + +@media (min-width:1024px) { + .umb-block-grid__layout-item { + grid-column-end: span min(var(--umb-block-grid--item-column-span, 1), var(--umb-block-grid--grid-columns)); + } + .umb-block-grid__area { + grid-column-end: span min(var(--umb-block-grid--area-column-span, 1), var(--umb-block-grid--area-grid-columns)); + \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/Title.css b/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/Title.css new file mode 100644 index 0000000000..eaeea812d5 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/fixtures/stylesheet/Title.css @@ -0,0 +1,8 @@ + +.title{ + font-family: 'Lato'; + font-weight: 300; + font-size: 34px; + margin-top: 0.5em; + color: red; +} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 9957594987..52861c754f 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -7,8 +7,8 @@ "name": "acceptancetest", "hasInstallScript": true, "dependencies": { - "@umbraco/json-models-builders": "^1.0.3", - "@umbraco/playwright-testhelpers": "^1.0.21", + "@umbraco/json-models-builders": "^1.0.5", + "@umbraco/playwright-testhelpers": "^1.0.22", "camelize": "^1.0.0", "dotenv": "^16.0.2", "faker": "^4.1.0", @@ -17,7 +17,7 @@ "xhr2": "^0.2.1" }, "devDependencies": { - "@playwright/test": "^1.19.2", + "@playwright/test": "^1.31", "del": "^6.0.0", "ncp": "^2.0.0", "prompt": "^1.2.0", @@ -86,19 +86,22 @@ } }, "node_modules/@playwright/test": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.26.0.tgz", - "integrity": "sha512-D24pu1k/gQw3Lhbpc38G5bXlBjGDrH5A52MsrH12wz6ohGDeQ+aZg/JFSEsT/B3G8zlJe/EU4EkJK74hpqsjEg==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz", + "integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==", "dev": true, "dependencies": { "@types/node": "*", - "playwright-core": "1.26.0" + "playwright-core": "1.31.1" }, "bin": { "playwright": "cli.js" }, "engines": { "node": ">=14" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, "node_modules/@sideway/address": { @@ -129,20 +132,20 @@ "dev": true }, "node_modules/@umbraco/json-models-builders": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.3.tgz", - "integrity": "sha512-NNBIP9ZXXZvxanmG5OvE+Ppc2ObSLLUyBbwZiPtwerFxdlnYuUYA6qCq6mj7vx3na6MOQTPZMAiNFEaM0V9xFw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.5.tgz", + "integrity": "sha512-14xowT8oiW9+DQVOoundRPvcvnNrU0Ey+06G/q/iZyUnqaNRu/i5nUqcbUZGdv6VBCdxaxq2H3WwtSET3gtneA==", "dependencies": { "camelize": "^1.0.0", "faker": "^4.1.0" } }, "node_modules/@umbraco/playwright-testhelpers": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.21.tgz", - "integrity": "sha512-JfE1MvKc7LEVayF9AX4Ctmx8c6+M+m6+mV7g7QSOYPO7ky/PSlzVvI9S9S7vcpuwLB2Vp4NE9PcXaXUGEvNCnw==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.22.tgz", + "integrity": "sha512-hFqqQvEIylagfqFyhQ2rSyYlUP+xpWA5lkhJjkpb2qpxkIISxjwC/FYJTJGvcoBHuUaZrjsSv4lM2aJy2ZWHMA==", "dependencies": { - "@umbraco/json-models-builders": "^1.0.2", + "@umbraco/json-models-builders": "^1.0.5", "camelize": "^1.0.0", "faker": "^4.1.0", "form-data": "^4.0.0", @@ -409,6 +412,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -725,9 +742,9 @@ } }, "node_modules/playwright-core": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.26.0.tgz", - "integrity": "sha512-p8huU8eU4gD3VkJd3DA1nA7R3XA6rFvFL+1RYS96cSljCF2yJE9CWEHTPF4LqX8KN9MoWCrAfVKP5381X3CZqg==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz", + "integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==", "dev": true, "bin": { "playwright": "cli.js" @@ -1018,13 +1035,14 @@ } }, "@playwright/test": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.26.0.tgz", - "integrity": "sha512-D24pu1k/gQw3Lhbpc38G5bXlBjGDrH5A52MsrH12wz6ohGDeQ+aZg/JFSEsT/B3G8zlJe/EU4EkJK74hpqsjEg==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz", + "integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==", "dev": true, "requires": { "@types/node": "*", - "playwright-core": "1.26.0" + "fsevents": "2.3.2", + "playwright-core": "1.31.1" } }, "@sideway/address": { @@ -1055,20 +1073,20 @@ "dev": true }, "@umbraco/json-models-builders": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.3.tgz", - "integrity": "sha512-NNBIP9ZXXZvxanmG5OvE+Ppc2ObSLLUyBbwZiPtwerFxdlnYuUYA6qCq6mj7vx3na6MOQTPZMAiNFEaM0V9xFw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.5.tgz", + "integrity": "sha512-14xowT8oiW9+DQVOoundRPvcvnNrU0Ey+06G/q/iZyUnqaNRu/i5nUqcbUZGdv6VBCdxaxq2H3WwtSET3gtneA==", "requires": { "camelize": "^1.0.0", "faker": "^4.1.0" } }, "@umbraco/playwright-testhelpers": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.21.tgz", - "integrity": "sha512-JfE1MvKc7LEVayF9AX4Ctmx8c6+M+m6+mV7g7QSOYPO7ky/PSlzVvI9S9S7vcpuwLB2Vp4NE9PcXaXUGEvNCnw==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.22.tgz", + "integrity": "sha512-hFqqQvEIylagfqFyhQ2rSyYlUP+xpWA5lkhJjkpb2qpxkIISxjwC/FYJTJGvcoBHuUaZrjsSv4lM2aJy2ZWHMA==", "requires": { - "@umbraco/json-models-builders": "^1.0.2", + "@umbraco/json-models-builders": "^1.0.5", "camelize": "^1.0.0", "faker": "^4.1.0", "form-data": "^4.0.0", @@ -1273,6 +1291,13 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1503,9 +1528,9 @@ "dev": true }, "playwright-core": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.26.0.tgz", - "integrity": "sha512-p8huU8eU4gD3VkJd3DA1nA7R3XA6rFvFL+1RYS96cSljCF2yJE9CWEHTPF4LqX8KN9MoWCrAfVKP5381X3CZqg==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz", + "integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==", "dev": true }, "prompt": { diff --git a/tests/Umbraco.Tests.AcceptanceTest/package.json b/tests/Umbraco.Tests.AcceptanceTest/package.json index abc16a6865..5df5fc8d4a 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package.json @@ -10,7 +10,7 @@ "createTest": "node createTest.js" }, "devDependencies": { - "@playwright/test": "^1.19.2", + "@playwright/test": "^1.31", "typescript": "^4.8.3", "tslib": "^2.4.0", "del": "^6.0.0", @@ -19,8 +19,8 @@ "wait-on": "^6.0.1" }, "dependencies": { - "@umbraco/json-models-builders": "^1.0.3", - "@umbraco/playwright-testhelpers": "^1.0.21", + "@umbraco/json-models-builders": "^1.0.5", + "@umbraco/playwright-testhelpers": "^1.0.22", "camelize": "^1.0.0", "faker": "^4.1.0", "form-data": "^4.0.0", diff --git a/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts b/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts index f5c9bf5fde..d084cd69ba 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts @@ -36,6 +36,7 @@ const config: PlaywrightTestConfig = { // baseURL: 'http://localhost:44332', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + // When working locally it can be a good idea to use trace: 'on-first-retry' instead of 'retain-on-failure', it can cut the local test times in half. trace: 'retain-on-failure', ignoreHTTPSErrors: true, }, diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorAdvanced.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorAdvanced.spec.ts new file mode 100644 index 0000000000..87b19b8776 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorAdvanced.spec.ts @@ -0,0 +1,355 @@ +import {AliasHelper, ConstantHelper, test} from "@umbraco/playwright-testhelpers"; +import {BlockGridDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; +import {ContentBuilder, DocumentTypeBuilder} from "@umbraco/json-models-builders"; +import {expect} from "@playwright/test"; + +test.describe('BlockGridEditorAdvancedContent', () => { + const documentName = 'DocumentTest'; + const blockGridName = 'BlockGridTest'; + const elementName = 'ElementTitle'; + + const documentAlias = AliasHelper.toAlias(documentName); + const blockGridAlias = AliasHelper.toAlias(blockGridName); + const elementAlias = AliasHelper.toAlias(elementName); + + test.beforeEach(async ({page, umbracoApi, umbracoUi}, testInfo) => { + await umbracoApi.report.report(testInfo); + await umbracoApi.login(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + }); + + test.afterEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + }); + + test.describe('Advanced', () => { + test('can see custom view in content for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + // CustomView + const customViewItemName = "Image"; + const customViewFileName = "Image.html"; + const customViewPath = 'customViews/' + customViewFileName; + const customViewMimeType = "text/html"; + // Image + const imageName = "Umbraco"; + const umbracoFileValue = {"src": "Umbraco.png"}; + const imageFileName = "Umbraco.png"; + const imagePath = 'mediaLibrary/' + imageFileName; + const imageMimeType = "image/png"; + + await umbracoApi.media.ensureNameNotExists(imageName); + await umbracoApi.media.ensureNameNotExists(customViewItemName); + + const imageData = await umbracoApi.media.createImageWithFile(imageName, umbracoFileValue, imageFileName, imagePath, imageMimeType); + + const customViewData = await umbracoApi.media.createFileWithFile(customViewItemName, customViewFileName, customViewPath, customViewMimeType); + const customViewMediaPath = customViewData.mediaLink; + + const element = new DocumentTypeBuilder() + .withName(elementName) + .withAlias(elementAlias) + .AsElementType() + .addGroup() + .withName('ImageGroup') + .withAlias('imageGroup') + .addImagePickerProperty() + .withLabel('Image') + .withAlias('image') + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(element); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withView(customViewMediaPath) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .addImage() + .withMediaKey(imageData.key) + .done() + .done() + .addLayout() + .withContentUdi(element['key']) + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + // Checks if the block has the correct CustomView + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[view="' + customViewMediaPath + '"]')).toBeVisible(); + // Checks if the custom view updated the block by locating a name in the customView + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[view="' + customViewMediaPath + '"]').locator('[name="BlockGridCustomView"]')).toBeVisible(); + // Checks if the block is clickable + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').click(); + + // Clean + await umbracoApi.media.ensureNameNotExists(customViewItemName); + await umbracoApi.media.ensureNameNotExists(imageName); + }); + + test('can see custom stylesheet in content for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const stylesheetName = "StylesheetBlockGrid.css"; + const stylesheetPath = "stylesheet/" + stylesheetName; + const stylesheetMimeType = "text/css"; + + await umbracoApi.media.ensureNameNotExists(stylesheetName); + + const stylesheetData = await umbracoApi.media.createFileWithFile(stylesheetName, stylesheetName, stylesheetPath, stylesheetMimeType); + const stylesheetDataPath = stylesheetData.mediaLink; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withStylesheet(stylesheetDataPath) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + // Checks if the block has the correct template + await expect(page.locator('umb-block-grid-entry', {hasText: elementName}).locator('umb-block-grid-block')).toHaveAttribute('stylesheet', stylesheetDataPath); + + // Clean + await umbracoApi.media.ensureNameNotExists(stylesheetName); + }); + + test('can see changed overlay editor size in content for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const editorSize = 'large'; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withEditorSize(editorSize) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Opens the content editor + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').click(); + + // Assert + // Checks if the editor contains the correct editor size + await expect(page.locator('.umb-editor--' + editorSize).locator('[id="sub-view-0"]')).toBeVisible(); + }); + + test('can use inline editing mode in content for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const newText = 'UpdatedText'; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withInlineEditing(true) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Updates the block text by using the inline editing mode + await page.locator('[data-element="property-' + blockGridAlias + '"]').locator('[id="title"]').click(); + await page.locator('[data-element="property-' + blockGridAlias + '"]').locator('[id="title"]').fill(newText); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the value in the block was updated by using the inline editing mode + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]').locator('[id="title"]:visible')).toHaveValue(newText); + }); + + test('can hide content editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withForceHideContentEditorInOverlay(true) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Click the block + await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: elementName}).click(); + + // Assert + // Checks if the block is still clickable, if it's clickable that means the the content editor is hidden + await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: elementName}).click(); + }); + }); + + test.describe('Catalogue appearance', () => { + test('can see background color in content for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const elementTwoName = 'TheSecondElement'; + const elementTwoAlias = AliasHelper.toAlias(elementTwoName); + + const backgroundColor = 'rgb(244, 67, 54)'; + + await umbracoApi.documentTypes.ensureNameNotExists(elementTwoName); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + const elementTwo = await umbracoApi.documentTypes.createDefaultElementType(elementTwoName, elementTwoAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withBackgroundColor(backgroundColor) + .done() + .addBlock() + .withContentElementTypeKey(elementTwo['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Opens the content editor + await page.locator('[data-element="property-' + blockGridAlias + '"]').getByRole('button', {name: 'Add content'}).click(); + + // Assert + // Checks if the element has the set background color + await expect(page.locator('umb-block-card', {hasText: elementName}).locator('[style="background-color: ' + backgroundColor + '; background-image: none;"]')).toBeVisible(); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementTwoName); + }); + + test('can see icon color in content for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const elementTwoName = 'TheSecondElement'; + const elementTwoAlias = AliasHelper.toAlias(elementTwoName); + + const iconColor = 'rgb(25, 92, 201)'; + + await umbracoApi.documentTypes.ensureNameNotExists(elementTwoName); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + const elementTwo = await umbracoApi.documentTypes.createDefaultElementType(elementTwoName, elementTwoAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withIconColor(iconColor) + .done() + .addBlock() + .withContentElementTypeKey(elementTwo['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Opens the content editor + await page.locator('[data-element="property-' + blockGridAlias + '"]').getByRole('button', {name: 'Add content'}).click(); + + // Assert + // Checks if the element has the set icon color + await expect(page.locator('umb-block-card', {hasText: elementName}).locator('.umb-icon')).toHaveAttribute('style', 'color:' + iconColor); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementTwoName); + }); + + test('can see thumbnail in content for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + // Thumbnail + const imageName = "Umbraco"; + const umbracoFileValue = {"src": "Umbraco.png"}; + const imageFileName = "Umbraco.png"; + const imagePath = 'mediaLibrary/' + imageFileName; + const imageMimeType = "image/png"; + + // ElementTwo + const elementTwoName = 'TheSecondElement'; + const elementTwoAlias = AliasHelper.toAlias(elementTwoName); + + await umbracoApi.media.ensureNameNotExists(imageName); + await umbracoApi.documentTypes.ensureNameNotExists(elementTwoName); + + const imageData = await umbracoApi.media.createImageWithFile(imageName, umbracoFileValue, imageFileName, imagePath, imageMimeType); + const imageDataPath = imageData.mediaLink; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + const elementTwo = await umbracoApi.documentTypes.createDefaultElementType(elementTwoName, elementTwoAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withThumbnail(imageDataPath) + .done() + .addBlock() + .withContentElementTypeKey(elementTwo['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Opens the content editor + await page.locator('[data-element="property-' + blockGridAlias + '"]').getByRole('button', {name: 'Add content'}).click(); + + // Assert + // Checks if the element has the thumbnail + await expect(page.locator('umb-block-card', {hasText: elementName}).locator('.__showcase')).toHaveAttribute('style', 'background-image: url("' + imageDataPath + '?width=400");'); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementTwoName); + await umbracoApi.media.ensureNameNotExists(imageName); + }); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorAreasContent.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorAreasContent.spec.ts new file mode 100644 index 0000000000..a55c12a7d9 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorAreasContent.spec.ts @@ -0,0 +1,1054 @@ +import {AliasHelper, ConstantHelper, test} from "@umbraco/playwright-testhelpers"; +import {ContentBuilder} from "@umbraco/json-models-builders"; +import {BlockGridDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; +import {expect} from "@playwright/test"; + +test.describe('BlockGridEditorAreasContent', () => { + const documentName = 'DocumentTest'; + const blockGridName = 'BlockGridTest'; + const elementTitleName = 'ElementTitle'; + const titleText = 'ElementTitle'; + const titleArea = 'AreaTitle'; + const elementBodyName = 'ElementBody'; + const bodyText = 'Lorem ipsum dolor sit amet'; + + const elementBodyAlias = AliasHelper.toAlias(elementBodyName); + const documentAlias = AliasHelper.toAlias(documentName); + const blockGridAlias = AliasHelper.toAlias(blockGridName); + const elementTitleAlias = AliasHelper.toAlias(elementTitleName); + + async function createContentWithABlockInAnotherBlock(umbracoApi,elementParent, elementChild, dataType?, document?) { + + if (dataType == null) { + dataType = await umbracoApi.dataTypes.createBlockGridDataTypeWithArea(umbracoApi, elementParent, elementChild, blockGridName, titleArea); + } + if (document == null) { + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, elementParent, dataType); + } + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementChild.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementChild['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(elementParent.groups[0].properties[0].alias, titleText) + .withContentTypeKey(elementParent['key']) + .done() + .addLayout() + .withContentUdi(elementParent['key']) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementChild['key']) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + return await umbracoApi.content.save(rootContentNode); + } + + test.beforeEach(async ({page, umbracoApi, umbracoUi}, testInfo) => { + await umbracoApi.report.report(testInfo); + await umbracoApi.login(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementTitleName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + }); + + test.afterEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementTitleName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + }); + + test.describe('Areas test', () => { + test('can add an area from a block grid editor to content', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataType = await umbracoApi.dataTypes.createBlockGridDataTypeWithArea(umbracoApi, element, elementBody, blockGridName, titleArea); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Adds a body to the area + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-area-alias="' + titleArea + '"]').click(); + await page.locator('[name="infiniteEditorForm"]').locator('[data-element="editor-container"]').getByRole('button', {name: elementBodyName}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill(bodyText); + await page.locator('[label="Create"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the body element is under the first element + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-content-element-type-key="' + elementBody['key'] + '"]')).toBeVisible(); + // Checks if the value inside of body is correct + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-content-element-type-key="' + elementBody['key'] + '"]').click(); + await expect(page.locator('[id="sub-view-0"]').locator('[id="title"]')).toHaveValue(bodyText); + }); + + test('can update an area in a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const newBodyText = 'Ipsum'; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + await createContentWithABlockInAnotherBlock(umbracoApi, element, elementBody); + + await umbracoUi.navigateToContent(blockGridName); + + // Updates ElementBody with new text + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-content-element-type-key="' + elementBody['key'] + '"]').click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill(newBodyText); + await page.getByRole('button', {name: 'Submit'}).click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the value inside of body has changed to the correct text + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-content-element-type-key="' + elementBody['key'] + '"]').click(); + await expect(page.locator('[id="sub-view-0"]').locator('[id="title"]')).toHaveValue(newBodyText); + }); + + test('can delete an area from a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + await createContentWithABlockInAnotherBlock(umbracoApi, element, elementBody); + + await umbracoUi.navigateToContent(blockGridName); + + // Deletes the element in the area + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-content-element-type-key="' + elementBody['key'] + '"]').getByTitle("Delete").click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the element was removed from the area + await expect(await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-content-element-type-key="' + elementBody['key'] + '"]')).not.toBeVisible(); + }); + + test('can add multiple areas to a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const areaAlias = "AreaBody"; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias(titleArea) + .done() + .addArea() + .withAlias(areaAlias) + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createContentWithABlockInAnotherBlock(umbracoApi, element, elementBody, dataType); + + await umbracoUi.navigateToContent(blockGridName); + + // Adds another body to the area + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-area-alias="' + areaAlias + '"]').click(); + await page.locator('[name="infiniteEditorForm"]').locator('[data-element="editor-container"]').getByRole('button', {name: 'body'}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill(bodyText); + await page.locator('[label="Create"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if both elements exist in the area + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"] >> umb-block-grid-entries')).toHaveCount(2); + }); + }); + + test.describe('Grid Columns for Areas', () => { + test('can see updated grid columns in areas for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const areaGridColumns = 6; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withAreaGridColumns(areaGridColumns) + .addArea() + .withColumnSpan(areaGridColumns) + .withAlias(titleArea) + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + // Checks if the area in content has the value that is defined in the block grid editor + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-area-alias="' + titleArea + '"]')).toHaveAttribute('data-area-col-span', '6'); + }); + + test('can add two different blocks in a area with different grid columns', async ({page, umbracoApi, umbracoUi}) => { + const secondArea = 'AreaFour'; + const columnSpanEight = "8"; + const columnSpanFour = "4"; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias(titleArea) + .withColumnSpan(columnSpanEight) + .done() + .addArea() + .withAlias(secondArea) + .withColumnSpan(columnSpanFour) + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key']) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementBody['key']) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Adds a block to the area with a different column span + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-area-alias="' + secondArea + '"]').getByRole('button', {name: 'Add content'}).click(); + await page.locator('[name="infiniteEditorForm"]').locator('[data-element="editor-container"]').getByRole('button', {name: elementBodyName}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill('BodyTwoText'); + await page.locator('[label="Create"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if there are two block in the ElementTitle Area + await expect(page.locator('[data-content-element-type-alias="'+elementTitleAlias+'"]').locator('umb-block-grid-entries')).toHaveCount(2); + // Checks if there are two blocks with different column spans + await expect(page.locator('[data-content-element-type-alias="' + elementTitleAlias + '"]').locator('umb-block-grid-entries').locator('[data-col-span="' + columnSpanEight + '"]')).toBeVisible(); + await expect(page.locator('[data-content-element-type-alias="' + elementTitleAlias + '"]').locator('umb-block-grid-entries').locator('[data-col-span="' + columnSpanFour + '"]')).toBeVisible(); + }); + + test('can add a block with a different row span for an area in block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .withRowMaxSpan(3) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias(titleArea) + .withRowSpan(3) + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, false); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if the button has the correct row span + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-area-row-span="3"]').getByRole('button', {name: 'Add content'})).toBeVisible(); + + // Adds a block to the area + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-area-alias="' + titleArea + '"]').getByRole('button', {name: 'Add content'}).click(); + await page.locator('[name="infiniteEditorForm"]').locator('[data-element="editor-container"]').getByRole('button', {name: elementBodyName}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill(bodyText); + await page.locator('[label="Create"]').click(); + // Drags the block so it gets a row span of 3 + const dragFrom = await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]').getByTitle('Drag to scale'); + const dragTo = await page.locator('[data-element="property-' + blockGridAlias + '"]').getByRole('button', {name: 'Clipboard'}); + await umbracoUi.dragAndDrop(dragFrom, dragTo, 0, 0, 10); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the row span of the block has changed from 1 to 3 + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]')).toHaveAttribute('data-row-span', '3'); + }); + }); + + test.describe('Create Button Label', () => { + test('can add a create button label for an area in block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const newButtonLabel = 'NewAreaBlock'; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withCreateLabel(newButtonLabel) + .withAlias(titleArea) + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, false); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + const createButtonLocator = await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[create-label="' + newButtonLabel + '"]'); + // Check if the area has the correct create button label + await expect(createButtonLocator).toBeVisible(); + // Checks if the button is clickable + await createButtonLocator.click(); + }); + }); + + test.describe('Number of blocks', () => { + test('can add a minimum number of blocks for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withMinAllowed(2) + .withAlias(titleArea) + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createContentWithABlockInAnotherBlock(umbracoApi, element, elementBody, dataType); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if a validation error is visible + await expect(page.locator('[key="validation_entriesShort"]')).toBeVisible(); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if an error has been thrown with the property not being valid + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]', {hasText: 'This property is invalid'})).toBeVisible(); + // Adds an element to an area + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-area-alias="' + titleArea + '"]').getByRole('button', {name: 'Add content'}).click(); + await page.locator('[name="infiniteEditorForm"]').locator('[data-element="editor-container"]').getByRole('button', {name: elementBodyName}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill('Test'); + await page.locator('[label="Create"]').click(); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if there are two blocks in the area + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"] >> umb-block-grid-entry')).toHaveCount(2); + }); + + test('can add a maximum number of blocks for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const bodyTextTwo = 'AnotherBody'; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withMaxAllowed(1) + .withAlias(titleArea) + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyTextTwo) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key']) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementBody['key'], 0) + .done() + .addItems() + .withContentUdi(elementBody['key'], 1) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if a validation error is visible + await expect(page.locator('[key="validation_entriesExceed"]')).toBeVisible(); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if an error has been thrown with the property not being valid + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]', {hasText: 'This property is invalid'})).toBeVisible(); + // Removes an element from a area + const BodyTwoUdi = rootContentNode.variants[0].properties[0].value.contentData[1].udi; + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-element-udi="' + BodyTwoUdi + '"]').getByTitle("Delete").click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if there only is one block in the area + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"] >> umb-block-grid-entry')).toHaveCount(1); + }); + }); + + test.describe('Allowed Block Types', () => { + test('can add allowed block types for an area to a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias(titleArea) + .addSpecifiedAllowance() + .withElementTypeKey(elementBody['key']) + .done() + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key']) + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Adds a ElementBody + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-area-alias="' + titleArea + '"]').click(); + // Since the ElementBody is added as the only Specified Allowance for the area of the Element, then we should be instantly directed to it, instead of having to pick it. + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill(bodyText); + await page.locator('[label="Create"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + }); + + test('can add allowed block types with a min amount in a area for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias(titleArea) + .addSpecifiedAllowance() + .withElementTypeKey(elementBody['key']) + .withMinAllowed(2) + .done() + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createContentWithABlockInAnotherBlock(umbracoApi, element, elementBody, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if a validation error is visible + await expect(page.locator('[key="blockEditor_areaValidationEntriesShort"]')).toBeVisible(); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if an error has been thrown with the property not being valid + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]', {hasText: 'This property is invalid'})).toBeVisible(); + // Adds an element to an area + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-area-alias="' + titleArea + '"]').locator('.umb-block-grid__block--last-inline-create-button').click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill('Test'); + await page.locator('[label="Create"]').click(); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if there are two blocks in the area + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"] >> umb-block-grid-entry')).toHaveCount(2); + }); + + test('can add allowed block types with a max amount in a area for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias(titleArea) + .addSpecifiedAllowance() + .withElementTypeKey(elementBody['key']) + .withMaxAllowed(2) + .done() + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, "BodyTwo") + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, "BodyThree") + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key']) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementBody['key'], 0) + .done() + .addItems() + .withContentUdi(elementBody['key'], 1) + .done() + .addItems() + .withContentUdi(elementBody['key'], 2) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if a validation error is visible + await expect(page.locator('[key="blockEditor_areaValidationEntriesExceed"]')).toBeVisible(); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if an error has been thrown with the property not being valid + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]', {hasText: 'This property is invalid'})).toBeVisible(); + // Removes an element from a area + const BodyThreeUdi = rootContentNode.variants[0].properties[0].value.contentData[2].udi; + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-element-udi="' + BodyThreeUdi + '"]').getByTitle("Delete").click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if there are two blocks in the area + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"] >> umb-block-grid-entry')).toHaveCount(2); + }); + + test('can add two different blocks with different min and max', async ({page, umbracoApi, umbracoUi}) => { + const elementFooterName = 'ElementFooter'; + const elementFooterAlias = AliasHelper.toAlias(elementFooterName); + + await umbracoApi.documentTypes.ensureNameNotExists(elementFooterName); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + const elementTitle = await umbracoApi.documentTypes.createDefaultElementType(elementFooterName, elementFooterAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementTitle['key']) + .done() + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias(titleArea) + .addSpecifiedAllowance() + .withElementTypeKey(elementBody['key']) + .withMinAllowed(1) + .withMaxAllowed(1) + .done() + .addSpecifiedAllowance() + .withElementTypeKey(elementTitle['key']) + .withMinAllowed(1) + .withMaxAllowed(1) + .done() + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, "AnotherBodyText") + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key']) + .addAreas() + .withKey(dataType.preValues[0].value[2].areas[0].key) + .addItems() + .withContentUdi(elementBody['key'], 0) + .done() + .addItems() + .withContentUdi(elementBody['key'], 1) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if there is validation error for both blocks + await expect(page.locator('[key="blockEditor_areaValidationEntriesExceed"]', {hasText: elementBodyName})).toBeVisible(); + await expect(page.locator('[key="blockEditor_areaValidationEntriesShort"]', {hasText: elementFooterName})).toBeVisible(); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if an error has been thrown with the property not being valid + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]', {hasText: 'This property is invalid'})).toBeVisible(); + // Removes an ElementBody from a area + const BodyTwoUdi = rootContentNode.variants[0].properties[0].value.contentData[1].udi; + await page.locator('[data-content-element-type-key="' + element['key'] + '"] >> [data-element-udi="' + BodyTwoUdi + '"]').getByTitle("Delete").click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + // Checks if the validation error for ElementBody is removed + await expect(page.locator('[key="blockEditor_areaValidationEntriesExceed"]', {hasText: elementBodyName})).not.toBeVisible(); + // Adds an ElementFooter to the area + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-area-alias="' + titleArea + '"]').locator('.umb-block-grid__block--last-inline-create-button').click(); + await page.locator('[name="infiniteEditorForm"]').locator('[data-element="editor-container"]').getByRole('button', {name: elementFooterName}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill('TitleTest'); + await page.locator('[label="Create"]').click(); + // Checks if the validation error for ElementTitle is removed + await expect(page.locator('[key="blockEditor_areaValidationEntriesShort"]', {hasText: elementFooterName})).not.toBeVisible(); + // Tries to publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if there are two blocks in the area + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]')).toHaveCount(1); + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-content-element-type-alias="' + elementFooterAlias + '"]')).toHaveCount(1); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementFooterName); + }); + }); + + test.describe('Moving Areas', () => { + test('can move an element out of an area', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataType = await umbracoApi.dataTypes.createBlockGridDataTypeWithArea(umbracoApi, element, elementBody, blockGridName, titleArea); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key'], 0) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementBody['key'], 0) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Drags and drops the ElementBody to another element + const dragFrom = await page.locator('[data-content-element-type-key="' + element['key'] + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]'); + const dragTo = await page.locator('[key="blockEditor_addBlock"]'); + await umbracoUi.dragAndDrop(dragFrom, dragTo, 0, 0, 10); + + // Assert + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]').locator('umb-block-grid-entry')).toHaveCount(2); + }); + + test('can move an element from one area to another', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataType = await umbracoApi.dataTypes.createBlockGridDataTypeWithArea(umbracoApi, element, elementBody, blockGridName, titleArea); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, "AnotherOne") + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key'], 0) + .done() + .addLayout() + .withContentUdi(element['key'], 1) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementBody['key'], 0) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + const ElementMoveFromUdi = rootContentNode.variants[0].properties[0].value.contentData[2].udi; + const ElementMoveToUdi = rootContentNode.variants[0].properties[0].value.contentData[1].udi; + + await umbracoUi.navigateToContent(blockGridName); + + // Drags and drops the ElementBody to another element + const dragFrom = await page.locator('[data-element-udi="' + ElementMoveFromUdi + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]'); + const dragTo = await page.locator('[data-element-udi="' + ElementMoveToUdi + '"]').locator('[data-area-alias="' + titleArea + '"]').getByRole('button', {name: 'Add content'}); + await umbracoUi.dragAndDrop(dragFrom, dragTo, -15, 0, 15); + + // Assert + // Expects the element MoveFrom to have 0 entries + await expect(page.locator('[data-element-udi="' + ElementMoveFromUdi + '"]').locator('umb-block-grid-entry')).toHaveCount(0); + // Expects the element MoveTo to have 1 entry + await expect(page.locator('[data-element-udi="' + ElementMoveToUdi + '"]').locator('umb-block-grid-entry')).toHaveCount(1); + }); + }); + + test.describe('Copy and Paste Blocks', () => { + test('can copy and paste an element in a area', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataType = await umbracoApi.dataTypes.createBlockGridDataTypeWithArea(umbracoApi, element, elementBody, blockGridName, titleArea); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, "AnotherOne") + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key'], 0) + .done() + .addLayout() + .withContentUdi(element['key'], 1) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementBody['key'], 0) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + const blockParentUdi = rootContentNode.variants[0].properties[0].value.contentData[2].udi; + const blockToCopyTooUdi = rootContentNode.variants[0].properties[0].value.contentData[1].udi; + + await umbracoUi.navigateToContent(blockGridName); + + // Copies and pastes the block into another block + await page.locator('[data-element-udi="' + blockParentUdi + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]').getByRole('button', {name: 'Copy'}).click(); + await page.locator('[data-element-udi="' + blockToCopyTooUdi + '"]').locator('[data-area-alias="' + titleArea + '"]').click(); + await umbracoUi.clickDataElementByElementName('sub-view-clipboard'); + await page.locator('[data-element="editor-container"]').locator('umb-block-card', {hasText: elementBodyName}).click(); + + // Assert + // Checks if both blocks exist with a block inside of each + await expect(page.locator('[data-element-udi="' + blockParentUdi + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]')).toBeVisible(); + await expect(page.locator('[data-element-udi="' + blockToCopyTooUdi + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]')).toBeVisible(); + // Checks if the correct text is inside of the pasted block + await page.locator('[data-element-udi="' + blockToCopyTooUdi + '"]').locator('[data-content-element-type-alias="' + elementBodyAlias + '"]').click(); + await expect(page.locator('[id="sub-view-0"]').locator('[id="title"]')).toHaveValue(bodyText); + }); + + test('can copy and paste a block with another block inside of it', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataType = await umbracoApi.dataTypes.createBlockGridDataTypeWithArea(umbracoApi, element, elementBody, blockGridName, titleArea); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .appendContentProperties(elementBody.groups[0].properties[0].alias, bodyText) + .withContentTypeKey(elementBody['key']) + .done() + .addBlockGridEntry() + .appendContentProperties(element.groups[0].properties[0].alias, titleText) + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key'], 0) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementBody['key'], 0) + .done() + .done() + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + const blockParentUdi = rootContentNode.variants[0].properties[0].value.contentData[1].udi; + + await umbracoUi.navigateToContent(blockGridName); + + // Copies and pastes the block into another block + await page.locator('[data-element-udi="' + blockParentUdi + '"]').getByRole('button', {name: 'Copy'}).nth(1).click(); + await page.locator('[data-element="property-' + blockGridAlias + '"]').getByRole('button', {name: 'Clipboard'}).click(); + await page.locator('[data-element="editor-container"]').locator('umb-block-card', {hasText: elementTitleName}).click(); + + // Checks if the blocks were copied + await expect(page.locator('[data-content-element-type-alias="' + elementTitleAlias + '"]')).toHaveCount(2); + }); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorContent.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorContent.spec.ts new file mode 100644 index 0000000000..9eb4d75e92 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorContent.spec.ts @@ -0,0 +1,617 @@ +import {AliasHelper, ConstantHelper, test} from "@umbraco/playwright-testhelpers"; +import {ContentBuilder, DocumentTypeBuilder, MediaBuilder, MediaFileBuilder} from "@umbraco/json-models-builders"; +import {BlockGridDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; +import {expect} from "@playwright/test"; + +test.describe('BlockGridEditorContent', () => { + const documentName = 'DocumentTest'; + const blockGridName = 'BlockGridTest'; + const elementName = 'ElementTest'; + + const documentAlias = AliasHelper.toAlias(documentName); + const blockGridAlias = AliasHelper.toAlias(blockGridName); + const elementAlias = AliasHelper.toAlias(elementName); + + test.beforeEach(async ({page, umbracoApi, umbracoUi}, testInfo) => { + await umbracoApi.report.report(testInfo); + await umbracoApi.login(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + }); + + test.afterEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + }); + + test('can create content with a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, null, null); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Adds TestElement + await page.locator('[key="blockEditor_addThis"]', {hasText: elementName}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill('Hi there!'); + await page.locator('[label="Create"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the content was created + await expect(page.locator('.umb-block-grid__block--view')).toHaveCount(1); + await expect(page.locator('.umb-block-grid__block--view').first()).toHaveText(elementName); + }); + + test('can update content with a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const newContentValue = 'UpdatedTitle'; + const newSettingValue = 'UpdatedSetting'; + + const element = await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, null, null, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Updates the already created content text + await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: elementName}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill(newContentValue); + await umbracoUi.clickDataElementByElementName('sub-view-settings'); + // Adds text to the setting element + await page.locator('[id="sub-view-1"]').locator('[id="title"]').fill(newSettingValue); + await page.locator('[label="Submit"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the Content and Setting were updated after it was saved + await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: elementName}).click(); + await expect(page.locator('[id="sub-view-0"]').locator('[id="title"]')).toHaveValue(newContentValue); + await umbracoUi.clickDataElementByElementName('sub-view-settings'); + await expect(page.locator('[id="sub-view-1"]').locator('[id="title"]')).toHaveValue(newSettingValue); + }); + + test('can delete a block grid editor in content', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, null, null, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Deletes the block grid editor inside of the content + await page.getByTitle("Delete").click(); + + // Can't use our constant helper because the action for delete does not contain an s. The correct way is 'action-delete' + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the content is actually deleted + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: elementName})).not.toBeVisible(); + await expect(page.locator('.umb-block-grid__block--view')).toHaveCount(0); + }); + + test('can copy block grid content and paste it', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, null, null, null); + + await umbracoUi.navigateToContent(blockGridName); + // Checks to make sure that there is only one item + await expect(page.locator('.umb-block-grid__block--view')).toHaveCount(1); + + // Copies the block grid content + await page.getByTitle("Copy").click(); + + await expect(page.locator('.alert-success', {hasText: 'Copied to clipboard'})).toBeVisible(); + // Pastes block grid content + await page.getByTitle("Clipboard").click(); + + await page.locator('umb-block-card', {hasText: elementName}).click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + // Checks if there now are two blocks + await expect(page.locator('.umb-block-grid__block')).toHaveCount(2); + // Checks if the text was copied to the pasted block + await page.locator('.umb-block-grid__block--view').nth(1).click(); + await expect(page.locator('[id="sub-view-0"] >> [name="textbox"]')).toHaveValue('aliasTest'); + }); + + test('can copy block grid content and paste it into another group with the same block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const blockGridTwoName = 'BlockGridTwo'; + const blockGridTwoAlias = AliasHelper.toAlias(blockGridTwoName); + + const groupOne = 'BlockGridGroupOne'; + const groupTwo = 'BlockGridGroupTwo'; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataType = await umbracoApi.dataTypes.createDefaultBlockGrid(umbracoApi, blockGridName, element); + + const docType = new DocumentTypeBuilder() + .withName(documentName) + .withAlias(documentAlias) + .withAllowAsRoot(true) + .addGroup() + .withName(groupOne) + .addCustomProperty(dataType['id']) + .withLabel(blockGridName) + .withAlias(blockGridAlias) + .done() + .done() + .addGroup() + .withName(groupTwo) + .addCustomProperty(dataType['id']) + .withLabel(blockGridTwoName) + .withAlias(blockGridTwoAlias) + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(docType); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, true); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks to make sure that there is only one item in the first group + await expect(page.locator('[data-element="group-a' + groupOne + '"] >> .umb-block-grid__block--view')).toHaveCount(1); + // Checks to make sure that there are no items in the second group + await expect(page.locator('[data-element="group-a' + groupTwo + '"] >> .umb-block-grid__block--view')).toHaveCount(0); + + // Copies block grid content from the first group + await page.locator('[title="Copy"]').click(); + await expect(page.locator('.alert-success', {hasText: 'Copied to clipboard'})).toBeVisible(); + // Pastes into the second group + await page.locator('[title="Clipboard"]').nth(1).click(); + await page.locator('umb-block-card', {hasText: elementName}).click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + await expect(page.locator('.alert-success', {hasText: 'Content Published'})).toBeVisible(); + + // Assert + // Checks if both groups has one item each + await expect(page.locator('[data-element="group-a' + groupOne + '"] >> .umb-block-grid__block--view')).toHaveCount(1); + await expect(page.locator('[data-element="group-a' + groupTwo + '"] >> .umb-block-grid__block--view')).toHaveCount(1); + // Opens the second group to see if the block grid item was copied with the correct text + await page.locator('[data-element="group-a' + groupTwo + '"] >> .umb-block-grid__block--view').click(); + await expect(page.locator('[id="sub-view-0"] >> [name="textbox"]')).toHaveValue('aliasTest'); + }); + + test.describe('Moving blocks', () => { + test('can move a block under another block', async ({page, umbracoApi, umbracoUi}) => { + const bottomBlock = "BottomBlock"; + const topBlock = "TopBlock"; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + // We give the dataType a label so we can differentiate between the top and bottom block in the content editor. + const dataType = await umbracoApi.dataTypes.createDefaultBlockGrid(umbracoApi, blockGridName, element, '{{' + element.groups[0].properties[0].alias + '}}'); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, bottomBlock) + .done() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, topBlock) + .done() + .addLayout() + .withContentUdi(element['key'], 0) + .done() + .addLayout() + .withContentUdi(element['key'], 1) + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Drag and Drop + const dragFromLocator = await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: bottomBlock}); + const dragToLocator = await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: topBlock}); + await umbracoUi.dragAndDrop(dragFromLocator, dragToLocator, 10, -5, 15); + + // Assert + // Checks if the BottomBlock is moved to be under TopBlock + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').nth(1)).toContainText(bottomBlock); + }); + + test('can move a block next to another block', async ({page, umbracoApi, umbracoUi}) => { + const leftBlock = "LeftBlock"; + const rightBlock = "RightBlock"; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withLabel('{{' + element.groups[0].properties[0].alias +'}}') + .withContentElementTypeKey(element['key']) + .addColumnSpanOptions(6) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, rightBlock) + .done() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, leftBlock) + .done() + .addLayout() + .withContentUdi(element['key'], 0) + .done() + .addLayout() + .withContentUdi(element['key'], 1) + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Drag and Drop + const dragFromLocator = await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: rightBlock}); + const dragToLocator = await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: leftBlock}); + await umbracoUi.dragAndDrop(dragFromLocator, dragToLocator, -5, 20, 15); + + // Assert + // Checks if the rightBlock is moved to the right side of the blocks + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"]').nth(1)).toContainText(rightBlock); + }); + }); + + test('can create content with a image', async ({page, umbracoApi, umbracoUi}) => { + const imageName = "Umbraco"; + + await umbracoApi.media.ensureNameNotExists(imageName); + + const element = new DocumentTypeBuilder() + .withName(elementName) + .withAlias(elementAlias) + .AsElementType() + .addGroup() + .withName("ImageGroup") + .withAlias('imageGroup') + .addImagePickerProperty() + .withLabel("Image") + .withAlias("image") + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(element); + + await umbracoApi.media.createDefaultImage(imageName); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, null); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .done() + .addLayout() + .withContentUdi(element['key']) + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Selects the created image for the block + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').click(); + await page.locator('[data-element="property-image"]').locator('[key="' + ConstantHelper.buttons.add + '"]').click(); + await page.locator('[data-element="media-grid"] >> [title="' + imageName + '"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.select)); + await page.locator('[label="Submit"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the image was added to the block + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').click(); + await expect(page.locator('.umb-media-card-grid__cell >> [title="' + imageName + '"]')).toBeVisible(); + // Clean + await umbracoApi.media.ensureNameNotExists(imageName); + }); + + test.describe('Amount', () => { + test('can set a minimum of required blocks in content with a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .withMin(2) + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, false); + + await umbracoUi.navigateToContent(blockGridName); + // Checks if there is validation for needing 2 entries or more + await expect(page.locator('[key="validation_entriesShort"]')).toContainText('Minimum 2 entries'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if a validation error is thrown when trying to save when there is not enough blocks + await expect(page.locator('.alert-error')).toBeVisible(); + + // Adds another block + await page.locator('[key="blockEditor_addThis"]', {hasText: elementName}).click(); + await page.locator('[label="Create"]').click(); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.getSuccessNotification(); + // Checks if there are two blocks in the area + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]').locator('umb-block-grid-entry')).toHaveCount(2); + + }); + + test('can set a maximum of required blocks in content with a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .withMax(2) + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, "aliasTest") + .done() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, "aliasTestes") + .done() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, "aliasTester") + .done() + .addLayout() + .withContentUdi(element['key'], 0) + .done() + .addLayout() + .withContentUdi(element['key'], 1) + .done() + .addLayout() + .withContentUdi(element['key'], 2) + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if there is validation for needing 2 entries or less + await expect(page.locator('[key="validation_entriesExceed"]')).toContainText('Maximum 2 entries'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if a validation error is thrown when trying to save when there is too many blocks + await expect(page.locator('.alert-error')).toBeVisible(); + + // Deletes a block + await page.locator('[title="Delete"]').nth(2).click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.getSuccessNotification(); + // Checks if there are two blocks in the area + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]').locator('umb-block-grid-entry')).toHaveCount(2); + }); + }); + + test.describe('Live editing mode', () => { + test('can use live editing mode in content with a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const newText = 'LiveUpdatedContent'; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + // We use the a label so we can see if the block is live updated when content is being written to the element + .withLabel('{{' + element.groups[0].properties[0].alias + '}}') + .withContentElementTypeKey(element['key']) + .withEditorSize('small') + .done() + .withUseLiveEditing(true) + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, false); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if the block contains the correct text before being edited + await expect(page.locator('[data-content-element-type-alias="'+elementAlias +'"]')).toContainText('aliasTest'); + + // Updates the text without saving the changes. + await page.locator('[data-content-element-type-key="' + element['key'] + '"]').click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill(newText); + + // Checks if the block is being live updated as the content is being updated. + await expect(page.locator('[data-content-element-type-alias="'+elementAlias +'"]')).toContainText(newText); + }); + }); + + test.describe('Editor width', () => { + test('can see updated editor width in a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const maxWidth = "40%"; + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .withMaxPropertyWidth(maxWidth) + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, false); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + // Checks if the max-width size is the same as defined in the block grid editor + await expect(page.locator('[data-element="property-' + blockGridAlias + '"] >> [style="max-width: ' + maxWidth + ';"]')).toBeVisible(); + }); + }); + + test.describe('Grid Columns', () => { + test('can see updated grid columns in content', async ({page, umbracoApi, umbracoUi}) => { + const gridColumns = 6; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .withGridColumns(gridColumns) + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, false); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + // Checks if the grid columns are the same as defined in the block grid editor + await expect(page.locator('[data-element="property-' + blockGridAlias + '"] >> [grid-columns="' + gridColumns + '"]')).toBeVisible(); + }); + }); + + test.describe('Layout Stylesheet', () => { + test('can use a layout stylesheet in content', async ({page, umbracoApi, umbracoUi}) => { + const stylesheetName = "StylesheetBlockGrid.css"; + const stylesheetPath = "stylesheet/" + stylesheetName; + const stylesheetMimeType = "text/css"; + + await umbracoApi.media.ensureNameNotExists(stylesheetName); + + const stylesheetData = await umbracoApi.media.createFileWithFile(stylesheetName, stylesheetName, stylesheetPath, stylesheetMimeType); + const stylesheetDataPath = stylesheetData.mediaLink; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .withLayoutStylesheet(stylesheetDataPath) + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + // Checks if the block has the correct template + await expect(page.locator('[data-element="property-'+blockGridAlias+'"]').locator('umb-block-grid-root')).toHaveAttribute('stylesheet', stylesheetDataPath); + + // Clean + await umbracoApi.media.ensureNameNotExists(stylesheetName); + }); + }); + + test.describe('Create Button Label', () => { + test('can see updated create button label in content', async ({page, umbracoApi, umbracoUi}) => { + const newButtonLabel = 'AddTestButton'; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .withCreateLabel(newButtonLabel) + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, false); + + // Goes to the created Content + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + await umbracoUi.clickDataElementByElementName('tree-item-' + blockGridName); + + // Assert + // Checks if the new button label is on the BlockGridEditor + await expect(page.locator('[data-element="property-' + blockGridAlias + '"] >> .umb-block-grid__actions')).toContainText(newButtonLabel); + // Checks if the button label can be clicked + await page.locator('[data-element="property-' + blockGridAlias + '"] >> .umb-block-grid__actions', {hasText: newButtonLabel}).click(); + }); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts new file mode 100644 index 0000000000..d95d39ccaa --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts @@ -0,0 +1,792 @@ +import {AliasHelper, ConstantHelper, test} from "@umbraco/playwright-testhelpers"; +import {ContentBuilder, DocumentTypeBuilder, PartialViewBuilder,} from "@umbraco/json-models-builders"; +import {expect} from "@playwright/test"; +import {BlockGridDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; +import {umbracoConfig} from "@umbraco/playwright-testhelpers/dist/umbraco.config"; + +test.describe('BlockGridEditorRending', () => { + const documentName = 'DocumentTest'; + const blockGridName = 'BlockGridTest'; + const elementTitleName = 'ElementTitle'; + const contentText = "ContentTest"; + + const documentAlias = 'documentTest'; + const blockGridAlias = 'blockGridTest'; + const elementTitleAlias = "elementTitle"; + + const elementTitleLabel = 'Title'; + const elementTitleLabelAlias = AliasHelper.toAlias(elementTitleLabel); + + test.beforeEach(async ({page, umbracoApi, umbracoUi}, testInfo) => { + await umbracoApi.report.report(testInfo); + await umbracoApi.login(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementTitleName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + await umbracoApi.templates.ensureNameNotExists(documentName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementTitleAlias + '.cshtml') + }); + + test.afterEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementTitleName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + await umbracoApi.templates.ensureNameNotExists(documentName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementTitleAlias + '.cshtml') + }); + + async function createElementWithRTE(umbracoApi, elementName, elementAlias, label, labelAlias){ + const element = new DocumentTypeBuilder() + .withName(elementName) + .withAlias(elementAlias) + .AsElementType() + .addGroup() + .withName('TestString') + .withAlias('testString') + .addRichTextProperty() + .withLabel(label) + .withAlias(labelAlias) + .done() + .done() + .build(); + return await umbracoApi.documentTypes.save(element); + } + + async function createDocumentWithTemplateAndDataType(umbracoApi, dataType){ + const docType = new DocumentTypeBuilder() + .withName(documentName) + .withAlias('documentTest') + .withDefaultTemplate('documentTest') + .withAllowAsRoot(true) + .addGroup() + .withName('BlockGridGroup') + .withAlias('blockGridGroup') + .addCustomProperty(dataType['id']) + .withLabel(blockGridName) + .withAlias(blockGridAlias) + .done() + .done() + .build(); + return await umbracoApi.documentTypes.save(docType); + } + + async function editDefaultTemplate(umbracoApi){ + await umbracoApi.templates.edit(documentName, '@using Umbraco.Cms.Web.Common.PublishedModels;\n' + + '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n' + + '@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;' + '\n' + + '@await Html.GetBlockGridHtmlAsync(Model.' + blockGridName + ')'); + } + + async function createPartialViewWithArea(umbracoApi, elementAlias, elementLabelAlias){ + const partialViewElementTitle = new PartialViewBuilder() + .withName(elementAlias) + .withContent('@using Umbraco.Extensions' + '\n' + + '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage' + + '\n' + + '
' + '\n' + + '

@Model.Content.Value("' + elementLabelAlias + '")

' + '\n' + + '
@await Html.GetBlockGridItemAreasHtmlAsync(Model)
' + + '
') + .build(); + partialViewElementTitle.virtualPath = "/Views/Partials/blockgrid/Components/"; + return await umbracoApi.partialViews.save(partialViewElementTitle); + } + + async function createPartialViewWithBlock(umbracoApi,elementAlias, elementLabelAlias){ + const partialViewElementBody = new PartialViewBuilder() + .withName(elementAlias) + .withContent('@using Umbraco.Extensions' + '\n' + + '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage' + + '\n' + + '
' + '\n' + + '

@Model.Content.Value("' + elementLabelAlias + '")

' + '\n' + + '
') + .build(); + partialViewElementBody.virtualPath = "/Views/Partials/blockgrid/Components/"; + await umbracoApi.partialViews.save(partialViewElementBody); + } + + test('can render content with a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await createElementWithRTE(umbracoApi, elementTitleName, elementTitleAlias, elementTitleLabel, elementTitleLabelAlias); + + const dataType = await umbracoApi.dataTypes.createDefaultBlockGrid(umbracoApi, blockGridName, element); + + await createDocumentWithTemplateAndDataType(umbracoApi, dataType); + + await editDefaultTemplate(umbracoApi); + + await createPartialViewWithBlock(umbracoApi, elementTitleAlias, elementTitleLabelAlias); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName('BlockGridContent') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, contentText) + .done() + .addLayout() + .withContentUdi(element['key']) + .done() + .done() + .done() + .done() + .withTemplateAlias(documentName) + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + await page.goto(umbracoConfig.environment.baseUrl); + await expect(page).toHaveScreenshot('Block-grid-editor.png'); + }); + + test('can render content with a block grid editor with two elements', async ({page, umbracoApi, umbracoUi}) => { + // ElementBody + const elementBodyName = "ElementBody"; + const elementBodyAlias = AliasHelper.toAlias(elementBodyName); + const elementBodyLabel = 'Body'; + const elementBodyLabelAlias = AliasHelper.toAlias(elementBodyLabel); + + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + + const element = await createElementWithRTE(umbracoApi, elementTitleName, elementTitleAlias, elementTitleLabel, elementTitleLabelAlias); + const elementBody = await createElementWithRTE(umbracoApi, elementBodyName, elementBodyAlias, elementBodyLabel, elementBodyLabelAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .build(); + const dataType= await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createDocumentWithTemplateAndDataType(umbracoApi, dataType); + + await editDefaultTemplate(umbracoApi); + + // Creates partial view for the ElementTitle + await createPartialViewWithBlock(umbracoApi, elementTitleAlias, elementTitleLabelAlias); + + // Creates partial view for the ElementBody + await createPartialViewWithBlock(umbracoApi, elementBodyAlias, elementBodyLabelAlias); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName('BlockGridContent') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, contentText) + .done() + .addBlockGridEntry() + .withContentTypeKey(elementBody['key']) + .appendContentProperties(elementBody.groups[0].properties[0].alias, 'Lorem Ipsum') + .done() + .addLayout() + .withContentUdi(element['key']) + .done() + .addLayout() + .withContentUdi(elementBody['key']) + .done() + .done() + .done() + .done() + .withTemplateAlias(documentName) + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + await page.goto(umbracoConfig.environment.baseUrl); + await expect(page).toHaveScreenshot('Block-grid-editor-with-two-elements.png'); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + }); + + test.describe('Rendering areas', () => { + test('can render a block grid with an area', async ({page, umbracoApi, umbracoUi}) => { + // ElementBody + const elementBodyName = "ElementBody"; + const elementBodyAlias = AliasHelper.toAlias(elementBodyName); + const elementBodyLabel = 'Body'; + const elementBodyLabelAlias = AliasHelper.toAlias(elementBodyLabel); + + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + + const element = await createElementWithRTE(umbracoApi, elementTitleName, elementTitleAlias, elementTitleLabel, elementTitleLabelAlias); + const elementBody = await createElementWithRTE(umbracoApi, elementBodyName, elementBodyAlias, elementBodyLabel, elementBodyLabelAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias('titleArea') + .done() + .done() + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .build(); + const dataType= await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createDocumentWithTemplateAndDataType(umbracoApi, dataType); + + await editDefaultTemplate(umbracoApi); + + // Creates partial view for the ElementTitle + await createPartialViewWithArea(umbracoApi, elementTitleAlias, elementTitleLabelAlias); + + // Creates partial view for the ElementBody + await createPartialViewWithBlock(umbracoApi, elementBodyAlias, elementBodyLabelAlias); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName('BlockGridContent') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, contentText) + .done() + .addBlockGridEntry() + .withContentTypeKey(elementBody['key']) + .appendContentProperties(elementBody.groups[0].properties[0].alias, 'Lorem Ipsum') + .done() + .addLayout() + .withContentUdi(element['key']) + .addAreas() + .withKey(dataType.preValues[0].value[0].areas[0].key) + .addItems() + .withContentUdi(elementBody['key']) + .done() + .done() + .done() + .done() + .done() + .done() + .withTemplateAlias(documentName) + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + await page.goto(umbracoConfig.environment.baseUrl); + await expect(page).toHaveScreenshot('Block-grid-editor-with-area.png'); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + }) + + test('can render a block grid with multiple areas', async ({page, umbracoApi, umbracoUi}) => { + // ElementBody + const elementBodyName = "ElementBody"; + const elementBodyAlias = AliasHelper.toAlias(elementBodyName); + const elementBodyLabel = 'Body'; + const elementBodyLabelAlias = AliasHelper.toAlias(elementBodyLabel); + + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + + const element = await createElementWithRTE(umbracoApi, elementTitleName, elementTitleAlias, elementTitleLabel, elementTitleLabelAlias); + const elementBody = await createElementWithRTE(umbracoApi, elementBodyName, elementBodyAlias, elementBodyLabel, elementBodyLabelAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias('titleArea') + .done() + .done() + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createDocumentWithTemplateAndDataType(umbracoApi, dataType); + + await editDefaultTemplate(umbracoApi); + + // Creates partial view for the ElementTitle + await createPartialViewWithArea(umbracoApi, elementTitleAlias, elementTitleLabelAlias); + + // Creates partial view for the ElementBody + await createPartialViewWithBlock(umbracoApi, elementBodyAlias, elementBodyLabelAlias); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName('BlockGridContent') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, contentText) + .done() + .addBlockGridEntry() + .withContentTypeKey(elementBody['key']) + .appendContentProperties(elementBody.groups[0].properties[0].alias, 'Lorem ipsum') + .done() + .addBlockGridEntry() + .withContentTypeKey(elementBody['key']) + .appendContentProperties(elementBody.groups[0].properties[0].alias, 'dolor sit amet,') + .done() + .addBlockGridEntry() + .withContentTypeKey(elementBody['key']) + .appendContentProperties(elementBody.groups[0].properties[0].alias, 'consectetuer adipiscing elit,') + .done() + .addBlockGridEntry() + .withContentTypeKey(elementBody['key']) + .appendContentProperties(elementBody.groups[0].properties[0].alias, 'sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.') + .done() + .addLayout() + .withContentUdi(element['key']) + .addAreas() + .withKey(dataType.preValues[0].value[0].areas[0].key) + .addItems() + .withContentUdi(elementBody['key'],0) + .done() + .addItems() + .withContentUdi(elementBody['key'],1) + .done() + .addItems() + .withContentUdi(elementBody['key'],2) + .done() + .addItems() + .withContentUdi(elementBody['key'],3) + .done() + .done() + .done() + .done() + .done() + .done() + .withTemplateAlias(documentName) + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + await page.goto(umbracoConfig.environment.baseUrl); + await expect(page).toHaveScreenshot('Block-grid-editor-with-multiple-areas.png'); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + }); + + test('can render a block grid with an area that has another area', async ({page, umbracoApi, umbracoUi}) => { + // ElementBody + const elementBodyName = "ElementBody"; + const elementBodyAlias = AliasHelper.toAlias(elementBodyName); + const elementBodyLabel = 'Body'; + const elementBodyLabelAlias = AliasHelper.toAlias(elementBodyLabel); + + // ElementFooter + const elementFooterName = "ElementFooter"; + const elementFooterAlias = AliasHelper.toAlias(elementFooterName); + const elementFooterLabel = 'Footer'; + const elementFooterLabelAlias = AliasHelper.toAlias(elementFooterLabel); + + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.documentTypes.ensureNameNotExists(elementFooterName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementFooterAlias + '.cshtml'); + + const element = await createElementWithRTE(umbracoApi, elementTitleName, elementTitleAlias, elementTitleLabel, elementTitleLabelAlias); + const elementBody = await createElementWithRTE(umbracoApi, elementBodyName, elementBodyAlias, elementBodyLabel, elementBodyLabelAlias); + const elementFooter = await createElementWithRTE(umbracoApi, elementFooterName, elementFooterAlias, elementFooterLabel, elementFooterLabelAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias('titleArea') + .done() + .done() + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .addArea() + .withAlias('bodyArea') + .done() + .done() + .addBlock() + .withContentElementTypeKey(elementFooter['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createDocumentWithTemplateAndDataType(umbracoApi, dataType); + + await editDefaultTemplate(umbracoApi); + + // Creates partial view for the ElementTitle + await createPartialViewWithArea(umbracoApi, elementTitleAlias, elementTitleLabelAlias); + + // Creates partial view for the ElementBody + await createPartialViewWithArea(umbracoApi, elementBodyAlias, elementBodyLabelAlias); + + // Creates partial view for the ElementFooter + await createPartialViewWithBlock(umbracoApi, elementFooterAlias, elementFooterLabelAlias); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName('BlockGridContent') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, contentText) + .done() + .addBlockGridEntry() + .withContentTypeKey(elementBody['key']) + .appendContentProperties(elementBody.groups[0].properties[0].alias, 'Lorem ipsum') + .done() + .addBlockGridEntry() + .withContentTypeKey(elementFooter['key']) + .appendContentProperties(elementFooter.groups[0].properties[0].alias, 'dolor sit amet') + .done() + .addLayout() + .withContentUdi(element['key']) + .addAreas() + .withKey(dataType.preValues[0].value[0].areas[0].key) + .addItems() + .withContentUdi(elementBody['key']) + .addAreas() + .withKey(dataType.preValues[0].value[1].areas[0].key) + .addItems() + .withContentUdi(elementFooter['key']) + .done() + .done() + .done() + .done() + .done() + .done() + .done() + .done() + .withTemplateAlias(documentName) + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + await page.goto(umbracoConfig.environment.baseUrl); + await expect(page).toHaveScreenshot('Block-grid-editor-with-area-with-area.png'); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.documentTypes.ensureNameNotExists(elementFooterName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementFooterAlias + '.cshtml'); + }); + }); + + test.describe('Layout Stylesheet', () => { + test('can render a block with a custom layout stylesheet', async ({page, umbracoApi, umbracoUi}) => { + const stylesheetName = "Title.css"; + const stylesheetPath = "stylesheet/" + stylesheetName; + const stylesheetMimeType = "text/css"; + + await umbracoApi.media.ensureNameNotExists(stylesheetName); + + const stylesheetData = await umbracoApi.media.createFileWithFile(stylesheetName, stylesheetName, stylesheetPath, stylesheetMimeType); + const stylesheetDataPath = stylesheetData.mediaLink; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withStylesheet(stylesheetDataPath) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createDocumentWithTemplateAndDataType(umbracoApi, dataType); + + await editDefaultTemplate(umbracoApi); + + const partialViewElement = new PartialViewBuilder() + .withName(elementTitleAlias) + .withContent('@using Umbraco.Extensions' + '\n' + + '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage' + + '\n' + + '
' + '\n' + + '

@Model.Content.Value("' + elementTitleLabelAlias + '")

' + '\n' + + '
' + '\n' + + '') + .build(); + partialViewElement.virtualPath = "/Views/Partials/blockgrid/Components/"; + await umbracoApi.partialViews.save(partialViewElement); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName('BlockGridContent') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, contentText) + .done() + .addLayout() + .withContentUdi(element['key']) + .done() + .done() + .done() + .done() + .withTemplateAlias(documentName) + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + await page.goto(umbracoConfig.environment.baseUrl); + await expect(page).toHaveScreenshot('Block-grid-editor-with-custom-stylesheet.png'); + + // Clean + await umbracoApi.media.ensureNameNotExists(stylesheetName); + }); + + test('can render a block with another area where each block has a different custom layout stylesheet', async ({page, umbracoApi, umbracoUi}) => { + // TitleStylesheet + const stylesheetTitleName = "Title.css"; + const stylesheetTitlePath = "stylesheet/" + stylesheetTitleName; + const stylesheetMimeType = "text/css"; + // BodyStylesheet + const stylesheetBodyName = "Body.css"; + const stylesheetBodyPath = "stylesheet/" + stylesheetBodyName; + // ElementBody + const elementBodyName = "ElementBody"; + const elementBodyAlias = AliasHelper.toAlias(elementBodyName); + + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.media.ensureNameNotExists(stylesheetTitleName); + await umbracoApi.media.ensureNameNotExists(stylesheetBodyName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + + const stylesheetTitleData = await umbracoApi.media.createFileWithFile(stylesheetTitleName, stylesheetTitleName, stylesheetTitlePath, stylesheetMimeType); + const stylesheetBodyData = await umbracoApi.media.createFileWithFile(stylesheetBodyName, stylesheetBodyName, stylesheetBodyPath, stylesheetMimeType); + const stylesheetTitleDataPath = stylesheetTitleData.mediaLink; + const stylesheetBodyDataPath = stylesheetBodyData.mediaLink; + + const elementTitle = await umbracoApi.documentTypes.createDefaultElementType(elementTitleName, elementTitleAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementTitle['key']) + .withStylesheet(stylesheetTitleDataPath) + .addArea() + .withAlias('TitleArea') + .done() + .done() + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .withStylesheet(stylesheetBodyDataPath) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createDocumentWithTemplateAndDataType(umbracoApi, dataType); + + await editDefaultTemplate(umbracoApi); + + const partialViewElementTitle = new PartialViewBuilder() + .withName(elementTitleAlias) + .withContent('@using Umbraco.Extensions' + '\n' + + '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage' + + '\n' + + '
' + '\n' + + '

@Model.Content.Value("' + elementTitleLabelAlias + '")

' + '\n' + + '
' + '\n' + + '' + + '
' + '\n' + + '@await Html.GetBlockGridItemAreasHtmlAsync(Model)'+ '\n' + + '
') + .build(); + partialViewElementTitle.virtualPath = "/Views/Partials/blockgrid/Components/"; + await umbracoApi.partialViews.save(partialViewElementTitle); + + const partialViewElementBody = new PartialViewBuilder() + .withName(elementBodyAlias) + .withContent('@using Umbraco.Extensions' + '\n' + + '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage' + + '\n' + + '
' + '\n' + + '

@Model.Content.Value("' + elementTitleLabelAlias + '")

' + '\n' + + '
' + '\n' + + '') + .build(); + partialViewElementBody.virtualPath = "/Views/Partials/blockgrid/Components/"; + await umbracoApi.partialViews.save(partialViewElementBody); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName('BlockGridContent') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(elementTitle['key']) + .appendContentProperties(elementTitle.groups[0].properties[0].alias, contentText) + .done() + .addBlockGridEntry() + .withContentTypeKey(elementBody['key']) + .appendContentProperties(elementBody.groups[0].properties[0].alias, 'Lorem Ipsum') + .done() + .addLayout() + .withContentUdi(elementTitle['key']) + .addAreas() + .withKey(dataType.preValues[0].value[0].areas[0].key) + .addItems() + .withContentUdi(elementBody['key']) + .done() + .done() + .done() + .done() + .done() + .done() + .withTemplateAlias(documentName) + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + await page.goto(umbracoConfig.environment.baseUrl); + await expect(page).toHaveScreenshot('Block-grid-editor-with-two-custom-stylesheets.png'); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + await umbracoApi.media.ensureNameNotExists(stylesheetTitleName); + await umbracoApi.media.ensureNameNotExists(stylesheetBodyName); + await umbracoApi.partialViews.ensureNameNotExists('blockgrid/Components', elementBodyAlias + '.cshtml'); + }); + }); + + test.describe('Image', () => { + test('can render a block grid with an image', async ({page, umbracoApi, umbracoUi}) => { + // Image + const imageName = "Umbraco"; + const umbracoFileValue = {"src": "Umbraco.png"}; + const imageFileName = "Umbraco.png"; + const imagePath = 'mediaLibrary/' + imageFileName; + const imageMimeType = "image/png"; + + await umbracoApi.media.ensureNameNotExists(imageName); + + const imageData = await umbracoApi.media.createImageWithFile(imageName, umbracoFileValue, imageFileName, imagePath, imageMimeType); + + const element = new DocumentTypeBuilder() + .withName(elementTitleName) + .withAlias(elementTitleAlias) + .AsElementType() + .addGroup() + .withName('ImageGroup') + .withAlias('imageGroup') + .addImagePickerProperty() + .withLabel('Image') + .withAlias('image') + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(element); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await createDocumentWithTemplateAndDataType(umbracoApi, dataType); + + await editDefaultTemplate(umbracoApi); + + const partialViewImage = new PartialViewBuilder() + .withName(elementTitleAlias) + .withContent('@using Umbraco.Extensions' + '\n' + + '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage' + '\n' + + '\n' + + '@{' + '\n' + + 'var typedMediaPickerSingle = Model.Content.Value("image");' + '\n' + + 'Umbraco' + '\n' + + '}') + .build(); + partialViewImage.virtualPath = "/Views/Partials/blockgrid/Components/"; + await umbracoApi.partialViews.save(partialViewImage); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName(blockGridName) + .withSave(true) + .addProperty() + .withAlias(blockGridAlias) + .addBlockGridValue() + .addBlockGridEntry() + .withContentTypeKey(element['key']) + .addImage() + .withMediaKey(imageData.key) + .done() + .done() + .addLayout() + .withContentUdi(element['key']) + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + await page.goto(umbracoConfig.environment.baseUrl); + await expect(page).toHaveScreenshot('Block-grid-editor-with-image.png'); + + // Clean + await umbracoApi.media.ensureNameNotExists(imageName); + }); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-chromium-linux.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-chromium-linux.png new file mode 100644 index 0000000000..364e1ed8ec Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-chromium-linux.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-chromium-win32.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-chromium-win32.png new file mode 100644 index 0000000000..5034356d48 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-chromium-win32.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-chromium-linux.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-chromium-linux.png new file mode 100644 index 0000000000..161e2705e8 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-chromium-linux.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-chromium-win32.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-chromium-win32.png new file mode 100644 index 0000000000..20322de9aa Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-chromium-win32.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-with-area-chromium-linux.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-with-area-chromium-linux.png new file mode 100644 index 0000000000..4ba97a629d Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-with-area-chromium-linux.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-with-area-chromium-win32.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-with-area-chromium-win32.png new file mode 100644 index 0000000000..aa7aea3a67 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-area-with-area-chromium-win32.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-custom-stylesheet-chromium-linux.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-custom-stylesheet-chromium-linux.png new file mode 100644 index 0000000000..95b1bc2969 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-custom-stylesheet-chromium-linux.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-custom-stylesheet-chromium-win32.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-custom-stylesheet-chromium-win32.png new file mode 100644 index 0000000000..cf4a790043 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-custom-stylesheet-chromium-win32.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-image-chromium-linux.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-image-chromium-linux.png new file mode 100644 index 0000000000..8217f96b6d Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-image-chromium-linux.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-image-chromium-win32.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-image-chromium-win32.png new file mode 100644 index 0000000000..8217f96b6d Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-image-chromium-win32.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-multiple-areas-chromium-linux.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-multiple-areas-chromium-linux.png new file mode 100644 index 0000000000..dad19a45ee Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-multiple-areas-chromium-linux.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-multiple-areas-chromium-win32.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-multiple-areas-chromium-win32.png new file mode 100644 index 0000000000..90f139bce4 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-multiple-areas-chromium-win32.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-custom-stylesheets-chromium-linux.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-custom-stylesheets-chromium-linux.png new file mode 100644 index 0000000000..17cdeda521 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-custom-stylesheets-chromium-linux.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-custom-stylesheets-chromium-win32.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-custom-stylesheets-chromium-win32.png new file mode 100644 index 0000000000..dbd8277637 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-custom-stylesheets-chromium-win32.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-elements-chromium-linux.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-elements-chromium-linux.png new file mode 100644 index 0000000000..9cd0b5d3c2 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-elements-chromium-linux.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-elements-chromium-win32.png b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-elements-chromium-win32.png new file mode 100644 index 0000000000..c4d2235a34 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorRendering.spec.ts-snapshots/Block-grid-editor-with-two-elements-chromium-win32.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorSettings.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorSettings.spec.ts new file mode 100644 index 0000000000..358a979a43 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Content/blockGridEditorSettings.spec.ts @@ -0,0 +1,155 @@ +import {AliasHelper, ConstantHelper, test} from "@umbraco/playwright-testhelpers"; +import {BlockGridDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; +import {expect} from "@playwright/test"; +import {ContentBuilder} from "@umbraco/json-models-builders"; + +test.describe('BlockGridEditorSettings', () => { + const documentName = 'DocumentTest'; + const blockGridName = 'BlockGridTest'; + const elementName = 'ElementTitle'; + + const documentAlias = AliasHelper.toAlias(documentName); + const blockGridAlias = AliasHelper.toAlias(blockGridName); + const elementAlias = AliasHelper.toAlias(elementName); + + test.beforeEach(async ({page, umbracoApi, umbracoUi}, testInfo) => { + await umbracoApi.report.report(testInfo); + await umbracoApi.login(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + }); + + test.afterEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.dataTypes.ensureNameNotExists(blockGridName); + }); + + test.describe('General', () => { + test('can see label in content for a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const newLabel = "New Label"; + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withLabel(newLabel + '{{}}') + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + // Checks if the element contains the correct label + await expect(page.locator('[data-content-element-type-alias="' + elementAlias + '"]')).toContainText(newLabel); + // Checks if the element is clickable + await page.locator('[data-content-element-type-alias="' + elementAlias + '"]').click(); + }); + }); + + test.describe('Permissions', () => { + test('can set allow in root to false for a element in a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withAllowAtRoot(false) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.documentTypes.createDefaultDocumentWithBlockGridEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockGridName) + .withSave(true) + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.navigateToContent(blockGridName); + + // Checks if adding a block is disabled + await expect(page.locator('[data-element="property-'+blockGridAlias+'"]').locator('umb-block-grid-root').locator('[disabled="disabled"]')).toBeDisabled(); + // Checks if the button is not clickable + await expect(page.locator('[data-element="property-' + blockGridAlias + '"]').locator('[key="blockEditor_addBlock"]')).not.toBeEnabled(); + }); + + test('can set allow in areas to false for an element in a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const elementBodyName = 'BodyElement'; + const elementBodyAlias = AliasHelper.toAlias(elementBodyName); + + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + const elementBody = await umbracoApi.documentTypes.createDefaultElementType(elementBodyName, elementBodyAlias); + + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(elementBody['key']) + .withAllowInAreas(false) + .done() + .addBlock() + .withContentElementTypeKey(element['key']) + .addArea() + .withAlias('titleArea') + .done() + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi,element,dataType,false); + + await umbracoUi.navigateToContent(blockGridName); + + // Assert + // Checks if the elementTitle is the only element selectable to be in a area + await expect(page.locator('[data-area-alias="titleArea"]').locator('[key="blockEditor_addThis"]')).toContainText('Add ' + elementName); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementBodyName); + }); + }); + + test.describe('Size options', () => { + test('can resize a block to another column span for an element in a block grid editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + const dataTypeBlockGrid = new BlockGridDataTypeBuilder() + .withName(blockGridName) + .addBlock() + .withContentElementTypeKey(element['key']) + .addColumnSpanOptions(12) + .addColumnSpanOptions(6) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockGrid); + + await umbracoApi.content.createDefaultContentWithABlockGridEditor(umbracoApi, element, dataType, null); + + await umbracoUi.navigateToContent(blockGridName); + + // Drags the blocks from a columnSpan of 12 to 6 + const dragFrom = await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: elementName}).locator('[title="Drag to scale"]'); + const dragTo = await page.locator('[data-content-element-type-key="' + element['key'] + '"]', {hasText: elementName}); + await umbracoUi.dragAndDrop(dragFrom,dragTo, 0, 0 ,10); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Checks if the block is resized to a column span of 6 + await expect(page.locator('[data-content-element-type-key="' + element['key'] + '"], [data-col-span="6"]', {hasText: elementName})).toBeVisible(); + }); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Datatype/BlockGridEditorDataTypeBlocks.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Datatype/BlockGridEditorDataTypeBlocks.spec.ts index 36e7fd6d00..2acd818661 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Datatype/BlockGridEditorDataTypeBlocks.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockGridEditor/Datatype/BlockGridEditorDataTypeBlocks.spec.ts @@ -259,7 +259,9 @@ test.describe('BlockGridEditorDataTypeBlock', () => { await umbracoApi.documentTypes.ensureNameNotExists(elementNameThree); }); - test('can create a block grid datatype with multiple groups and multiple element in each group', async ({page, umbracoApi, umbracoUi}) => { + test('can create a block grid datatype with multiple groups and multiple element in each group', async ({page, umbracoApi, umbracoUi},testInfo) => { + await testInfo.slow(); + const GroupOne = 'GroupOne'; const elementNameFourth = 'FourthElement'; const elementFourthAlias = AliasHelper.toAlias(elementNameFourth); @@ -581,7 +583,10 @@ test.describe('BlockGridEditorDataTypeBlock', () => { await umbracoUi.navigateToDataType(blockGridName); // Drags the element from the default group to the 'MoveToHere' Group. - await page.locator('.umb-block-card-group').nth(0).locator('[data-content-element-type-key="' + element['key'] + '"]').dragTo(page.locator('[key="blockEditor_addBlockType"]').nth(1)); + const dragFrom = await page.locator('.umb-block-card-group').nth(0).locator('[data-content-element-type-key="' + element['key'] + '"]'); + const dragTo = await page.locator('[key="blockEditor_addBlockType"]').nth(1); + await umbracoUi.dragAndDrop(dragFrom, dragTo, 0, 0, 15); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); // Assert @@ -618,7 +623,10 @@ test.describe('BlockGridEditorDataTypeBlock', () => { await umbracoUi.navigateToDataType(blockGridName); // Drags the group GroupMove under GroupNotMoving - await page.locator('.umb-block-card-group >> [icon="icon-navigation"]').nth(0).dragTo(page.locator('[key="blockEditor_addBlockType"]').nth(2)); + const dragFrom = await page.locator('.umb-block-card-group >> [icon="icon-navigation"]').nth(0); + const dragTo = await page.locator('[key="blockEditor_addBlockType"]').nth(2); + await umbracoUi.dragAndDrop(dragFrom, dragTo, 0, 0, 15); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); // Assert @@ -632,7 +640,9 @@ test.describe('BlockGridEditorDataTypeBlock', () => { await umbracoUi.doesDataTypeExist(blockGridName); }); - test('can move a group with elements in a block grid editor ', async ({page, umbracoApi, umbracoUi}) => { + test('can move a group with elements in a block grid editor', async ({page, umbracoApi, umbracoUi}, testInfo) => { + await testInfo.slow(); + const GroupMove = 'GroupMove'; const GroupNotMoving = 'GroupNotMoving'; @@ -672,17 +682,12 @@ test.describe('BlockGridEditorDataTypeBlock', () => { await umbracoUi.navigateToDataType(blockGridName); // Drags the group GroupMove under GroupNotMoving - await page.locator('.umb-block-card-group >> [icon="icon-navigation"]').nth(0).hover(); - await page.mouse.down(); - await page.mouse.move(0, -20); - await page.locator('[key="blockEditor_addBlockType"]').nth(2).hover({ - position: { - x: 0, y: 20 - } - }); - await page.mouse.up(); + const dragFrom = await page.locator('.umb-block-card-group >> [icon="icon-navigation"]').nth(0); + const dragTo = await page.locator('[key="blockEditor_addBlockType"]').nth(2); + await umbracoUi.dragAndDrop(dragFrom, dragTo, 20, 0, 15); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); - + // Assert await umbracoUi.isSuccessNotificationVisible(); // Checks if the elements were moved with their group diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDataType.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDataType.spec.ts index 0c2c7d9a8f..0ca492c30f 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDataType.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDataType.spec.ts @@ -4,7 +4,6 @@ import {MediaBuilder, MediaFileBuilder, StylesheetBuilder} from "@umbraco/json-m import {BlockListDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; test.describe('BlockListEditorDataType', () => { - const documentName = 'DocumentName' const blockListName = 'BlockListTest'; const elementName = 'TestElement'; @@ -13,13 +12,11 @@ test.describe('BlockListEditorDataType', () => { test.beforeEach(async ({page, umbracoApi, umbracoUi}, testInfo) => { await umbracoApi.report.report(testInfo); await umbracoApi.login(); - await umbracoApi.documentTypes.ensureNameNotExists(documentName); await umbracoApi.dataTypes.ensureNameNotExists(blockListName); await umbracoApi.documentTypes.ensureNameNotExists(elementName); }); test.afterEach(async({page, umbracoApi, umbracoUi}) => { - await umbracoApi.documentTypes.ensureNameNotExists(documentName); await umbracoApi.dataTypes.ensureNameNotExists(blockListName); await umbracoApi.documentTypes.ensureNameNotExists(elementName); }) @@ -152,7 +149,10 @@ test.describe('BlockListEditorDataType', () => { await expect(page.locator('[block-config-model="block"]')).toHaveCount(1); }); - test('can edit a block list editor', async ({page, umbracoApi, umbracoUi}) => { + test('can edit a block list editor', async ({page, umbracoApi, umbracoUi}, testInfo) => { + // We need to increase the timeout because the test is taking too long to end + await testInfo.slow() + const elementNameTwo = 'SecondElement'; const elementTwoAlias = AliasHelper.toAlias(elementNameTwo); const stylesheetName = 'TestStyleSheet'; @@ -160,7 +160,7 @@ test.describe('BlockListEditorDataType', () => { const imageName = "Umbraco"; const umbracoFileValue = {"src": "Umbraco.png"}; const fileName = "Umbraco.png"; - const path = fileName; + const path = 'mediaLibrary/' + fileName; const mimeType = "image/png"; await umbracoApi.documentTypes.ensureNameNotExists(elementNameTwo); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataTypes/dataTypes.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataTypes/dataTypes.spec.ts index bc9b644820..be7802dca4 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataTypes/dataTypes.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataTypes/dataTypes.spec.ts @@ -120,7 +120,6 @@ test.describe('DataTypes', () => { }); test('Test Url Picker', async ({page, umbracoApi, umbracoUi}) => { - const urlPickerDocTypeName = 'Url Picker Test'; const pickerDocTypeAlias = AliasHelper.toAlias(urlPickerDocTypeName); @@ -155,18 +154,18 @@ test.describe('DataTypes', () => { await page.locator('.umb-tree-root').click({button: "right"}); await page.locator('[data-element="action-create"]').click(); await page.locator('[data-element="action-create-' + pickerDocTypeAlias + '"] > .umb-action-link').click(); - + // Fill out content await umbracoUi.setEditorHeaderName('UrlPickerContent'); await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); await umbracoUi.isSuccessNotificationVisible(); await page.locator('span:has-text("×")').click(); await page.locator('.umb-node-preview-add').click(); - - // Should really try and find a better way to do this, but umbracoTreeItem tries to click the content pane in the background - await page.locator('#treePicker >> [data-element="tree-item-UrlPickerContent"]').click(); - await page.locator('.umb-editor-footer-content__right-side > [button-style="success"] > .umb-button > .btn > .umb-button__content').click(); - await expect(await page.locator('.umb-node-preview__name').first()).toBeVisible(); + + await page.locator('[data-element="editor-container"]').locator('[data-element="tree-item-UrlPickerContent"]').click(); + await expect(page.locator('[alias="urlLinkPicker"]').locator('input[id="urlLinkPicker"]')).toHaveValue('/'); + await page.locator('.umb-editor-footer-content__right-side').locator('[label-key="' + ConstantHelper.buttons.submit + '"]').click(); + await expect(page.locator('.umb-node-preview__name').first()).toBeVisible(); // Save and publish await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Languages/languages.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Languages/languages.spec.ts index e28b0ee341..8e951d8d82 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Languages/languages.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Languages/languages.spec.ts @@ -59,19 +59,19 @@ test.describe('Languages', () => { await expect(doesExistEN).toBe(true); // Delete UK Language - await page.locator('umb-button[label-key="general_delete"]').last().click(); - await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('contentTypeEditor_yesDelete')); + await page.getByRole('button', {name: language2}).locator('[label-key=' + ConstantHelper.buttons.delete + ']').click(); + await umbracoUi.clickDataElementByElementName('button-overlaySubmit'); // Assert the da language still exists and that the uk is deleted // DA doesExistDA = await umbracoApi.languages.exists(language1); await expect(doesExistDA).toBe(true); // EN + await expect(page.getByRole('button', {name: language2})).not.toBeVisible(); doesExistEN = await umbracoApi.languages.exists(language2); await expect(doesExistEN).toBe(false); // Cleanup await umbracoApi.languages.ensureCultureNotExists(language1); - await umbracoApi.languages.ensureCultureNotExists(language2); }); }); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaFiles.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaFiles.spec.ts index 4805cc5af0..18eb51e9b6 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaFiles.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaFiles.spec.ts @@ -14,7 +14,7 @@ test.describe('media File Types', () => { test('create Article', async ({page, umbracoApi, umbracoUi}) => { const articleName = "Article"; const fileName = "Article.pdf"; - const path = fileName; + const path = 'mediaLibrary/' + fileName; const mimeType = "application/pdf"; await umbracoApi.media.ensureNameNotExists(articleName); @@ -32,7 +32,7 @@ test.describe('media File Types', () => { test('create Audio', async ({page, umbracoApi, umbracoUi}) => { const audioName = "Audio"; const fileName = "Audio.mp3"; - const path = fileName; + const path = 'mediaLibrary/' + fileName; const mimeType = "audio/mp3" await umbracoApi.media.ensureNameNotExists(audioName); @@ -50,7 +50,7 @@ test.describe('media File Types', () => { test('create File', async ({page, umbracoApi, umbracoUi}) => { const fileItemName = "File"; const fileName = "File.txt"; - const path = fileName; + const path = 'mediaLibrary/' + fileName; const mimeType = "*/*"; await umbracoApi.media.ensureNameNotExists(fileItemName); @@ -84,7 +84,7 @@ test.describe('media File Types', () => { const imageName = "Umbraco"; const umbracoFileValue = {"src": "Umbraco.png"}; const fileName = "Umbraco.png" - const path = fileName; + const path = 'mediaLibrary/' + fileName; const mimeType = "image/png"; await umbracoApi.media.ensureNameNotExists(imageName); @@ -102,7 +102,7 @@ test.describe('media File Types', () => { test('create VectorGraphics(SVG)', async ({page, umbracoApi, umbracoUi}) => { const vectorGraphicsName = 'VectorGraphics'; const fileName = "VectorGraphics.svg"; - const path = fileName; + const path = 'mediaLibrary/' + fileName; const mimeType = "image/svg+xml"; await umbracoApi.media.ensureNameNotExists(vectorGraphicsName); @@ -120,7 +120,7 @@ test.describe('media File Types', () => { test('create Video', async ({page, umbracoApi, umbracoUi}) => { const videoName = "Video"; const fileName = "Video.mp4"; - const path = fileName; + const path = 'mediaLibrary/' + fileName; const mimeType = "video/mp4"; await umbracoApi.media.ensureNameNotExists(videoName); @@ -367,7 +367,7 @@ test.describe('media File Types', () => { const fileItemNameOld = "File"; const fileItemNameNew = "UpdatedFile"; const fileName = "File.txt"; - const path = fileName; + const path = 'mediaLibrary/' + fileName; const mimeType = "*/*"; await umbracoApi.media.ensureNameNotExists(fileItemNameOld); @@ -389,7 +389,7 @@ test.describe('media File Types', () => { test('Update existing File with new File', async ({page, umbracoApi, umbracoUi}) => { const fileItemName = "File"; const fileName = "File.txt"; - const path = fileName; + const path = 'mediaLibrary/' + fileName; const fileNameNew = "UpdatedFile.txt" const pathNew = "./fixtures/mediaLibrary/" + fileNameNew; const mimeType = "*/*"; diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts index ec9dbb5900..6d0d870e33 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts @@ -7,7 +7,13 @@ test.describe('Media', () => { await umbracoApi.report.report(testInfo); await umbracoApi.login(); await umbracoUi.goToSection(ConstantHelper.sections.media); - await umbracoApi.media.deleteAllMedia() + await umbracoApi.media.deleteAllMedia(); + await umbracoApi.media.clearRecycleBin(); + }); + + test.afterEach(async ({page, umbracoApi, umbracoUi}, testInfo) => { + await umbracoApi.media.deleteAllMedia(); + await umbracoApi.media.clearRecycleBin(); }); test('move one of each Files into a Folder', async ({page, umbracoApi, umbracoUi}) => { @@ -28,10 +34,7 @@ test.describe('Media', () => { {fileTypeNames: vectorGraphicsName}, {fileTypeNames: videoName} ]; - - await umbracoApi.media.deleteAllFiles(articleName,audioName,fileName,folderName,imageName,vectorGraphicsName,videoName); - await umbracoApi.media.ensureNameNotExists(folderToMoveTooName); - + // Action await umbracoApi.media.createAllFileTypes(articleName, audioName, fileName, folderName, imageName, vectorGraphicsName, videoName); await umbracoApi.media.createDefaultFolder(folderToMoveTooName); @@ -60,20 +63,13 @@ test.describe('Media', () => { await expect(page.locator('[data-element="media-grid"]', {hasText: names.fileTypeNames})).toBeVisible(); } await expect(page.locator(".umb-folder-grid", {hasText: folderName})).toBeVisible(); - - // Clean - await umbracoApi.media.deleteAllFiles(articleName, audioName, fileName, folderName, imageName, vectorGraphicsName, videoName); - await umbracoApi.media.ensureNameNotExists(folderToMoveTooName); }); test('sort by Name', async ({page, umbracoApi, umbracoUi}) => { const FolderNameA = 'A'; const FolderNameB = 'B'; const FolderNameC = 'C'; - await umbracoApi.media.ensureNameNotExists(FolderNameA); - await umbracoApi.media.ensureNameNotExists(FolderNameB); - await umbracoApi.media.ensureNameNotExists(FolderNameC); - + // Action await umbracoApi.media.createDefaultFolder(FolderNameC); await umbracoApi.media.createDefaultFolder(FolderNameB); @@ -88,16 +84,10 @@ test.describe('Media', () => { // Assert const item = await page.locator('[ui-sortable="vm.sortableOptions"]').locator("xpath=/*[1]") await expect(item).toContainText(FolderNameA); - - // Clean - await umbracoApi.media.ensureNameNotExists(FolderNameA); - await umbracoApi.media.ensureNameNotExists(FolderNameB); - await umbracoApi.media.ensureNameNotExists(FolderNameC); }); test('search after a specific Folder', async ({page, umbracoApi, umbracoUi}) => { const FolderSearchName = 'SearchMe'; - await umbracoApi.media.ensureNameNotExists(FolderSearchName); // Action await umbracoApi.media.createDefaultFolder(FolderSearchName) @@ -106,16 +96,11 @@ test.describe('Media', () => { // Assert await expect(page.locator(".umb-folder-grid__folder-description", {hasText: FolderSearchName})).toBeVisible(); - - // Clean - await umbracoApi.media.ensureNameNotExists(FolderSearchName); }); test('change Grid to List', async ({page, umbracoApi, umbracoUi}) => { const FolderOneName = 'FolderOne'; const FolderTwoName = 'FolderTwo'; - await umbracoApi.media.ensureNameNotExists(FolderOneName); - await umbracoApi.media.ensureNameNotExists(FolderTwoName); // Action await umbracoApi.media.createDefaultFolder(FolderOneName); @@ -126,17 +111,11 @@ test.describe('Media', () => { // Assert await expect(page.locator('[icon="icon-list"]')).toBeVisible(); - - // Clean - await umbracoApi.media.ensureNameNotExists(FolderOneName); - await umbracoApi.media.ensureNameNotExists(FolderTwoName); }); test('change List to Grid', async ({page, umbracoApi, umbracoUi}) => { const FolderOneName = 'FolderOne'; const FolderTwoName = 'FolderTwo'; - await umbracoApi.media.ensureNameNotExists(FolderOneName); - await umbracoApi.media.ensureNameNotExists(FolderTwoName); // Action await umbracoApi.media.createDefaultFolder(FolderOneName); @@ -150,9 +129,5 @@ test.describe('Media', () => { // Assert await expect(page.locator('[icon="icon-thumbnails-small"]')).toBeVisible(); - - // Clean - await umbracoApi.media.ensureNameNotExists(FolderOneName); - await umbracoApi.media.ensureNameNotExists(FolderTwoName); }); }); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/ModelsBuilder/modelsbuilder.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/ModelsBuilder/modelsbuilder.spec.ts index 49ffc2afd0..445fa2f7a6 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/ModelsBuilder/modelsbuilder.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/ModelsBuilder/modelsbuilder.spec.ts @@ -193,7 +193,9 @@ test.describe('Modelsbuilder tests', () => { await umbracoApi.templates.ensureNameNotExists(docTypeName); }); - test('Can update view and document type', async ({page, umbracoApi, umbracoUi}) => { + test('Can update view and document type', async ({page, umbracoApi, umbracoUi},testInfo) => { + await testInfo.slow(); + const docTypeName = "TestDocument"; const docTypeAlias = AliasHelper.toAlias(docTypeName); const propertyAlias = "title"; @@ -263,7 +265,7 @@ test.describe('Modelsbuilder tests', () => { // We only have to type out the opening tag, the editor adds the closing tag automatically. await editor.type("

@Model.Bod"); await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); - await umbracoUi.isSuccessNotificationVisible({timeout: 10000}); + await umbracoUi.isSuccessNotificationVisible({timeout: 20000}); await page.locator('span:has-text("×")').click(); // Navigate to the content section and update the content @@ -273,6 +275,8 @@ test.describe('Modelsbuilder tests', () => { await page.locator("#bod").type("Fancy body text"); await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + await page.waitForTimeout(2000); + await umbracoApi.content.verifyRenderedContent("/", "

" + propertyValue + "

Fancy body text

", true); await umbracoApi.content.deleteAllContent(); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Packages/packages.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Packages/packages.spec.ts index f08a48b8fd..5506cc7ddd 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Packages/packages.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Packages/packages.spec.ts @@ -79,6 +79,7 @@ test.describe('Packages', () => { // Fill out package creation form // Waits until the element package Content is visible await page.locator('[key="packager_packageContent"]').isVisible(); + await expect(page.locator('[label="@treeHeaders_documentTypes"]').locator('[text="' + rootDocTypeName + '"]')).toBeVisible(); await page.locator("#headerName").type(packageName); await page.locator('.controls > .umb-node-preview-add').click(); await page.locator('.umb-tree-item__label').first().click(); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViews.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViews.spec.ts index 12343a66de..1ae2768156 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViews.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViews.spec.ts @@ -23,7 +23,7 @@ test.describe('Partial Views', () => { const name = "TestPartialView"; const fileName = name + ".cshtml"; - await umbracoApi.partialViews.ensureNameNotExists(fileName); + await umbracoApi.partialViews.ensureNameNotExists('', fileName); await openPartialViewsCreatePanel(page, umbracoUi); @@ -31,23 +31,23 @@ test.describe('Partial Views', () => { await page.locator('.menu-label localize[key="create_newEmptyPartialView"]').click(); //Type name - await umbracoUi.setEditorHeaderName(name); + await page.locator('[data-element="editor-name-field"]').type(name); //Save await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); //Assert - await umbracoUi.isSuccessNotificationVisible({timeout:10000}); + await umbracoUi.isSuccessNotificationVisible({timeout: 20000}); //Clean up - await umbracoApi.partialViews.ensureNameNotExists(fileName); + await umbracoApi.partialViews.ensureNameNotExists('', fileName); }); test('Create partial view from snippet', async ({page, umbracoApi, umbracoUi}) => { const name = "TestPartialViewFromSnippet"; const fileName = name + ".cshtml"; - await umbracoApi.partialViews.ensureNameNotExists(fileName); + await umbracoApi.partialViews.ensureNameNotExists('', fileName); await openPartialViewsCreatePanel(page, umbracoUi); @@ -63,10 +63,10 @@ test.describe('Partial Views', () => { await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); // Assert - await umbracoUi.isSuccessNotificationVisible({timeout:10000}); + await umbracoUi.isSuccessNotificationVisible({timeout:20000}); // Clean up - await umbracoApi.partialViews.ensureNameNotExists(fileName); + await umbracoApi.partialViews.ensureNameNotExists('', fileName); }); test('Partial view with no name', async ({page, umbracoApi, umbracoUi}) => { @@ -90,7 +90,7 @@ test.describe('Partial Views', () => { const name = "TestDeletePartialView"; const fileName = name + ".cshtml"; - await umbracoApi.partialViews.ensureNameNotExists(fileName); + await umbracoApi.partialViews.ensureNameNotExists('', fileName); // Build and save partial view const partialView = new PartialViewBuilder() @@ -111,14 +111,14 @@ test.describe('Partial Views', () => { await expect(await page.locator("body", { hasText: fileName})).not.toBeVisible(); // Clean - await umbracoApi.partialViews.ensureNameNotExists(fileName); + await umbracoApi.partialViews.ensureNameNotExists('', fileName); }); test('Edit partial view', async ({page, umbracoApi, umbracoUi}) => { const name = 'EditPartialView'; const fileName = name + ".cshtml"; - await umbracoApi.partialViews.ensureNameNotExists(fileName); + await umbracoApi.partialViews.ensureNameNotExists('', fileName); const partialView = new PartialViewBuilder() .withName(name) @@ -135,9 +135,9 @@ test.describe('Partial Views', () => { await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); // Assert - await umbracoUi.isSuccessNotificationVisible({timeout:10000}); + await umbracoUi.isSuccessNotificationVisible({timeout:20000}); // Clean - await umbracoApi.partialViews.ensureNameNotExists(fileName); + await umbracoApi.partialViews.ensureNameNotExists('', fileName); }); });