diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index ecf10b8854..0681900d88 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,11 +4,11 @@ contact_links: url: https://github.com/umbraco/Umbraco-CMS/discussions/new?category=features-and-ideas about: Start a new discussion when you have ideas or feature requests, eventually discussions can turn into plans - name: ⁉️ Support Question - url: https://our.umbraco.com + url: https://forum.umbraco.com about: This issue tracker is NOT meant for support questions. If you have a question, please join us on the forum. - name: 📖 Documentation Issue url: https://github.com/umbraco/UmbracoDocs/issues about: Documentation issues should be reported on the Umbraco documentation repository. - name: 🔐 Security Issue - url: https://umbraco.com/about-us/trust-center/security-and-umbraco/how-to-report-a-vulnerability-in-umbraco/ + url: https://umbraco.com/trust-center/security-and-umbraco/how-to-report-a-vulnerability-in-umbraco/ about: Discovered a Security Issue in Umbraco? diff --git a/.github/README.md b/.github/README.md index f4e3f76009..63b908daed 100644 --- a/.github/README.md +++ b/.github/README.md @@ -4,8 +4,8 @@ [![NuGet Version](https://img.shields.io/nuget/v/Umbraco.Cms)](https://www.nuget.org/packages/Umbraco.Cms) [![Build status](https://img.shields.io/azure-devops/build/umbraco/Umbraco%2520Cms/301?logo=azurepipelines&label=Azure%20Pipelines)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=301) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) +[![Forum](https://img.shields.io/badge/help-forum-blue)](https://forum.umbraco.com) [![Chat about Umbraco on Discord](https://img.shields.io/discord/869656431308189746?logo=discord&logoColor=fff)](https://discord.gg/umbraco) -[![Read what's going on in the Umbraco Discord chat now](https://img.shields.io/badge/read-discord-blue)](https://discord-chats.umbraco.com) ![Mastodon Follow](https://img.shields.io/mastodon/follow/110661369750014952?domain=https%3A%2F%2Fumbracocommunity.social) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index a33ee744f7..92621c30d3 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -432,6 +432,33 @@ stages: displayName: Start SQL Server Docker image (Linux) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + - powershell: | + $maxAttempts = 12 + $attempt = 0 + $status = "" + + while (($status -ne 'running') -and ($attempt -lt $maxAttempts)) { + Start-Sleep -Seconds 5 + # We use the docker inspect command to check the status of the container. If the container is not running, we wait 5 seconds and try again. And if reaches 12 attempts, we fail the build. + $status = docker inspect -f '{{.State.Status}}' mssql + + if ($status -ne 'running') { + Write-Host "Waiting for SQL Server to be ready... Attempt $($attempt + 1)" + $attempt++ + } + } + + if ($status -eq 'running') { + Write-Host "SQL Server container is running" + docker ps -a + } else { + Write-Host "SQL Server did not become ready in time. Last known status: $status" + docker logs mssql + exit 1 + } + displayName: Wait for SQL Server to be ready (Linux) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + - pwsh: SqlLocalDB start MSSQLLocalDB displayName: Start SQL Server LocalDB (Windows) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) diff --git a/build/nightly-E2E-test-pipelines.yml b/build/nightly-E2E-test-pipelines.yml index f692360cc1..17b6f1c4db 100644 --- a/build/nightly-E2E-test-pipelines.yml +++ b/build/nightly-E2E-test-pipelines.yml @@ -291,17 +291,17 @@ stages: testCommand: "npm run testSqlite -- --shard=1/3" vmImage: "ubuntu-latest" SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) - CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" + CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" LinuxPart2Of3: testCommand: "npm run testSqlite -- --shard=2/3" vmImage: "ubuntu-latest" SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) - CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" + CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" LinuxPart3Of3: testCommand: "npm run testSqlite -- --shard=3/3" vmImage: "ubuntu-latest" SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) - CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" + CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" WindowsPart1Of3: vmImage: "windows-latest" testCommand: "npm run testSqlite -- --shard=1/3" @@ -457,4 +457,4 @@ stages: testResultsFormat: 'JUnit' testResultsFiles: '*.xml' searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" - testRunTitle: "$(Agent.JobName)" \ No newline at end of file + testRunTitle: "$(Agent.JobName)" diff --git a/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs b/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs index 888b1dfdf2..d11e08bad3 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs @@ -1,4 +1,7 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -12,14 +15,27 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase _logger; + /// /// Initializes a new instance of the class. /// /// The items. + [Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")] public DataValueReferenceFactoryCollection(Func> items) - : base(items) + : this( + items, + StaticServiceProvider.Instance.GetRequiredService>()) { } + /// + /// Initializes a new instance of the class. + /// + /// The items. + /// The logger. + public DataValueReferenceFactoryCollection(Func> items, ILogger logger) + : base(items) => _logger = logger; + /// /// Gets all unique references from the specified properties. /// @@ -33,7 +49,7 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase(); // Group by property editor alias to avoid duplicate lookups and optimize value parsing - foreach (var propertyValuesByPropertyEditorAlias in properties.GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Values)) + foreach (IGrouping> propertyValuesByPropertyEditorAlias in properties.GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Values)) { if (!propertyEditors.TryGet(propertyValuesByPropertyEditorAlias.Key, out IDataEditor? dataEditor)) { @@ -48,7 +64,7 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase public ISet GetReferences(IDataEditor dataEditor, IEnumerable values) => - GetReferencesEnumerable(dataEditor, values).ToHashSet(); - private IEnumerable GetReferencesEnumerable(IDataEditor dataEditor, IEnumerable values) + GetReferencesEnumerable(dataEditor, values, null).ToHashSet(); + + private ISet GetReferences(IDataEditor dataEditor, IEnumerable values, string propertyEditorAlias) => + GetReferencesEnumerable(dataEditor, values, propertyEditorAlias).ToHashSet(); + + private IEnumerable GetReferencesEnumerable(IDataEditor dataEditor, IEnumerable values, string? propertyEditorAlias) { // TODO: We will need to change this once we support tracking via variants/segments // for now, we are tracking values from ALL variants if (dataEditor.GetValueEditor() is IDataValueReference dataValueReference) { - foreach (UmbracoEntityReference reference in values.SelectMany(dataValueReference.GetReferences)) + foreach (UmbracoEntityReference reference in GetReferencesFromPropertyValues(values, dataValueReference, propertyEditorAlias)) { yield return reference; } @@ -107,6 +127,38 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase GetReferencesFromPropertyValues(IEnumerable values, IDataValueReference dataValueReference, string? propertyEditorAlias) + { + var result = new List(); + foreach (var value in values) + { + // When property editors on data types are changed, we could have values that are incompatible with the new editor. + // Leading to issues such as: + // - https://github.com/umbraco/Umbraco-CMS/issues/17628 + // - https://github.com/umbraco/Umbraco-CMS/issues/17725 + // Although some changes like this are not intended to be compatible, we should handle them gracefully and not + // error in retrieving references, which would prevent manipulating or deleting the content that uses the data type. + try + { + IEnumerable references = dataValueReference.GetReferences(value); + result.AddRange(references); + } + catch (Exception ex) + { + // Log the exception but don't throw, continue with the next value. + _logger.LogError( + ex, + "Error getting references from value {Value} with data editor {DataEditor} and property editor alias {PropertyEditorAlias}.", + value, + dataValueReference.GetType().FullName, + propertyEditorAlias ?? "n/a"); + throw; + } + } + + return result; + } + /// /// Gets all relation type aliases that are automatically tracked. /// @@ -114,6 +166,10 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase /// All relation type aliases that are automatically tracked. /// + [Obsolete("Use GetAllAutomaticRelationTypesAliases. This will be removed in Umbraco 15.")] + public ISet GetAutomaticRelationTypesAliases(PropertyEditorCollection propertyEditors) => + GetAllAutomaticRelationTypesAliases(propertyEditors); + public ISet GetAllAutomaticRelationTypesAliases(PropertyEditorCollection propertyEditors) { // Always add default automatic relation types diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs index a8fa01de69..3777d84eb4 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs @@ -71,17 +71,17 @@ public abstract class BlockValuePropertyValueEditorBase : DataV continue; } - var districtValues = valuesByPropertyEditorAlias.Distinct().ToArray(); + var distinctValues = valuesByPropertyEditorAlias.Distinct().ToArray(); if (dataEditor.GetValueEditor() is IDataValueReference reference) { - foreach (UmbracoEntityReference value in districtValues.SelectMany(reference.GetReferences)) + foreach (UmbracoEntityReference value in distinctValues.SelectMany(reference.GetReferences)) { result.Add(value); } } - IEnumerable references = _dataValueReferenceFactoryCollection.GetReferences(dataEditor, districtValues); + IEnumerable references = _dataValueReferenceFactoryCollection.GetReferences(dataEditor, distinctValues); foreach (UmbracoEntityReference value in references) { diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockGrid/ComplexBlockGridTest.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockGrid/ComplexBlockGridTest.spec.ts new file mode 100644 index 0000000000..78b1ea1213 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockGrid/ComplexBlockGridTest.spec.ts @@ -0,0 +1,134 @@ +import {expect} from '@playwright/test'; +import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +// DocumentType +const documentTypeName = 'TestDocumentType'; +let documentTypeId = ''; +const groupName = 'TestGroup'; + +// Content +const contentName = 'TestContent'; +let contentId = ''; + +// Property Value +const wrongPropertyValue = 'This is a test with wrong value**'; +const correctPropertyValue = 'Test'; + +// ElementTypes +// TextString Element Type (for Block List) +const textStringElementGroupName = 'TextStringElementGroup'; +const textStringElementTypeName = 'TestElementWithTextString'; +const textStringElementRegex = '^[a-zA-Z0-9]*$'; +let textStringElementTypeId = ''; +// Area Element Type (for Block Grid) +const areaElementTypeName = 'TestElementArea'; +const areaAlias = 'testArea'; +let areaElementTypeId = ''; +// Rich Text Editor Element Type (for Block Grid) +const richTextEditorElementGroupName = 'RichTextEditorElementGroup'; +const richTextEditorElementTypeName = 'RichTextEditorTestElement'; +let richTextEditorElementTypeId = ''; +// Block List Element Type +const blockListElementTypeName = 'BlockListElement'; +const blockListGroupName = 'BlockListGroup'; +let blockListElementTypeId = ''; + +// DataTypes +const blockGridDataTypeName = 'TestBlockGridEditor'; +const blockListDataTypeName = 'TestBlockListEditor'; +const textStringElementDataTypeName = 'Textstring'; +const richTextDataTypeName = 'Rich Text Editor'; +let blockListDataTypeId = ''; +let blockGridDataTypeId = ''; + +test.beforeEach(async ({umbracoApi}) => { + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(areaElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(richTextEditorElementTypeName); + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName); + await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName); + await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName); +}); + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(areaElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(richTextEditorElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName); + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName); + await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName); + await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName); +}); + +test('can update property value nested in a block grid area with an RTE with a block list editor', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => { + test.slow(); + // Arrange + // ElementType with Textstring And REGEX only accept letters and numbers + const textStringElementDataType = await umbracoApi.dataType.getByName(textStringElementDataTypeName); + textStringElementTypeId = await umbracoApi.documentType.createElementTypeWithRegexValidation(textStringElementTypeName, textStringElementGroupName, textStringElementDataTypeName, textStringElementDataType.id, textStringElementRegex); + // Block List Editor with Textstring + blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, textStringElementTypeId); + // ElementType with Block List Editor + blockListElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockListElementTypeName, blockListGroupName, blockListDataTypeName, blockListDataTypeId); + // Rich Text Editor in an ElementType, with a Block(Element Type), the block contains a Block List Editor + const richTextEditorId = await umbracoApi.dataType.createRichTextEditorWithABlock(richTextDataTypeName, blockListElementTypeId); + richTextEditorElementTypeId = await umbracoApi.documentType.createDefaultElementType(richTextEditorElementTypeName, richTextEditorElementGroupName, richTextDataTypeName, richTextEditorId); + // ElementType Area that is Empty + areaElementTypeId = await umbracoApi.documentType.createEmptyElementType(areaElementTypeName); + // Block Grid with 2 blocks, one with RTE and Inline, and one with areas + blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockWithInlineEditingModeAndABlockWithAnArea(blockGridDataTypeName, richTextEditorElementTypeId, true, areaElementTypeId, areaAlias); + // Document Type with the following + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridDataTypeName, blockGridDataTypeId, groupName); + // Creates Content + contentId = await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.clickAddBlockGridElementWithName(areaElementTypeName); + await umbracoUi.content.clickSelectBlockElementWithName(areaElementTypeName); + await umbracoUi.content.clickAddBlockGridElementWithName(richTextEditorElementTypeName); + await umbracoUi.content.clickExactLinkWithName(richTextEditorElementTypeName); + await umbracoUi.content.clickInsertBlockButton(); + await umbracoUi.content.clickExactLinkWithName(blockListElementTypeName); + await umbracoUi.content.clickAddBlockGridElementWithName(textStringElementTypeName); + await umbracoUi.content.clickExactLinkWithName(textStringElementTypeName); + // Enter text in the textstring block that won't match regex + await umbracoUi.content.enterPropertyValue(textStringElementDataTypeName, wrongPropertyValue); + await umbracoUi.content.clickCreateButtonForModalWithElementTypeNameAndGroupName(textStringElementTypeName, textStringElementGroupName); + await umbracoUi.content.clickCreateButtonForModalWithElementTypeNameAndGroupName(blockListElementTypeName, blockListGroupName); + await umbracoUi.content.clickCreateButtonForModalWithElementTypeNameAndGroupName(richTextEditorElementTypeName, richTextEditorElementGroupName); + await umbracoUi.content.clickSaveAndPublishButton(); + // Checks that the error notification is shown since the textstring block has the wrong value + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved, true, true); + await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished, true, true); + // Updates the textstring block with the correct value + await umbracoUi.waitForTimeout(1000); + await umbracoUi.content.clickBlockElementWithName(blockListElementTypeName); + await umbracoUi.content.clickEditBlockListEntryWithName(textStringElementTypeName); + await umbracoUi.content.enterPropertyValue(textStringElementDataTypeName, correctPropertyValue); + await umbracoUi.content.clickUpdateButtonForModalWithElementTypeNameAndGroupName(textStringElementTypeName, textStringElementGroupName); + await umbracoUi.content.clickUpdateButtonForModalWithElementTypeNameAndGroupName(blockListElementTypeName, blockListGroupName); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved, true, true); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published, true, true); + // Checks if published + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.variants[0].state).toBe('Published'); + // Checks if the textstring block has the correct value after reloading the page + await umbracoUi.reloadPage(); + // Waits to make sure the page has loaded + await umbracoUi.waitForTimeout(2000); + await umbracoUi.content.clickBlockElementWithName(blockListElementTypeName); + // Needs to wait to make sure it has loaded + await umbracoUi.waitForTimeout(2000); + await umbracoUi.content.clickEditBlockListEntryWithName(textStringElementTypeName); + await umbracoUi.content.doesPropertyContainValue(textStringElementDataTypeName, correctPropertyValue); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockGrid/SecondLevelBlockProperties.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockGrid/SecondLevelBlockProperties.spec.ts new file mode 100644 index 0000000000..292d7c96d3 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockGrid/SecondLevelBlockProperties.spec.ts @@ -0,0 +1,161 @@ +import {AliasHelper, ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +// Content Name +const contentName = 'ContentName'; + +// Document Type +const documentTypeName = 'DocumentTypeName'; +let documentTypeId = null; +const documentTypeGroupName = 'DocumentGroup'; + +// Block Grid +const blockGridDataTypeName = 'BlockGridName'; +let blockGridDataTypeId = null; + +// Text String +const textStringElementTypeName = 'TextStringElementName'; +let textStringElementTypeId = null; +let textStringGroupName = 'TextGroup'; +const textStringDataTypeName = 'Textstring'; + +test.beforeEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName); +}); + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName); +}); + +test('can publish a block grid editor with a rich text editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const richTextEditorValue = 'Hello World'; + const expectedRichTextEditorOutputValue = '

