diff --git a/build/nightly-E2E-test-pipelines.yml b/build/nightly-E2E-test-pipelines.yml index 7bbdec8e145c..e95d6ac403c8 100644 --- a/build/nightly-E2E-test-pipelines.yml +++ b/build/nightly-E2E-test-pipelines.yml @@ -526,6 +526,23 @@ stages: CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient additionalEnvironmentVariables: true + # ExtensionRegistry + WindowsExtensionRegistry: + vmImage: "windows-latest" + testFolder: "ExtensionRegistry" + port: '' + testCommand: "npx playwright test --project=extensionRegistry" + CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True + CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient + additionalEnvironmentVariables: false + LinuxExtensionRegistry: + vmImage: "ubuntu-latest" + testFolder: "ExtensionRegistry" + port: '' + testCommand: "npx playwright test --project=extensionRegistry" + CONNECTIONSTRINGS__UMBRACODBDSN: Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True + CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient + additionalEnvironmentVariables: false pool: vmImage: $(vmImage) steps: @@ -657,4 +674,4 @@ stages: --data "$PAYLOAD" \ "$SLACK_WEBHOOK_URL" env: - SLACK_WEBHOOK_URL: $(E2ESLACKWEBHOOKURL) + SLACK_WEBHOOK_URL: $(E2ESLACKWEBHOOKURL) \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts b/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts index c09d132505c8..72776856bdeb 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts @@ -54,6 +54,17 @@ export default defineConfig({ storageState: STORAGE_STATE } }, + { + name: 'extensionRegistry', + testMatch: 'ExtensionRegistry/*.spec.ts', + dependencies: ['setup'], + use: { + ...devices['Desktop Chrome'], + // Use prepared auth state. + ignoreHTTPSErrors: true, + storageState: STORAGE_STATE + } + }, { name: 'deliveryApi', testMatch: 'DeliveryApi/**', @@ -63,7 +74,7 @@ export default defineConfig({ // Use prepared auth state. ignoreHTTPSErrors: true, storageState: STORAGE_STATE - }, + } }, { name: 'externalLoginAzureADB2C', diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/DeliveryApi.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/DeliveryApi.spec.ts index 5d3ae153ebc1..cde8e2c9ccd2 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/DeliveryApi.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/DeliveryApi.spec.ts @@ -1,7 +1,7 @@ import {expect} from '@playwright/test'; import {AliasHelper, test} from '@umbraco/playwright-testhelpers'; -test('can get content from delivery api', async ({umbracoApi}) => { +test.skip('can get content from delivery api', async ({umbracoApi}) => { // Arrange const documentTypeName = 'TestDocumentType'; const contentName = 'TestContent'; diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/dist/customtexteditor.js b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/dist/customtexteditor.js new file mode 100644 index 000000000000..7081fa59d1bf --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/dist/customtexteditor.js @@ -0,0 +1,100 @@ +import { css as p, property as u, state as c, customElement as g, html as l } from "@umbraco-cms/backoffice/external/lit"; +import { UmbLitElement as m } from "@umbraco-cms/backoffice/lit-element"; +import { UmbTextStyles as v } from "@umbraco-cms/backoffice/style"; +var x = Object.defineProperty, _ = Object.getOwnPropertyDescriptor, o = (t, e, a, n) => { + for (var r = n > 1 ? void 0 : n ? _(e, a) : e, i = t.length - 1, h; i >= 0; i--) + (h = t[i]) && (r = (n ? h(e, a, r) : h(r)) || r); + return n && r && x(e, a, r), r; +}; +let s = class extends m { + constructor() { + super(...arguments), this.value = ""; + } + render() { + const t = this.value?.length || 0, a = this._maxLength !== null && this._maxLength !== void 0 && this._maxLength > 0; + let n = ""; + return this._maxLength && (t > this._maxLength * 0.9 ? n = "danger" : t > this._maxLength * 0.7 && (n = "warning")), l` + + ${a ? l` +
+ ${t}/${this._maxLength} +
+ ` : ""} + `; + } + connectedCallback() { + super.connectedCallback(), this._updateConfigValues(); + } + _updateConfigValues() { + this._maxLength = this.config?.getValueByAlias("maxChars"), this._placeholder = this.config?.getValueByAlias("placeholder") || "Enter text here..."; + } + _onInput(t) { + const e = t.target, a = e.value; + if (this._maxLength && a.length > this._maxLength) { + e.value = this.value; + return; + } + this.value = a, this._dispatchChangeEvent(); + } + _dispatchChangeEvent() { + this.dispatchEvent( + new CustomEvent("property-value-change", { + detail: { + value: this.value + }, + bubbles: !0, + composed: !0 + }) + ); + } +}; +s.styles = [ + v, + p` + .text-input{ + width: 100%; + } + .char-counter { + position: absolute; + bottom: -20px; + right: 0; + font-size: 12px; + color: var(--uui-color-text-alt); + } + + .char-counter.warning { + color: var(--uui-color-warning); + } + + .char-counter.danger { + color: var(--uui-color-danger); + } + ` +]; +o([ + u({ type: String }) +], s.prototype, "value", 2); +o([ + u({ attribute: !1 }) +], s.prototype, "config", 2); +o([ + c() +], s.prototype, "_maxLength", 2); +o([ + c() +], s.prototype, "_placeholder", 2); +s = o([ + g("custom-text-editor") +], s); +export { + s as CustomTextEditorElement, + s as default +}; +//# sourceMappingURL=customtexteditor.js.map diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/dist/customtexteditor.js.map b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/dist/customtexteditor.js.map new file mode 100644 index 000000000000..14c2bd2d0eb9 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/dist/customtexteditor.js.map @@ -0,0 +1 @@ +{"version":3,"file":"customtexteditor.js","sources":["../src/customtexteditor.ts"],"sourcesContent":["import { html, css, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';\r\nimport { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';\r\nimport type { UmbPropertyEditorConfigCollection, UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor';\r\nimport { UmbTextStyles } from '@umbraco-cms/backoffice/style';\r\n\r\n@customElement('custom-text-editor')\r\nexport class CustomTextEditorElement extends UmbLitElement implements UmbPropertyEditorUiElement {\r\n\r\n @property({ type: String })\r\n value: string = '';\r\n\r\n @property({ attribute: false })\r\n public config?: UmbPropertyEditorConfigCollection;\r\n\r\n @state()\r\n private _maxLength?: number;\r\n\r\n @state()\r\n private _placeholder?: string;\r\n\r\n override render() {\r\n const characterCount = this.value?.length || 0;\r\n const hasMaxLength = this._maxLength !== null && this._maxLength !== undefined && this._maxLength > 0;\r\n const showCharCounter = hasMaxLength;\r\n\r\n // Safe calculation with null checks\r\n let counterClass = '';\r\n if (this._maxLength) {\r\n if (characterCount > this._maxLength * 0.9) {\r\n counterClass = 'danger';\r\n } else if (characterCount > this._maxLength * 0.7) {\r\n counterClass = 'warning';\r\n }\r\n }\r\n\r\n return html`\r\n \r\n ${showCharCounter\r\n ? html`\r\n
\r\n ${characterCount}/${this._maxLength}\r\n
\r\n `\r\n : ''\r\n }\r\n `;\r\n }\r\n\r\n connectedCallback(): void {\r\n super.connectedCallback();\r\n this._updateConfigValues();\r\n }\r\n\r\n private _updateConfigValues(): void {\r\n this._maxLength = this.config?.getValueByAlias('maxChars') as number;\r\n this._placeholder = this.config?.getValueByAlias('placeholder') as string || 'Enter text here...';\r\n }\r\n\r\n private _onInput(event: Event): void {\r\n const target = event.target as HTMLInputElement;\r\n const newValue = target.value;\r\n\r\n // Apply max length validation if configured\r\n if (this._maxLength && newValue.length > this._maxLength) {\r\n target.value = this.value; // Revert to previous value\r\n return;\r\n }\r\n\r\n this.value = newValue;\r\n this._dispatchChangeEvent();\r\n }\r\n\r\n private _dispatchChangeEvent(): void {\r\n this.dispatchEvent(\r\n new CustomEvent('property-value-change', {\r\n detail: {\r\n value: this.value,\r\n },\r\n bubbles: true,\r\n composed: true,\r\n })\r\n );\r\n }\r\n\r\n static styles = [\r\n UmbTextStyles,\r\n css`\r\n .text-input{\r\n width: 100%;\r\n }\r\n .char-counter {\r\n position: absolute;\r\n bottom: -20px;\r\n right: 0;\r\n font-size: 12px;\r\n color: var(--uui-color-text-alt);\r\n }\r\n\r\n .char-counter.warning {\r\n color: var(--uui-color-warning);\r\n }\r\n\r\n .char-counter.danger {\r\n color: var(--uui-color-danger);\r\n }\r\n `];\r\n}\r\n\r\nexport {\r\n CustomTextEditorElement as default\r\n};"],"names":["CustomTextEditorElement","UmbLitElement","characterCount","showCharCounter","counterClass","html","event","target","newValue","UmbTextStyles","css","__decorateClass","property","state","customElement"],"mappings":";;;;;;;;AAMO,IAAMA,IAAN,cAAsCC,EAAoD;AAAA,EAA1F,cAAA;AAAA,UAAA,GAAA,SAAA,GAGH,KAAA,QAAgB;AAAA,EAAA;AAAA,EAWP,SAAS;AACd,UAAMC,IAAiB,KAAK,OAAO,UAAU,GAEvCC,IADe,KAAK,eAAe,QAAQ,KAAK,eAAe,UAAa,KAAK,aAAa;AAIpG,QAAIC,IAAe;AACnB,WAAI,KAAK,eACDF,IAAiB,KAAK,aAAa,MACnCE,IAAe,WACRF,IAAiB,KAAK,aAAa,QAC1CE,IAAe,aAIhBC;AAAA;AAAA;AAAA;AAAA,2BAIY,KAAK,SAAS,EAAE;AAAA,iCACV,KAAK,gBAAgB,EAAE;AAAA,+BACzB,KAAK,cAAc,EAAE;AAAA,2BACzB,KAAK,QAAQ;AAAA;AAAA,kBAEtBF,IACAE;AAAA,+CAC6BD,CAAY;AAAA,wBACnCF,CAAc,IAAI,KAAK,UAAU;AAAA;AAAA,sBAGvC,EACE;AAAA;AAAA,EAEhB;AAAA,EAEA,oBAA0B;AACtB,UAAM,kBAAA,GACN,KAAK,oBAAA;AAAA,EACT;AAAA,EAEQ,sBAA4B;AAChC,SAAK,aAAa,KAAK,QAAQ,gBAAgB,UAAU,GACzD,KAAK,eAAe,KAAK,QAAQ,gBAAgB,aAAa,KAAe;AAAA,EACjF;AAAA,EAEQ,SAASI,GAAoB;AACjC,UAAMC,IAASD,EAAM,QACfE,IAAWD,EAAO;AAGxB,QAAI,KAAK,cAAcC,EAAS,SAAS,KAAK,YAAY;AACtD,MAAAD,EAAO,QAAQ,KAAK;AACpB;AAAA,IACJ;AAEA,SAAK,QAAQC,GACb,KAAK,qBAAA;AAAA,EACT;AAAA,EAEQ,uBAA6B;AACjC,SAAK;AAAA,MACD,IAAI,YAAY,yBAAyB;AAAA,QACrC,QAAQ;AAAA,UACJ,OAAO,KAAK;AAAA,QAAA;AAAA,QAEhB,SAAS;AAAA,QACT,UAAU;AAAA,MAAA,CACb;AAAA,IAAA;AAAA,EAET;AAwBJ;AA3GaR,EAqFF,SAAS;AAAA,EACZS;AAAA,EACAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBC;AAvGLC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAFjBZ,EAGT,WAAA,SAAA,CAAA;AAGOW,EAAA;AAAA,EADNC,EAAS,EAAE,WAAW,GAAA,CAAO;AAAA,GALrBZ,EAMF,WAAA,UAAA,CAAA;AAGCW,EAAA;AAAA,EADPE,EAAA;AAAM,GAREb,EASD,WAAA,cAAA,CAAA;AAGAW,EAAA;AAAA,EADPE,EAAA;AAAM,GAXEb,EAYD,WAAA,gBAAA,CAAA;AAZCA,IAANW,EAAA;AAAA,EADNG,EAAc,oBAAoB;AAAA,GACtBd,CAAA;"} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/umbraco-package.json b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/umbraco-package.json new file mode 100644 index 000000000000..bda5dc316f69 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/custom-property-editor/umbraco-package.json @@ -0,0 +1,18 @@ +{ + "$schema": "../../umbraco-package-schema.json", + "name": "My.Extension", + "extensions": [ + { + "type": "propertyEditorUi", + "alias": "Custom.TextEditor", + "name": "Custom Text Editor", + "element": "/App_Plugins/custom-property-editor/dist/customtexteditor.js", + "meta": { + "label": "Custom Text Editor", + "propertyEditorSchemaAlias": "Umbraco.TextBox", + "icon": "icon-edit", + "group": "Custom" + } + } + ] +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/lock-action.api.js b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/lock-action.api.js new file mode 100644 index 000000000000..639ab422b9d0 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/lock-action.api.js @@ -0,0 +1,25 @@ +import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/document'; +import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; +import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; + +export class UmbDocumentLockEntityAction extends UmbEntityActionBase { + async execute() { + const context = await this.getContext(UMB_DOCUMENT_WORKSPACE_CONTEXT); + const ruleUnique = 'lock'; + + const myRule = { + unique: ruleUnique, + UmbVariantId: new UmbVariantId(), + }; + + const hasRule = context?.readOnlyGuard.getRules().find((rule) => rule.unique === ruleUnique); + + if (hasRule) { + context?.readOnlyGuard.removeRule(ruleUnique); + } else { + context?.readOnlyGuard.addRule(myRule); + } + } +} + +export { UmbDocumentLockEntityAction as api }; \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/lock-action.manifests.js b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/lock-action.manifests.js new file mode 100644 index 000000000000..ef8683502ecd --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/lock-action.manifests.js @@ -0,0 +1,17 @@ +import { UMB_DOCUMENT_ENTITY_TYPE } from '@umbraco-cms/backoffice/document'; + +export const manifests = [ + { + type: 'entityAction', + kind: 'default', + alias: 'Umb.EntityAction.Document.Lock', + name: 'Lock Document Entity Action', + forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE], + api: () => import('./lock-action.api.js'), + weight: 200, + meta: { + label: 'Lock it', + icon: 'icon-lock', + } + } +] \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/umbraco-package.json b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/umbraco-package.json new file mode 100644 index 000000000000..eb46049cfe34 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/lock-action/umbraco-package.json @@ -0,0 +1,12 @@ + { + "name": "E2E Test Package", + "version": "1.0.0", + "extensions": [ + { + "type": "bundle", + "alias": "E2E.Package.Bundle", + "name": "E2E Test Package Bundle", + "js": "/App_Plugins/lock-action/lock-action.manifests.js" + } + ] + } \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/retrieve-action.api.js b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/retrieve-action.api.js new file mode 100644 index 000000000000..cd16d528c540 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/retrieve-action.api.js @@ -0,0 +1,18 @@ +import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; +import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; + +export class RetrieveAction extends UmbEntityActionBase { + + async execute() { + const { entityType, unique } = this.args; + const message = `${entityType}_${unique}`; + const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + notificationContext?.peek('positive', { + data: { + headline: '', + message: message, + }, + }); + } +} +export { RetrieveAction as api }; diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/retrieve-action.manifests.js b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/retrieve-action.manifests.js new file mode 100644 index 000000000000..34062853b3df --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/retrieve-action.manifests.js @@ -0,0 +1,13 @@ +export const manifest = { + type: 'entityAction', + kind: 'default', + alias: 'Retrieve', + name: 'Retrieve', + weight: 200, + forEntityTypes: ['document', 'media'], + api: () => import('./retrieve-action.api.js'), + meta: { + icon: 'icon-add', + label: 'Retrieve' + }, +}; \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/umbraco-package.json b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/umbraco-package.json new file mode 100644 index 000000000000..8f6b011869ca --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/App_Plugins/retrieve-action/umbraco-package.json @@ -0,0 +1,12 @@ +{ + "name": "My entity action", + "version": "1.0.0", + "extensions": [ + { + "type": "bundle", + "alias": "Umb.EntityAction.Test.Bundle", + "name": "Test entity action bundle", + "js": "/App_Plugins/retrieve-action/retrieve-action.manifests.js" + } + ] +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/appsettings.json new file mode 100644 index 000000000000..49d90bb5936e --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/AdditionalSetup/appsettings.json @@ -0,0 +1,58 @@ +{ + "$schema": "appsettings-schema.json", + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System": "Warning" + } + }, + "WriteTo": [ + { + "Name": "Async", + "Args": { + "Configure": [ + { + "Name": "Console" + } + ] + } + } + ] + }, + "Umbraco": { + "CMS": { + "Unattended": { + "InstallUnattended": true, + "UnattendedUserName": "Playwright Test", + "UnattendedUserEmail": "playwright@umbraco.com", + "UnattendedUserPassword": "UmbracoAcceptance123!" + }, + "Content": { + "ContentVersionCleanupPolicy": { + "EnableCleanup": false + } + }, + "Global": { + "DisableElectionForSingleServer": true, + "InstallMissingDatabase": true, + "Id": "00000000-0000-0000-0000-000000000042", + "VersionCheckPeriod": 0, + "UseHttps": true + }, + "HealthChecks": { + "Notification": { + "Enabled": false + } + }, + "KeepAlive": { + "DisableKeepAliveTask": true + }, + "WebRouting": { + "UmbracoApplicationUrl": "https://localhost:44331/" + } + } + } +} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomPropertyEditor.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomPropertyEditor.spec.ts new file mode 100644 index 000000000000..98323e988a1b --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/CustomPropertyEditor.spec.ts @@ -0,0 +1,87 @@ +import {expect} from '@playwright/test'; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +// Content +const contentName = 'TestContent'; +// DocumentType +const documentTypeName = 'TestDocumentTypeForContent'; +// DataType +const dataTypeName = 'CustomTextBox'; +const editorUiAlias = 'Custom.TextEditor'; +const editorAlias = 'Umbraco.TextBox'; +// Property Editor +const customPropertyEditorName = 'Custom Text Editor'; +// Test values +const testValue = 'This is a test value for the custom property editor'; + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.dataType.ensureNameNotExists(dataTypeName); +}); + +test('can add custom property editor to a document type', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoUi.goToBackOffice(); + await umbracoUi.dataType.goToSection(ConstantHelper.sections.settings); + + // Act + await umbracoUi.dataType.clickActionsMenuAtRoot(); + await umbracoUi.dataType.clickCreateActionMenuOption(); + await umbracoUi.dataType.clickDataTypeButton(); + await umbracoUi.dataType.enterDataTypeName(dataTypeName); + await umbracoUi.dataType.clickSelectAPropertyEditorButton(); + await umbracoUi.dataType.selectAPropertyEditor(customPropertyEditorName); + await umbracoUi.dataType.clickSaveButton(); + + // Assert + await umbracoUi.dataType.waitForDataTypeToBeCreated(); + await umbracoUi.dataType.isDataTypeTreeItemVisible(dataTypeName); + expect(await umbracoApi.dataType.doesNameExist(dataTypeName)).toBeTruthy(); +}); + +test('can select custom property editor in property editor picker on data type', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoApi.documentType.createDefaultDocumentType(documentTypeName); + const dataTypeId = await umbracoApi.dataType.create(dataTypeName, editorAlias, editorUiAlias, []); + await umbracoUi.goToBackOffice(); + await umbracoUi.documentType.goToSection(ConstantHelper.sections.settings); + + // Act + await umbracoUi.documentType.goToDocumentType(documentTypeName); + await umbracoUi.documentType.clickAddGroupButton(); + await umbracoUi.documentType.addPropertyEditor(dataTypeName); + await umbracoUi.documentType.enterGroupName('Content'); + await umbracoUi.documentType.clickSaveButton(); + + // Assert + await umbracoUi.documentType.waitForDocumentTypeToBeCreated(); + expect(await umbracoApi.documentType.doesNameExist(documentTypeName)).toBeTruthy(); + const documentTypeData = await umbracoApi.documentType.getByName(documentTypeName); + // Checks if the correct property was added to the document type + expect(documentTypeData.properties[0].dataType.id).toBe(dataTypeId); +}); + +test('can write and read value from custom property editor', async ({umbracoApi, umbracoUi}) => { + // Arrange + const dataTypeValue = [ + { + alias: "maxChars", + value: "100", + }, + ]; + const dataTypeId = await umbracoApi.dataType.create(dataTypeName, editorAlias, editorUiAlias, dataTypeValue); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeId); + await umbracoApi.document.createDocumentWithTextContent(contentName, documentTypeId, "Test content", dataTypeName); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.enterPropertyValue(dataTypeName, testValue); + await umbracoUi.content.clickSaveButton(); + + // Assert + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.values[0].value).toEqual(testValue); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/ReadOnlyGuardRules.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/ReadOnlyGuardRules.spec.ts new file mode 100644 index 000000000000..a4f4500df05e --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/ReadOnlyGuardRules.spec.ts @@ -0,0 +1,56 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +// Content +const contentName = 'TestContent'; +const contentText = 'This is test content text'; +// DocumentType +const documentTypeName = 'TestDocumentTypeForContent'; +// DataType +const dataTypeName = 'Textstring'; + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.language.ensureIsoCodeNotExists("da"); +}); + +test('can lock an invariant content node', {tag: '@release'}, async ({umbracoApi, umbracoUi}) => { + // Arrange + const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id); + await umbracoApi.document.createDocumentWithTextContent(contentName, documentTypeId, contentText, dataTypeName); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.isDocumentReadOnly(false); + await umbracoUi.content.clickWorkspaceActionMenuButton(); + await umbracoUi.content.clickLockActionMenuOption(); + + // Assert + await umbracoUi.content.isContentNameReadOnly(); + await umbracoUi.content.isDocumentReadOnly(true); + await umbracoUi.content.isPropertyEditorUiWithNameReadOnly("text-box"); +}); + +test('can lock a variant content node', async ({umbracoApi, umbracoUi}) => { + // Arrange + await umbracoApi.language.createDanishLanguage(); + const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName); + const documentTypeId = await umbracoApi.documentType.createVariantDocumentTypeWithInvariantPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id); + await umbracoApi.document.createDocumentWithEnglishCultureAndTextContent(contentName, documentTypeId, contentText, dataTypeName); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.isDocumentReadOnly(false); + await umbracoUi.content.clickWorkspaceActionMenuButton(); + await umbracoUi.content.clickLockActionMenuOption(); + + // Assert + await umbracoUi.content.isContentNameReadOnly(); + await umbracoUi.content.isDocumentReadOnly(true); + await umbracoUi.content.isPropertyEditorUiWithNameReadOnly("text-box"); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/RetrieveAction.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/RetrieveAction.spec.ts new file mode 100644 index 000000000000..155b0b39379e --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExtensionRegistry/RetrieveAction.spec.ts @@ -0,0 +1,48 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +// Content +const contentName = 'TestContent'; +// DocumentType +const documentTypeName = 'TestDocumentTypeForContent'; +// DataType +const dataTypeName = 'Textstring'; +//Media +const mediaName = 'TestMedia'; + +test.afterEach(async ({umbracoApi}) => { + await umbracoApi.document.ensureNameNotExists(contentName); + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + await umbracoApi.media.ensureNameNotExists(mediaName); +}); + +test('can retrieve unique id and entity type of document', async ({umbracoApi, umbracoUi}) => { + // Arrange + const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id); + const documentId = await umbracoApi.document.createDocumentWithTextContent(contentName, documentTypeId, 'Test content', dataTypeName); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.goToContentWithName(contentName); + await umbracoUi.content.clickWorkspaceActionMenuButton(); + await umbracoUi.content.clickEntityActionWithName('Retrieve'); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText('document_' + documentId); +}); + +test('can retrieve unique id and entity type of media', async ({umbracoApi, umbracoUi}) => { + // Arrange + const mediaId = await umbracoApi.media.createDefaultMediaWithImage(mediaName); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.media); + + // Act + await umbracoUi.media.goToMediaWithName(mediaName); + await umbracoUi.media.clickWorkspaceActionMenuButton(); + await umbracoUi.media.clickEntityActionWithName('Retrieve'); + + // Assert + await umbracoUi.media.doesSuccessNotificationHaveText('media_' + mediaId); +});