diff --git a/.gitignore b/.gitignore index ab0f1d7d46..84e93181a3 100644 --- a/.gitignore +++ b/.gitignore @@ -90,10 +90,7 @@ preserve.belle /src/Umbraco.Web.UI/appsettings.Local.json # Tests -cypress.env.json -/tests/Umbraco.Tests.AcceptanceTest/cypress/screenshots/ -/tests/Umbraco.Tests.AcceptanceTest/cypress/support/chainable.ts -/tests/Umbraco.Tests.AcceptanceTest/cypress/videos/ +/tests/Umbraco.Tests.AcceptanceTest/.env /tests/Umbraco.Tests.Integration.SqlCe/DatabaseContextTests.sdf /tests/Umbraco.Tests.Integration.SqlCe/[Uu]mbraco/[Dd]ata/TEMP/ /tests/Umbraco.Tests.Integration/appsettings.Tests.Local.json diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index c41d2550be..a60662cfd0 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -346,24 +346,10 @@ stages: - job: displayName: E2E Tests variables: - - name: Umbraco__CMS__Unattended__InstallUnattended # Windows only - value: true - - name: Umbraco__CMS__Unattended__UnattendedUserName # Windows only - value: Playwright Test - - name: Umbraco__CMS__Unattended__UnattendedUserEmail # Windows only - value: playwright@umbraco.com - - name: Umbraco__CMS__Unattended__UnattendedUserPassword # Windows only - value: UmbracoAcceptance123! - - name: Umbraco__CMS__Global__InstallMissingDatabase # Windows only - value: true - - name: UmbracoDatabaseServer # Windows only - value: (LocalDB)\MSSQLLocalDB - - name: UmbracoDatabaseName # Windows only - value: Playwright - - name: ConnectionStrings__umbracoDbDSN # Windows only - value: Server=$(UmbracoDatabaseServer);Database=$(UmbracoDatabaseName);Integrated Security=true; - - name: PLAYWRIGHT_BASE_URL - value: https://localhost:8443 + Umbraco__CMS__Unattended__UnattendedUserName: Playwright Test + Umbraco__CMS__Unattended__UnattendedUserPassword: UmbracoAcceptance123! + Umbraco__CMS__Unattended__UnattendedUserEmail: playwright@umbraco.com + ASPNETCORE_URLS: https://localhost:8443 strategy: matrix: Linux: @@ -373,6 +359,20 @@ stages: Windows: vmImage: 'windows-latest' DOTNET_GENERATE_ASPNET_CERTIFICATE: true # Automatically generate HTTPS development certificate on Windows + # Enable console logging in Release mode + Serilog__WriteTo__0__Name: Async + Serilog__WriteTo__0__Args__configure__0__Name: Console + # Set unattended install settings + Umbraco__CMS__Unattended__InstallUnattended: true + Umbraco__CMS__Global__InstallMissingDatabase: true + UmbracoDatabaseServer: (LocalDB)\MSSQLLocalDB + UmbracoDatabaseName: Playwright + ConnectionStrings__umbracoDbDSN: Server=$(UmbracoDatabaseServer);Database=$(UmbracoDatabaseName);Integrated Security=true; + # Custom Umbraco settings + Umbraco__CMS__Global__VersionCheckPeriod: 0 + Umbraco__CMS__Global__UseHttps: true + Umbraco__CMS__HealthChecks__Notification__Enabled: false + Umbraco__CMS__KeepAlive__DisableKeepAliveTask: true pool: vmImage: $(vmImage) steps: @@ -393,22 +393,19 @@ stages: "npm_e2e" | "$(Agent.OS)" "npm_e2e" path: $(npm_config_cache) - - task: PowerShell@2 + - pwsh: | + New-Item -Path "." -Name ".env" -ItemType "file" -Value "UMBRACO_USER_LOGIN=$(Umbraco__CMS__Unattended__UnattendedUserEmail) + UMBRACO_USER_PASSWORD=$(Umbraco__CMS__Unattended__UnattendedUserPassword) + URL=$(ASPNETCORE_URLS)" displayName: Generate .env - inputs: - targetType: inline - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/ - script: | - New-Item -Path "." -Name ".env" -ItemType "file" -Value "UMBRACO_USER_LOGIN=$(Umbraco__CMS__Unattended__UnattendedUserEmail) - UMBRACO_USER_PASSWORD=$(Umbraco__CMS__Unattended__UnattendedUserPassword) - URL=$(PLAYWRIGHT_BASE_URL)" + workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/ - script: npm ci --no-fund --no-audit --prefer-offline workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/ displayName: Run npm ci - - powershell: sqllocaldb start mssqllocaldb + - pwsh: sqllocaldb start mssqllocaldb displayName: Start localdb (Windows only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - - powershell: Invoke-Sqlcmd -Query "CREATE DATABASE $env:UmbracoDatabaseName" -ServerInstance $env:UmbracoDatabaseServer + - pwsh: Invoke-Sqlcmd -Query "CREATE DATABASE $env:UmbracoDatabaseName" -ServerInstance $env:UmbracoDatabaseServer displayName: Create database (Windows only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - task: UseDotNet@2 @@ -416,72 +413,52 @@ stages: inputs: version: $(dotnetVersion) includePreviewVersions: $(dotnetIncludePreviewVersions) - # Linux containers smooth - - task: PowerShell@2 + - pwsh: | + $sha = 'g$(Build.SourceVersion)'.substring(0, 8) + docker build -t $(dockerImageName):$sha -f $(dockerfile) . + mkdir -p $(Build.ArtifactStagingDirectory)/docker-images + docker save -o $(Build.ArtifactStagingDirectory)/docker-images/$(dockerImageName).$sha.tar $(dockerImageName):$sha + dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p $(Umbraco__CMS__Unattended__UnattendedUserPassword) + docker run --name $(dockerImageName) -dp 8080:5000 -dp 8443:5001 -e UMBRACO__CMS__GLOBAL__ID=$(UMBRACO__CMS__GLOBAL__ID) -e ASPNETCORE_Kestrel__Certificates__Default__Password="$(Umbraco__CMS__Unattended__UnattendedUserPassword)" -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx -v ${HOME}/.aspnet/https:/https/ $(dockerImageName):$sha + docker ps condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - displayName: Build & run container (Linux only) - inputs: - workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc - targetType: inline - script: | - $sha = 'g$(Build.SourceVersion)'.substring(0, 8) - docker build -t $(dockerImageName):$sha -f $(dockerfile) . - mkdir -p $(Build.ArtifactStagingDirectory)/docker-images - docker save -o $(Build.ArtifactStagingDirectory)/docker-images/$(dockerImageName).$sha.tar $(dockerImageName):$sha - dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p UmbracoAcceptance123! - docker run --name $(dockerImageName) -dp 8080:5000 -dp 8443:5001 -e UMBRACO__CMS__GLOBAL__ID=$(UMBRACO__CMS__GLOBAL__ID) -e ASPNETCORE_Kestrel__Certificates__Default__Password="UmbracoAcceptance123!" -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx -v ${HOME}/.aspnet/https:/https/ $(dockerImageName):$sha - docker ps - # Urls matching docker setup. - - task: PowerShell@2 + displayName: Build and run container (Linux only) + workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc + - pwsh: | + dotnet new --install ./nupkg/Umbraco.Templates.*.nupkg + dotnet new umbraco --name Playwright --no-restore --output . + dotnet restore --configfile ./nuget.config + dotnet build --configuration $(buildConfiguration) --no-restore + dotnet dev-certs https + $process = Start-Process -FilePath "dotnet" -ArgumentList "run --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log + Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - displayName: Build & run app (Windows only) - inputs: - workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc - targetType: inline - script: | - dotnet new --install ./nupkg/Umbraco.Templates.*.nupkg - dotnet new umbraco --name Playwright --no-restore --output . - dotnet restore --configfile ./nuget.config - dotnet build --configuration $(buildConfiguration) --no-restore - dotnet dev-certs https - Start-Process -FilePath "dotnet" -ArgumentList "run --configuration $(buildConfiguration) --no-build --no-launch-profile --urls $(PLAYWRIGHT_BASE_URL)" - - task: PowerShell@2 + displayName: Build and run app (Windows only) + workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc + - pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS) displayName: Wait for app - inputs: - targetType: inline - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - script: | - npm i -g wait-on - wait-on -v --interval 1000 --timeout 120000 $(PLAYWRIGHT_BASE_URL) - - task: PowerShell@2 + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + - pwsh: npx playwright install --with-deps displayName: Install Playwright - inputs: - targetType: inline - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - script: npx playwright install - - task: PowerShell@2 + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + - pwsh: npm run test --ignore-certificate-errors --output $(Build.ArtifactStagingDirectory)\test-results displayName: Run Playwright (Desktop) continueOnError: true - inputs: - targetType: inline - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - script: 'npm run test --ignore-certificate-errors' - - task: PowerShell@2 - displayName: Check if artifacts folder exists - inputs: - targetType: inline - script: | - $MyVariable = Test-Path -Path $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/results - Write-Host "##vso[task.setvariable variable=resultFolderExists;]$MyVariable" - - task: CopyFiles@2 - displayName: Prepare artifacts - condition: eq(variables.resultFolderExists, 'True') - inputs: - sourceFolder: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/results/ - targetFolder: $(Build.ArtifactStagingDirectory)/playwright + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + env: + CI: true + PLAYWRIGHT_HTML_REPORT: $(Build.ArtifactStagingDirectory)\playwright-report + - pwsh: | + docker logs $(dockerImageName) > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 + docker stop $(dockerImageName) + condition: eq(variables['Agent.OS'], 'Linux') + displayName: Stop app (Linux only) + - pwsh: Stop-Process $env:AcceptanceTestProcessId + condition: eq(variables['Agent.OS'], 'Windows_NT') + displayName: Stop app (Windows only) - task: PublishPipelineArtifact@1 - displayName: "Publish test artifacts" - condition: eq(variables.resultFolderExists, 'True') + condition: always() + displayName: Publish test artifacts inputs: targetPath: $(Build.ArtifactStagingDirectory) artifact: 'E2E artifacts - $(Agent.OS) - Attempt #$(System.JobAttempt)' diff --git a/src/Umbraco.Cms/Umbraco.Cms.csproj b/src/Umbraco.Cms/Umbraco.Cms.csproj index 85aa303739..b80c033601 100644 --- a/src/Umbraco.Cms/Umbraco.Cms.csproj +++ b/src/Umbraco.Cms/Umbraco.Cms.csproj @@ -19,4 +19,14 @@ + + + + $(MSBuildThisFileDirectory)appsettings-schema.json + $(MSBuildThisFileDirectory)..\JsonSchema\ + + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html index f161c76ee0..8215c66691 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html @@ -11,17 +11,19 @@ data-element="property-{{property.alias}}" ng-repeat="property in tab.properties track by property.alias" property="property" + node="contentNodeModel" show-inherit="contentNodeModel.variants.length > 1 && property.variation !== 'CultureAndSegment'" inherits-from="defaultVariant.displayName"> - + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js index 5dd205790c..3114d0d1ce 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/apps/listview/listview.controller.js @@ -9,9 +9,9 @@ function propertyEditorReadonly () { // check for permission to update - return !(typeof $scope.variantContent !== 'undefined' && $scope.variantContent.allowedActions.includes('A')); + return $scope.variantContent && !$scope.variantContent.allowedActions.includes('A'); } - + } angular.module("umbraco").controller("Umbraco.Editors.Content.Apps.ListViewController", ContentAppListViewController); diff --git a/src/Umbraco.Web.UI.Client/src/views/dataTypes/views/datatype.settings.html b/src/Umbraco.Web.UI.Client/src/views/dataTypes/views/datatype.settings.html index b4422dc49e..f0eb5b535b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dataTypes/views/datatype.settings.html +++ b/src/Umbraco.Web.UI.Client/src/views/dataTypes/views/datatype.settings.html @@ -8,9 +8,9 @@ -
@@ -23,7 +23,7 @@ ng-click="vm.openPropertyEditorPicker()"> -
+
Required diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/views/macro.settings.controller.js b/src/Umbraco.Web.UI.Client/src/views/macros/views/macro.settings.controller.js index 5f633a6e4b..fd7889a7a6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/views/macro.settings.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/macros/views/macro.settings.controller.js @@ -14,11 +14,11 @@ function MacrosSettingsController($scope, editorService, localizationService) { //vm.removeMacroView = removeMacroView; $scope.model.openViewPicker = openViewPicker; $scope.model.removeMacroView = removeMacroView; - var labels = {}; - + vm.macroPartialViewPickerProperty = { alias : "macroPartialViewPickerProperty", description: "", label: "Macro partial view", validation: {mandatory : true}} localizationService.localizeMany(["macro_selectViewFile"]).then(function(data) { labels.selectViewFile = data[0]; + vm.macroPartialViewPickerProperty.description = data[0]; }); function openViewPicker() { @@ -45,7 +45,7 @@ function MacrosSettingsController($scope, editorService, localizationService) { name: $scope.model.macro.view }; - //$scope.model.submit($scope.model); + //$scope.model.submit($scope.model); editorService.close(); }, @@ -63,7 +63,7 @@ function MacrosSettingsController($scope, editorService, localizationService) { } function init() { - + } init(); diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html b/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html index c152c33193..9b79061654 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/views/settings.html @@ -5,10 +5,9 @@
- - - + + - + - +
+ + Required + +
+ +
diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker index 97777a0865..e19483cfa1 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker +++ b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker @@ -27,7 +27,11 @@ FROM mcr.microsoft.com/dotnet/nightly/aspnet:7.0 AS run WORKDIR /app COPY --from=build dist . -ENV ASPNETCORE_URLS="http://0.0.0.0:5000;https://0.0.0.0:5001" +# Enable console logging in Release mode +ENV Serilog__WriteTo__0__Name=Async +ENV Serilog__WriteTo__0__Args__configure__0__Name=Console + +# Set unattended install settings ENV ConnectionStrings__umbracoDbDSN_ProviderName="Microsoft.Data.Sqlite" ENV ConnectionStrings__umbracoDbDSN="Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True" ENV Umbraco__CMS__Unattended__InstallUnattended="true" @@ -35,4 +39,13 @@ ENV Umbraco__CMS__Unattended__UnattendedUserName="Playwright Test" ENV Umbraco__CMS__Unattended__UnattendedUserEmail="playwright@umbraco.com" ENV Umbraco__CMS__Unattended__UnattendedUserPassword="UmbracoAcceptance123!" +# Custom Umbraco settings +ENV Umbraco__CMS__Global__VersionCheckPeriod="0" +ENV Umbraco__CMS__Global__UseHttps="true" +ENV Umbraco__CMS__HealthChecks__Notification__Enabled="false" +ENV Umbraco__CMS__KeepAlive__DisableKeepAliveTask="true" + +# Set application URL +ENV ASPNETCORE_URLS="http://0.0.0.0:5000;https://0.0.0.0:5001" + CMD dotnet Playwright.dll diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts index 424b1310fb..e41e18e053 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts @@ -29,7 +29,7 @@ test.describe('Media', () => { {fileTypeNames: imageName}, {fileTypeNames: vectorGraphicsName}, {fileTypeNames: videoName} - ] + ]; await umbracoApi.media.deleteAllFiles(articleName,audioName,fileName,folderName,imageName,vectorGraphicsName,videoName); await umbracoApi.media.ensureNameNotExists(folderToMoveTooName); @@ -54,7 +54,8 @@ test.describe('Media', () => { await page.locator('[label-key="general_submit"]').click(); // Assert - await page.waitForTimeout(500); + // Needs to wait before refreshing the media tree, otherwise the media files wont be moved to the folder yet + await page.waitForTimeout(1000); await umbracoUi.refreshMediaTree(); await page.locator('[data-element="tree-item-' + folderToMoveTooName + '"]').click(); for (const names of mediaFileTypes) { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 29f2c2367e..884fc5ed70 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -3,6 +3,7 @@ Umbraco.Cms.Tests.Integration Umbraco CMS - Integration tests Contains helper classes for integration tests with Umbraco CMS, including all internal integration tests. + true true Umbraco.Cms.Tests.Integration