Hello World

'; + const richTextDataTypeName = 'RichTextDataTypeName'; + const richTextElementTypeName = 'RichTextElementName'; + const richTextElementGroupName = 'RichTextElementGroupName'; + await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(richTextElementTypeName); + + const richTextEditorDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(richTextDataTypeName); + const richTextElementTypeId = await umbracoApi.documentType.createDefaultElementType(richTextElementTypeName, richTextElementGroupName, richTextDataTypeName, richTextEditorDataTypeId); + blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridDataTypeName, richTextElementTypeId, true); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridDataTypeName, blockGridDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.clickAddBlockElementButton(); + await umbracoUi.content.clickBlockCardWithName(richTextElementTypeName, true); + await umbracoUi.content.enterRTETipTapEditor(richTextEditorValue); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the RTE is as expected + const documentData = await umbracoApi.document.getByName(contentName); + const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockGridDataTypeName)); + expect(documentValues.value.contentData[0].values[0].value.markup).toContain(expectedRichTextEditorOutputValue); + + // Clean + await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(richTextElementTypeName); +}); + +test('can publish a block grid editor with a block list editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const textStringValue = 'Hello World'; + const blockListDataTypeName = 'BlockListName'; + const blockListElementTypeName = 'BlockListElementName'; + const blockListElementGroupName = 'BlockListElementGroupName'; + await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName); + + const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName); + textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id); + const blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, textStringElementTypeId); + const blockListElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockListElementTypeName, blockListElementGroupName, blockListDataTypeName, blockListDataTypeId); + blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridDataTypeName, blockListElementTypeId, true); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridDataTypeName, blockGridDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.clickAddBlockElementButton(); + await umbracoUi.content.clickBlockCardWithName(blockListElementTypeName, true); + await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName); + await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true); + await umbracoUi.content.enterTextstring(textStringValue); + await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the BlockList is as expected + const documentData = await umbracoApi.document.getByName(contentName); + const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockGridDataTypeName)); + expect(documentValues.value.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue); + + // Clean + await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName); +}); + +test('can publish a block grid editor with a block grid editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const textStringValue = 'Hello World'; + const secondBlockGridDataTypeName = 'SecondBlockGridDataTypeName'; + const blockGridElementTypeName = 'BlockGridElementTypeName'; + const blockGridElementGroupName = 'BlockGridElementGroupName'; + await umbracoApi.dataType.ensureNameNotExists(secondBlockGridDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName); + + const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName); + textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id); + const secondBlockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(secondBlockGridDataTypeName, textStringElementTypeId); + const blockGridElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockGridElementTypeName, blockGridElementGroupName, secondBlockGridDataTypeName, secondBlockGridDataTypeId); + blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridDataTypeName, blockGridElementTypeId, true); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridDataTypeName, blockGridDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.clickAddBlockElementButton(); + await umbracoUi.content.clickBlockCardWithName(blockGridElementTypeName, true); + await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName); + await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true); + await umbracoUi.content.enterTextstring(textStringValue); + await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the BlockGrid is as expected + const documentData = await umbracoApi.document.getByName(contentName); + const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockGridDataTypeName)); + expect(documentValues.value.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue); + + // Clean + await umbracoApi.dataType.ensureNameNotExists(secondBlockGridDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockList/SecondLevelBlockProperties.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockList/SecondLevelBlockProperties.spec.ts new file mode 100644 index 0000000000..ac3dd0aefd --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/BlockList/SecondLevelBlockProperties.spec.ts @@ -0,0 +1,161 @@ +import {AliasHelper, ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +// Content Name +const contentName = 'ContentName'; + +// Document Type +const documentTypeName = 'DocumentTypeName'; +let documentTypeId = null; +const documentTypeGroupName = 'DocumentGroup'; + +// Block List +const blockListDataTypeName = 'BlockListName'; +let blockListDataTypeId = null; + +// Text String +const textStringElementTypeName = 'TextStringElementName'; +let textStringElementTypeId = null; +let textStringGroupName = 'TextGroup'; +const textStringDataTypeName = 'Textstring'; + +test.beforeEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName); +}); + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName); +}); + +test('can publish a block list editor with a rich text editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const richTextEditorValue = 'Hello World'; + const expectedRichTextEditorOutputValue = '

Hello World

'; + const richTextDataTypeName = 'RichTextName'; + const richTextElementTypeName = 'RichTextElementName'; + const richTextElementGroupName = 'RTEElementGroup'; + await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(richTextElementTypeName); + + const richTextEditorDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(richTextDataTypeName); + const richTextElementTypeId = await umbracoApi.documentType.createDefaultElementType(richTextElementTypeName, richTextElementGroupName, richTextDataTypeName, richTextEditorDataTypeId); + blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, richTextElementTypeId); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListDataTypeName, blockListDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.clickAddBlockElementButton(); + await umbracoUi.content.clickBlockCardWithName(richTextElementTypeName, true); + await umbracoUi.content.enterRTETipTapEditor(richTextEditorValue); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the RTE is as expected + const documentData = await umbracoApi.document.getByName(contentName); + const documentRichTextValues = documentData.values[0].value.contentData[0].values.find(value => value.alias === AliasHelper.toAlias(richTextDataTypeName)); + expect(documentRichTextValues.value.markup).toContain(expectedRichTextEditorOutputValue); + + // Clean + await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(richTextElementTypeName); +}); + +test('can publish a block list editor with a block grid editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const textStringValue = 'Hello World'; + const blockGridDataTypeName = 'BlockGridDataTypeName'; + const blockGridElementTypeName = 'BlockGridElementName'; + const blockGridElementGroupName = 'GridElementGroup'; + await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + + const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName); + textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id); + const blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridDataTypeName, textStringElementTypeId); + const blockGridElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockGridElementTypeName, blockGridElementGroupName, blockGridDataTypeName, blockGridDataTypeId); + blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, blockGridElementTypeId); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListDataTypeName, blockListDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.clickAddBlockElementButton(); + await umbracoUi.content.clickBlockCardWithName(blockGridElementTypeName, true); + await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName); + await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true); + await umbracoUi.content.enterTextstring(textStringValue); + await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the BlockGrid is as expected + const documentData = await umbracoApi.document.getByName(contentName); + const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockListDataTypeName)); + expect(documentValues.value.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue); + + // Clean + await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeId); +}); + +test('can publish a block list editor with a block list editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const textStringValue = 'Hello World'; + const secondBlockListDataTypeName = 'SecondBlockListName'; + const blockListElementTypeName = 'BlockListElementName'; + const blockListElementGroupName = 'ListElementGroup'; + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + await umbracoApi.dataType.ensureNameNotExists(secondBlockListDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName); + + const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName); + textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id); + const secondBlockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(secondBlockListDataTypeName, textStringElementTypeId); + const blockListElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockListElementTypeName, blockListElementGroupName, secondBlockListDataTypeName, secondBlockListDataTypeId); + blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, blockListElementTypeId); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListDataTypeName, blockListDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.clickAddBlockElementButton(); + await umbracoUi.content.clickBlockCardWithName(blockListElementTypeName, true); + await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName); + await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true); + await umbracoUi.content.enterTextstring(textStringValue); + await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the BlockList is as expected + const documentData = await umbracoApi.document.getByName(contentName); + const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockListDataTypeName)); + expect(documentValues.value.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue); + + // Clean + await umbracoApi.dataType.ensureNameNotExists(secondBlockListDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/ContentWithTiptap.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/ContentWithTiptap.spec.ts index 642b7caf3d..3d3b09d7ef 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/ContentWithTiptap.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/ContentWithTiptap.spec.ts @@ -81,7 +81,7 @@ test('can publish content with RTE Tiptap property editor', async ({umbracoApi, await umbracoUi.content.clickSaveAndPublishButton(); // Assert - //await umbracoUi.content.doesSuccessNotificationsHaveCount(2); + //await umbracoUi.content.doesSuccessNotificationsHaveCount(2); await umbracoUi.content.isErrorNotificationVisible(false); expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy(); const contentData = await umbracoApi.document.getByName(contentName); @@ -222,4 +222,4 @@ test.skip('can insert a link to an unpublished document in RTE Tiptap property e // Clean await umbracoApi.documentType.ensureNameNotExists(documentTypeForLinkedDocumentName); await umbracoApi.document.ensureNameNotExists(linkedDocumentName); -}); \ No newline at end of file +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/SecondLevelBlockProperties.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/SecondLevelBlockProperties.spec.ts new file mode 100644 index 0000000000..63179d576a --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/SecondLevelBlockProperties.spec.ts @@ -0,0 +1,165 @@ +import {AliasHelper, ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +// Content Name +const contentName = 'ContentName'; + +// Document Type +const documentTypeName = 'DocumentTypeName'; +let documentTypeId = null; +const documentTypeGroupName = 'DocumentGroup'; + +// Rich Text Editor +const richTextDataTypeName = 'RichTextDataType'; +let richTextDataTypeId = null; + +// Text String +const textStringElementTypeName = 'TextStringElementName'; +let textStringElementTypeId = null; +let textStringGroupName = 'TextGroup'; +const textStringDataTypeName = 'Textstring'; + +test.beforeEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName); +}); + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName); +}); + +test('can publish a rich text editor with a rich text editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const richTextEditorValue = 'Hello First World'; + const secondRichTextEditorValue = 'Hello Second World'; + const expectedRichTextEditorOutputValue = '

' + richTextEditorValue + '

'; + const secondExpectedRichTextEditorOutputValue = '

' + secondRichTextEditorValue + '

'; + const secondRichTextDataTypeName = 'SecondRichTextName'; + const richTextElementTypeName = 'RichTextElementName'; + const richTextElementGroupName = 'RichTextElementGroupName'; + await umbracoApi.dataType.ensureNameNotExists(secondRichTextDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(richTextElementGroupName); + + const secondRichTextEditorDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(secondRichTextDataTypeName); + const richTextElementTypeId = await umbracoApi.documentType.createDefaultElementType(richTextElementTypeName, richTextElementGroupName, secondRichTextDataTypeName, secondRichTextEditorDataTypeId); + richTextDataTypeId = await umbracoApi.dataType.createRichTextEditorWithABlock(richTextDataTypeName, richTextElementTypeId); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, richTextDataTypeName, richTextDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.enterRTETipTapEditor(richTextEditorValue); + await umbracoUi.content.clickInsertBlockButton(); + await umbracoUi.content.clickBlockCardWithName(richTextElementTypeName, true); + await umbracoUi.content.enterRTETipTapEditorWithName(AliasHelper.toAlias(secondRichTextDataTypeName), secondRichTextEditorValue); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the RTE is as expected + const documentData = await umbracoApi.document.getByName(contentName); + const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(richTextDataTypeName)); + // Value in the first RTE + expect(documentValues.value.markup).toContain(expectedRichTextEditorOutputValue); + // Value in the second RTE + const secondRTEInBlock = documentValues.value.blocks.contentData[0].values.find(value => value.alias === AliasHelper.toAlias(secondRichTextDataTypeName)); + expect(secondRTEInBlock.value.markup).toContain(secondExpectedRichTextEditorOutputValue); + + // Clean + await umbracoApi.documentType.ensureNameNotExists(richTextElementGroupName); +}); + +test('can publish a rich text editor with a block grid editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const textStringValue = 'Hello World'; + const blockGridDataTypeName = 'BlockGridDataTypeName'; + const blockGridElementTypeName = 'BlockGridElementTypeName'; + const blockGridElementGroupName = 'BlockGridElementGroupName'; + await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + + const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName); + textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id); + const blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockWithInlineEditingMode(blockGridDataTypeName, textStringElementTypeId, true); + const blockGridElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockGridElementTypeName, blockGridElementGroupName, blockGridDataTypeName, blockGridDataTypeId); + richTextDataTypeId = await umbracoApi.dataType.createRichTextEditorWithABlock(richTextDataTypeName, blockGridElementTypeId); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, richTextDataTypeName, richTextDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.clickInsertBlockButton(); + await umbracoUi.content.clickBlockCardWithName(blockGridElementTypeName, true); + await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName); + await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true); + await umbracoUi.content.enterTextstring(textStringValue); + await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the BlockGrid is as expected + const documentData = await umbracoApi.document.getByName(contentName); + expect(documentData.values[0].value.blocks.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue); + + // Clean + await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); +}); + +test('can publish a rich text editor with a block list editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const textStringValue = 'Hello World'; + const blockListDataTypeName = 'BlockListName'; + const blockListElementTypeName = 'BlockListElementName'; + const blockListElementGroupName = 'BlockListGroupName'; + await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); + + const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName); + textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id); + const blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, textStringElementTypeId); + const blockListElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockListElementTypeName, blockListElementGroupName, blockListDataTypeName, blockListDataTypeId); + richTextDataTypeId = await umbracoApi.dataType.createRichTextEditorWithABlock(richTextDataTypeName, blockListElementTypeId); + documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, richTextDataTypeName, richTextDataTypeId, documentTypeGroupName); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + + // Act + await umbracoUi.content.clickInsertBlockButton(); + await umbracoUi.content.clickBlockCardWithName(blockListElementTypeName, true); + await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName); + await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true); + await umbracoUi.content.enterTextstring(textStringValue); + await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName); + await umbracoUi.content.clickCreateModalButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + // Asserts that the value in the BlockGrid is as expected + const documentData = await umbracoApi.document.getByName(contentName); + expect(documentData.values[0].value.blocks.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue); + + // Clean + await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName); + await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName); + await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/TiptapStyleSelect.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/TiptapStyleSelect.spec.ts new file mode 100644 index 0000000000..faab11fe5f --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/TiptapStyleSelect.spec.ts @@ -0,0 +1,114 @@ +import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +const contentName = 'TestContent'; +const documentTypeName = 'TestDocumentTypeForContent'; +const customDataTypeName = 'Test RTE Tiptap Style Select'; +const inputText = 'This is Tiptap test'; + +test.beforeEach(async ({umbracoApi, umbracoUi}) => { + const customDataTypeId = await umbracoApi.dataType.createTiptapDataTypeWithStyleSelect(customDataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.enterRTETipTapEditor(inputText); + await umbracoUi.content.selectAllRTETipTapEditorText(); +}) + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(customDataTypeName); +}); + +test('can apply page header format', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoUi.content.clickStyleSelectButton(); + + // Act + await umbracoUi.content.hoverCascadingMenuItemWithName('Headers'); + await umbracoUi.content.clickCascadingMenuItemWithName('Page header'); + await umbracoUi.content.clickSaveButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value.markup).toEqual('

' + inputText + '

'); +}); + +test('can apply section header format', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoUi.content.clickStyleSelectButton(); + + // Act + await umbracoUi.content.hoverCascadingMenuItemWithName('Headers'); + await umbracoUi.content.clickCascadingMenuItemWithName('Section header'); + await umbracoUi.content.clickSaveButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value.markup).toEqual('

' + inputText + '

'); +}); + +test('can apply paragraph header format', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoUi.content.clickStyleSelectButton(); + + // Act + await umbracoUi.content.hoverCascadingMenuItemWithName('Headers'); + await umbracoUi.content.clickCascadingMenuItemWithName('Paragraph header'); + await umbracoUi.content.clickSaveButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value.markup).toEqual('

' + inputText + '

'); +}); + +test('can apply paragraph blocks format', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoUi.content.clickStyleSelectButton(); + + // Act + await umbracoUi.content.hoverCascadingMenuItemWithName('Blocks'); + await umbracoUi.content.clickCascadingMenuItemWithName('Paragraph'); + await umbracoUi.content.clickSaveButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value.markup).toEqual('

' + inputText + '

'); +}); + +test('can apply block quote format', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoUi.content.clickStyleSelectButton(); + + // Act + await umbracoUi.content.hoverCascadingMenuItemWithName('Containers'); + await umbracoUi.content.clickCascadingMenuItemWithName('Block quote'); + await umbracoUi.content.clickSaveButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value.markup).toEqual('

' + inputText + '

'); +}); + +test('can apply code block format', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoUi.content.clickStyleSelectButton(); + + // Act + await umbracoUi.content.hoverCascadingMenuItemWithName('Containers'); + await umbracoUi.content.clickCascadingMenuItemWithName('Code block'); + await umbracoUi.content.clickSaveButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value.markup).toEqual('
' + inputText + '

'); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/TiptapToolbar.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/TiptapToolbar.spec.ts new file mode 100644 index 0000000000..3f4cf6d572 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RichTextEditor/TiptapToolbar.spec.ts @@ -0,0 +1,190 @@ +import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +const contentName = 'TestContent'; +const documentTypeName = 'TestDocumentTypeForContent'; +const customDataTypeName = 'Test RTE Tiptap'; + +test.beforeEach(async ({umbracoApi}) => { + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.document.ensureNameNotExists(contentName); +}); + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(customDataTypeName); +}); + +test('can add a media in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const iconTitle = 'Media Picker'; + const imageName = 'Test Image For Content'; + await umbracoApi.media.ensureNameNotExists(imageName); + await umbracoApi.media.createDefaultMediaWithImage(imageName); + const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle); + await umbracoUi.content.selectMediaWithName(imageName); + await umbracoUi.content.clickChooseModalButton(); + await umbracoUi.content.clickMediaCaptionAltTextModalSubmitButton(); + await umbracoUi.content.clickSaveAndPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy(); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value.markup).toContain(' { + // Arrange + const iconTitle = 'Embed'; + const videoURL = 'https://www.youtube.com/watch?v=Yu29dE-0OoI'; + const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle); + await umbracoUi.content.enterEmbeddedURL(videoURL); + await umbracoUi.content.clickEmbeddedRetrieveButton(); + await umbracoUi.content.waitForEmbeddedPreviewVisible(); + await umbracoUi.content.clickEmbeddedMediaModalConfirmButton(); + await umbracoUi.content.clickSaveButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy(); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value.markup).toContain('data-embed-url'); + expect(contentData.values[0].value.markup).toContain(videoURL); +}); + +test('cannot submit an empty link in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const iconTitle = 'Link'; + const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle); + await umbracoUi.content.clickManualLinkButton(); + await umbracoUi.content.enterLink(''); + await umbracoUi.content.enterAnchorOrQuerystring(''); + await umbracoUi.content.enterLinkTitle(''); + await umbracoUi.content.clickAddButton(); + + // Assert + await umbracoUi.content.isTextWithMessageVisible(ConstantHelper.validationMessages.emptyLinkPicker); +}); + +// TODO: Remove skip when the front-end ready. Currently it still accept the empty link with an anchor or querystring +// Issue link: https://github.com/umbraco/Umbraco-CMS/issues/17411 +test.skip('cannot submit an empty URL with an anchor or querystring in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const iconTitle = 'Link'; + const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle); + await umbracoUi.content.clickManualLinkButton(); + await umbracoUi.content.enterLink(''); + await umbracoUi.content.enterAnchorOrQuerystring('#value'); + await umbracoUi.content.clickAddButton(); + + // Assert + await umbracoUi.content.isTextWithMessageVisible(ConstantHelper.validationMessages.emptyLinkPicker); +}); + +// TODO: Remove skip when the front-end ready. Currently it is impossible to link to unpublished document +// Issue link: https://github.com/umbraco/Umbraco-CMS/issues/17974 +test.skip('can insert a link to an unpublished document in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const iconTitle = 'Link'; + const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + // Create a document to link + const documentTypeForLinkedDocumentName = 'TestDocumentType'; + const documentTypeForLinkedDocumentId = await umbracoApi.documentType.createDefaultDocumentTypeWithAllowAsRoot(documentTypeForLinkedDocumentName); + const linkedDocumentName = 'LinkedDocument'; + await umbracoApi.document.createDefaultDocument(linkedDocumentName, documentTypeForLinkedDocumentId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle); + await umbracoUi.content.clickDocumentLinkButton(); + await umbracoUi.content.selectLinkByName(linkedDocumentName); + await umbracoUi.content.clickButtonWithName('Choose'); + await umbracoUi.content.clickAddButton(); + await umbracoUi.content.clickSaveButton(); + + // Assert + await umbracoUi.content.isSuccessNotificationVisible(); + + // Clean + await umbracoApi.documentType.ensureNameNotExists(documentTypeForLinkedDocumentName); + await umbracoApi.document.ensureNameNotExists(linkedDocumentName); +}); + +test('can view word count', async ({umbracoApi, umbracoUi}) => { + // Arrange + const inputText = 'Test Tiptap here!!!'; + const expectedWordCount = 3; + const customDataTypeId = await umbracoApi.dataType.createTiptapDataTypeWithWordCountStatusbar(customDataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.enterRTETipTapEditor(inputText); + + // Assert + await umbracoUi.content.doesTiptapHaveWordCount(expectedWordCount); +}); + +test('can view element path', async ({umbracoApi, umbracoUi}) => { + // Arrange + const inputText = 'This is Tiptap test'; + const expectedElementPath = 'p'; + const customDataTypeId = await umbracoApi.dataType.createTiptapDataTypeWithElementPathStatusbar(customDataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId); + await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.enterRTETipTapEditor(inputText); + + // Assert + await umbracoUi.content.doesElementPathHaveText(expectedElementPath); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent/BulkTrashContent.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent/BulkTrashContent.spec.ts new file mode 100644 index 0000000000..957c330e65 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent/BulkTrashContent.spec.ts @@ -0,0 +1,93 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +let collectionId = ''; +const contentName = 'TestContent'; +const documentTypeName = 'TestDocumentTypeForContent'; +const childDocumentTypeName = 'TestChildDocumentType'; +const firstChildContentName = 'First Child Content'; +const secondChildContentName = 'Second Child Content'; +const collectionDataTypeName = 'List View - Content'; +const referenceHeadline = ConstantHelper.trashDeleteDialogMessage.bulkReferenceHeadline; +const documentPickerName = ['TestPicker', 'DocumentTypeForPicker']; + +test.beforeEach(async ({umbracoApi}) => { + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.document.ensureNameNotExists(contentName); + const collectionDataTypeData = await umbracoApi.dataType.getByName(collectionDataTypeName); + collectionId = collectionDataTypeData.id; +}); + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.documentType.ensureNameNotExists(childDocumentTypeName); + await umbracoApi.document.emptyRecycleBin(); +}); + +test('can bulk trash content nodes without a relation', async ({umbracoApi, umbracoUi}) => { + // Arrange + const childDocumentTypeId = await umbracoApi.documentType.createDefaultDocumentType(childDocumentTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithAllowedChildNodeAndCollectionId(documentTypeName, childDocumentTypeId, collectionId); + const contentId = await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoApi.document.createDefaultDocumentWithParent(firstChildContentName, childDocumentTypeId, contentId); + await umbracoApi.document.createDefaultDocumentWithParent(secondChildContentName, childDocumentTypeId, contentId); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.selectContentWithNameInListView(firstChildContentName); + await umbracoUi.content.selectContentWithNameInListView(secondChildContentName); + await umbracoUi.content.clickTrashSelectedListItems(); + // Verify the references list not displayed + await umbracoUi.content.isReferenceHeadlineVisible(false); + await umbracoUi.content.clickConfirmTrashButton(); + + // // Assert + await umbracoUi.content.isSuccessNotificationVisible(); + expect(await umbracoApi.document.doesNameExist(firstChildContentName)).toBeFalsy(); + expect(await umbracoApi.document.doesNameExist(secondChildContentName)).toBeFalsy(); + await umbracoUi.content.isItemVisibleInRecycleBin(firstChildContentName); + await umbracoUi.content.isItemVisibleInRecycleBin(secondChildContentName); + expect(await umbracoApi.document.doesItemExistInRecycleBin(firstChildContentName)).toBeTruthy(); + expect(await umbracoApi.document.doesItemExistInRecycleBin(secondChildContentName)).toBeTruthy(); +}); + +test('can bulk trash content nodes with a relation', async ({umbracoApi, umbracoUi}) => { + // Arrange + const childDocumentTypeId = await umbracoApi.documentType.createDefaultDocumentType(childDocumentTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithAllowedChildNodeAndCollectionId(documentTypeName, childDocumentTypeId, collectionId); + const contentId = await umbracoApi.document.createDefaultDocument(contentName, documentTypeId); + await umbracoApi.document.publish(contentId); + const firstChildContentId = await umbracoApi.document.createDefaultDocumentWithParent(firstChildContentName, childDocumentTypeId, contentId); + await umbracoApi.document.publish(firstChildContentId); + await umbracoApi.document.createDefaultDocumentWithParent(secondChildContentName, childDocumentTypeId, contentId); + // Create a document that has a document picker with firstChildContentName + await umbracoApi.document.createDefaultDocumentWithOneDocumentLink(documentPickerName[0], firstChildContentName, firstChildContentId, documentPickerName[1]); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.selectContentWithNameInListView(firstChildContentName); + await umbracoUi.content.selectContentWithNameInListView(secondChildContentName); + await umbracoUi.content.clickTrashSelectedListItems(); + // Verify the references list + await umbracoUi.content.doesReferenceHeadlineHaveText(referenceHeadline); + await umbracoUi.content.doesReferenceItemsHaveCount(1); + await umbracoUi.content.isReferenceItemNameVisible(firstChildContentName); + await umbracoUi.content.clickConfirmTrashButton(); + + // // Assert + await umbracoUi.content.isSuccessNotificationVisible(); + expect(await umbracoApi.document.doesNameExist(firstChildContentName)).toBeFalsy(); + expect(await umbracoApi.document.doesNameExist(secondChildContentName)).toBeFalsy(); + await umbracoUi.content.isItemVisibleInRecycleBin(firstChildContentName); + await umbracoUi.content.isItemVisibleInRecycleBin(secondChildContentName); + expect(await umbracoApi.document.doesItemExistInRecycleBin(firstChildContentName)).toBeTruthy(); + expect(await umbracoApi.document.doesItemExistInRecycleBin(secondChildContentName)).toBeTruthy(); + + // Clean + await umbracoApi.documentType.ensureNameNotExists(documentPickerName[1]); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent/TrashContent.spec.ts similarity index 99% rename from tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent.spec.ts rename to tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent/TrashContent.spec.ts index d1357b88ac..0d3c854141 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/TrashContent/TrashContent.spec.ts @@ -6,7 +6,7 @@ const contentName = 'TestContent'; const documentTypeName = 'TestDocumentTypeForContent'; const dataTypeName = 'Textstring'; const contentText = 'This is test content text'; -const referenceHeadline = 'The following items depend on this'; +const referenceHeadline = ConstantHelper.trashDeleteDialogMessage.referenceHeadline; const documentPickerName = ['TestPicker', 'DocumentTypeForPicker']; test.beforeEach(async ({umbracoApi}) => { diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/RichTextEditor.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/RichTextEditor.spec.ts index 62b8dc3928..2660e106d0 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/RichTextEditor.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/RichTextEditor.spec.ts @@ -61,8 +61,8 @@ test('tiptap is the default property editor in rich text editor', async ({umbrac await umbracoUi.dataType.goToDataType(dataTypeName); // Assert - //await umbracoUi.dataType.doesSettingHaveValue(ConstantHelper.tipTapSettings); - //await umbracoUi.dataType.doesSettingItemsHaveCount(ConstantHelper.tipTapSettings); + await umbracoUi.dataType.doesSettingHaveValue(ConstantHelper.tipTapSettings); + await umbracoUi.dataType.doesSettingItemsHaveCount(ConstantHelper.tipTapSettings); await umbracoUi.dataType.doesPropertyEditorHaveName(tipTapPropertyEditorName); await umbracoUi.dataType.doesPropertyEditorHaveAlias(tipTapAlias); await umbracoUi.dataType.doesPropertyEditorHaveUiAlias(tipTapUiAlias); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/Tiptap.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/Tiptap.spec.ts index ac72b2f4a6..e61aa1b40e 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/Tiptap.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/Tiptap.spec.ts @@ -127,6 +127,7 @@ test('can add an available block', async ({umbracoApi, umbracoUi}) => { await umbracoUi.dataType.goToDataType(tipTapName); // Act + await umbracoUi.dataType.isExtensionItemChecked('Block', false); await umbracoUi.dataType.addAvailableBlocks(elementTypeName); await umbracoUi.dataType.clickSaveButton(); @@ -134,6 +135,8 @@ test('can add an available block', async ({umbracoApi, umbracoUi}) => { //await umbracoUi.dataType.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); await umbracoUi.dataType.isErrorNotificationVisible(false); expect(await umbracoApi.dataType.doesRTEContainBlocks(tipTapName, [elementTypeId])).toBeTruthy(); + // Verify that "Block" extension is enable + await umbracoUi.dataType.isExtensionItemChecked('Block'); // Clean await umbracoApi.documentType.ensureNameNotExists(elementTypeName); @@ -238,3 +241,38 @@ test('can disable extensions item', async ({umbracoApi, umbracoUi}) => { expect(extensionsValue.value.length).toBe(extensionsCount - 1); expect(extensionsValue.value).not.toContain(extensionItemName); }); + +test('can add a statusbar', async ({umbracoApi, umbracoUi}) => { + // Arrange + const statusbarName = 'Word Count'; + const statusbarApiValue = 'Umb.Tiptap.Statusbar.WordCount'; + await umbracoApi.dataType.createDefaultTiptapDataType(tipTapName); + await umbracoUi.dataType.goToDataType(tipTapName); + + // Act + await umbracoUi.dataType.clickStatusbarItemInToolboxWithName(statusbarName); + await umbracoUi.dataType.clickSaveButton(); + + // Assert + await umbracoUi.dataType.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + const tipTapData = await umbracoApi.dataType.getByName(tipTapName); + const statusbarValue = tipTapData.values.find(value => value.alias === 'statusbar'); + expect(statusbarValue.value).toEqual([[statusbarApiValue]]); +}); + +test('can remove a statusbar', async ({umbracoApi, umbracoUi}) => { + // Arrange + const statusbarName = 'Word Count'; + await umbracoApi.dataType.createTiptapDataTypeWithWordCountStatusbar(tipTapName); + await umbracoUi.dataType.goToDataType(tipTapName); + + // Act + await umbracoUi.dataType.clickStatusbarItemWithName(statusbarName); + await umbracoUi.dataType.clickSaveButton(); + + // Assert + await umbracoUi.dataType.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved); + const tipTapData = await umbracoApi.dataType.getByName(tipTapName); + const statusbarValue = tipTapData.values.find(value => value.alias === 'statusbar'); + expect(statusbarValue).toBeFalsy(); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/Media.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/Media.spec.ts index dddb5f11f3..25c923b50d 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/Media.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/Media.spec.ts @@ -251,3 +251,81 @@ test('can empty the recycle bin', async ({umbracoApi, umbracoUi}) => { expect(await umbracoApi.media.doesNameExist(mediaFileName)).toBeFalsy(); expect(await umbracoApi.media.doesMediaItemExistInRecycleBin(mediaFileName)).toBeFalsy(); }); + +test('can trash a media node with a relation', async ({umbracoApi, umbracoUi}) => { + // Arrange + const documentPickerName = ['TestPicker', 'DocumentTypeForPicker']; + await umbracoApi.media.emptyRecycleBin(); + await umbracoApi.media.createDefaultMediaFile(mediaFileName); + await umbracoApi.media.doesNameExist(mediaFileName); + // Create a document that have media picker is firstMediaFileName + await umbracoApi.document.createDefaultDocumentWithOneMediaLink(documentPickerName[0], mediaFileName, documentPickerName[1]); + await umbracoUi.media.goToSection(ConstantHelper.sections.media); + + // Act + await umbracoUi.media.clickActionsMenuForName(mediaFileName); + await umbracoUi.media.clickTrashButton(); + // Verify the references list + await umbracoUi.media.doesReferenceHeadlineHaveText(ConstantHelper.trashDeleteDialogMessage.referenceHeadline); + await umbracoUi.media.doesReferenceItemsHaveCount(1); + await umbracoUi.media.isReferenceItemNameVisible(documentPickerName[0]); + await umbracoUi.media.clickConfirmTrashButton(); + + // Assert + // await umbracoUi.media.doesSuccessNotificationHaveText(NotificationConstantHelper.success.movedToRecycleBin); + await umbracoUi.media.isErrorNotificationVisible(false); + await umbracoUi.media.isItemVisibleInRecycleBin(mediaFileName); + expect(await umbracoApi.media.doesNameExist(mediaFileName)).toBeFalsy(); + expect(await umbracoApi.media.doesMediaItemExistInRecycleBin(mediaFileName)).toBeTruthy(); + + // Clean + await umbracoApi.media.emptyRecycleBin(); + await umbracoApi.document.ensureNameNotExists(documentPickerName[0]); + await umbracoApi.documentType.ensureNameNotExists(documentPickerName[1]); +}); + +test('can bulk trash media nodes with a relation', async ({umbracoApi, umbracoUi}) => { + // Arrange + const firstMediaFileName = 'FirstMediaFile'; + const secondMediaFileName = 'SecondMediaFile'; + const documentPickerName1 = ['TestPicker1', 'DocumentTypeForPicker1']; + const documentPickerName2 = ['TestPicker2', 'DocumentTypeForPicker2']; + await umbracoApi.media.emptyRecycleBin(); + await umbracoApi.media.createDefaultMediaFile(firstMediaFileName); + await umbracoApi.media.createDefaultMediaFile(secondMediaFileName); + // Create a document that has a media picker with firstMediaFileName + await umbracoApi.document.createDefaultDocumentWithOneMediaLink(documentPickerName1[0], firstMediaFileName, documentPickerName1[1]); + // Create a document that has a media picker with secondMediaFileName + await umbracoApi.document.createDefaultDocumentWithOneMediaLink(documentPickerName2[0], secondMediaFileName, documentPickerName2[1]); + + // Act + await umbracoUi.media.goToSection(ConstantHelper.sections.media); + await umbracoUi.media.selectMediaWithName(firstMediaFileName); + await umbracoUi.media.selectMediaWithName(secondMediaFileName); + await umbracoUi.media.clickBulkTrashButton(); + // Verify the references list + await umbracoUi.media.doesReferenceHeadlineHaveText(ConstantHelper.trashDeleteDialogMessage.bulkReferenceHeadline); + await umbracoUi.media.doesReferenceItemsHaveCount(2); + await umbracoUi.media.isReferenceItemNameVisible(firstMediaFileName); + await umbracoUi.media.isReferenceItemNameVisible(secondMediaFileName); + await umbracoUi.media.clickConfirmTrashButton(); + + // Assert + // await umbracoUi.media.isSuccessNotificationVisible(); + await umbracoUi.media.isErrorNotificationVisible(false); + expect(await umbracoApi.media.doesNameExist(firstMediaFileName)).toBeFalsy(); + expect(await umbracoApi.media.doesNameExist(secondMediaFileName)).toBeFalsy(); + expect(await umbracoApi.media.doesMediaItemExistInRecycleBin(firstMediaFileName)).toBeTruthy(); + expect(await umbracoApi.media.doesMediaItemExistInRecycleBin(secondMediaFileName)).toBeTruthy(); + await umbracoUi.media.isItemVisibleInRecycleBin(firstMediaFileName); + await umbracoUi.media.isItemVisibleInRecycleBin(secondMediaFileName, true, false); + + // Clean + await umbracoApi.media.ensureNameNotExists(firstMediaFileName); + await umbracoApi.media.ensureNameNotExists(secondMediaFileName); + await umbracoApi.document.ensureNameNotExists(documentPickerName1[0]); + await umbracoApi.documentType.ensureNameNotExists(documentPickerName1[1]); + await umbracoApi.document.ensureNameNotExists(documentPickerName2[0]); + await umbracoApi.documentType.ensureNameNotExists(documentPickerName2[1]); + await umbracoApi.media.emptyRecycleBin(); +}); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs index 5b63a7271d..94a6c1884a 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; @@ -129,7 +130,7 @@ internal sealed class DocumentRepositoryTest : UmbracoIntegrationTest var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); var dataValueReferences = - new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); + new DataValueReferenceFactoryCollection(() => Enumerable.Empty(), new NullLogger()); var repository = new DocumentRepository( scopeAccessor, appCaches, diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs index 213d6f804a..3b21605d07 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs @@ -3,6 +3,7 @@ using System.Linq; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; @@ -64,7 +65,7 @@ internal sealed class MediaRepositoryTest : UmbracoIntegrationTest new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); var mediaUrlGenerators = new MediaUrlGeneratorCollection(() => Enumerable.Empty()); var dataValueReferences = - new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); + new DataValueReferenceFactoryCollection(() => Enumerable.Empty(), new NullLogger()); var repository = new MediaRepository( scopeAccessor, appCaches, diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs index 9f1a190284..d0310af4a1 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; using NPoco; @@ -53,7 +54,7 @@ internal sealed class MemberRepositoryTest : UmbracoIntegrationTest var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); var dataValueReferences = - new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); + new DataValueReferenceFactoryCollection(() => Enumerable.Empty(), new NullLogger()); return new MemberRepository( accessor, AppCaches.Disabled, diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index b3a8dfa119..973ea658dc 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Text; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; @@ -272,7 +273,7 @@ internal sealed class TemplateRepositoryTest : UmbracoIntegrationTest var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); var dataValueReferences = - new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); + new DataValueReferenceFactoryCollection(() => Enumerable.Empty(), new NullLogger()); var contentRepo = new DocumentRepository( scopeAccessor, AppCaches.Disabled, diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/BlockListEditorPropertyValueEditorTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/BlockListEditorPropertyValueEditorTests.cs index 879400f79a..fc56535200 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/BlockListEditorPropertyValueEditorTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/BlockListEditorPropertyValueEditorTests.cs @@ -1,5 +1,6 @@ using System.Globalization; using System.Text.Json.Nodes; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; @@ -148,7 +149,7 @@ public class BlockListEditorPropertyValueEditorTests new DataEditorAttribute("alias"), new BlockListEditorDataConverter(jsonSerializer), new(new DataEditorCollection(() => [])), - new DataValueReferenceFactoryCollection(Enumerable.Empty), + new DataValueReferenceFactoryCollection(Enumerable.Empty, Mock.Of>()), Mock.Of(), Mock.Of(), localizedTextServiceMock.Object, diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs index 16ea5c11f9..f83b779c63 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Cache; @@ -35,7 +36,7 @@ public class DataValueEditorReuseTests Mock.Of())); _propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty)); - _dataValueReferenceFactories = new DataValueReferenceFactoryCollection(Enumerable.Empty); + _dataValueReferenceFactories = new DataValueReferenceFactoryCollection(Enumerable.Empty, new NullLogger()); var blockVarianceHandler = new BlockEditorVarianceHandler(Mock.Of(), Mock.Of()); _dataValueEditorFactoryMock diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs index 33f4f307f8..43757f064c 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; @@ -49,7 +50,7 @@ public class DataValueReferenceFactoryCollectionTests [Test] public void GetAllReferences_All_Variants_With_IDataValueReferenceFactory() { - var collection = new DataValueReferenceFactoryCollection(() => new TestDataValueReferenceFactory().Yield()); + var collection = new DataValueReferenceFactoryCollection(() => new TestDataValueReferenceFactory().Yield(), new NullLogger()); // label does not implement IDataValueReference var labelEditor = new LabelPropertyEditor( @@ -93,7 +94,7 @@ public class DataValueReferenceFactoryCollectionTests [Test] public void GetAllReferences_All_Variants_With_IDataValueReference_Editor() { - var collection = new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); + var collection = new DataValueReferenceFactoryCollection(() => Enumerable.Empty(), new NullLogger()); // mediaPicker does implement IDataValueReference var mediaPicker = new MediaPicker3PropertyEditor( @@ -137,7 +138,7 @@ public class DataValueReferenceFactoryCollectionTests [Test] public void GetAllReferences_Invariant_With_IDataValueReference_Editor() { - var collection = new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); + var collection = new DataValueReferenceFactoryCollection(() => Enumerable.Empty(), new NullLogger()); // mediaPicker does implement IDataValueReference var mediaPicker = new MediaPicker3PropertyEditor( @@ -181,7 +182,7 @@ public class DataValueReferenceFactoryCollectionTests [Test] public void GetAutomaticRelationTypesAliases_ContainsDefault() { - var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty); + var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty, new NullLogger()); var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty)); var result = collection.GetAllAutomaticRelationTypesAliases(propertyEditors).ToArray(); @@ -193,7 +194,7 @@ public class DataValueReferenceFactoryCollectionTests [Test] public void GetAutomaticRelationTypesAliases_ContainsCustom() { - var collection = new DataValueReferenceFactoryCollection(() => new TestDataValueReferenceFactory().Yield()); + var collection = new DataValueReferenceFactoryCollection(() => new TestDataValueReferenceFactory().Yield(), new NullLogger()); var labelPropertyEditor = new LabelPropertyEditor(DataValueEditorFactory, IOHelper); var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => labelPropertyEditor.Yield()));