diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index c1cec3b15a..b53a2d0ad2 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -522,160 +522,72 @@ stages: # Connection string CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=Umbraco;Mode=Memory;Cache=Shared;Foreign Keys=True;Pooling=True CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.Sqlite + DatabaseType: SQLite + additionalEnvironmentVariables: false strategy: matrix: LinuxPart1Of3: vmImage: "ubuntu-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTestSqlite -- --shard=1/3" LinuxPart2Of3: vmImage: "ubuntu-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTestSqlite -- --shard=2/3" LinuxPart3Of3: vmImage: "ubuntu-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTestSqlite -- --shard=3/3" WindowsPart1Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTestSqlite -- --shard=1/3" WindowsPart2Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTestSqlite -- --shard=2/3" WindowsPart3Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTestSqlite -- --shard=3/3" pool: vmImage: $(vmImage) steps: - # Setup test environment - - task: DownloadPipelineArtifact@2 - displayName: Download NuGet artifacts - inputs: - artifact: nupkg - path: $(Agent.BuildDirectory)/app/nupkg - - - task: NodeTool@0 - displayName: Use Node.js $(nodeVersion) - retryCountOnTaskFailure: 3 - inputs: - versionSpec: $(nodeVersion) - - - task: UseDotNet@2 - displayName: Use .NET SDK from global.json - inputs: - useGlobalJson: true + # Setup test environment Template + - template: nightly-E2E-setup-template.yml + parameters: + nodeVersion: ${{ variables.nodeVersion }} + PlaywrightUserEmail: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL }} + PlaywrightPassword: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD }} + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + npm_config_cache: ${{ variables.npm_config_cache }} - pwsh: | - "UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL) - UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) - URL=$(ASPNETCORE_URLS) - STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json - CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env - displayName: Generate .env - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - - # Cache and restore NPM packages - - task: Cache@2 - displayName: Cache NPM packages - inputs: - key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' - restoreKeys: | - npm_e2e | "$(Agent.OS)" - npm_e2e - path: $(npm_config_cache) - - - script: npm ci --no-fund --no-audit --prefer-offline - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - displayName: Restore NPM packages - - # Build application - - pwsh: | - $cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g" - dotnet new nugetconfig - dotnet nuget add source ./nupkg --name Local - dotnet new install Umbraco.Templates::$cmsVersion - dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check dotnet restore UmbracoProject cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject - dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore + displayName: Restore project + workingDirectory: $(Agent.BuildDirectory)/app + + - pwsh: | + dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore dotnet dev-certs https displayName: Build application workingDirectory: $(Agent.BuildDirectory)/app + condition: succeeded() - # Run application - - bash: | - nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & - echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!" - displayName: Run application (Linux) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - workingDirectory: $(Agent.BuildDirectory)/app + # Run application Template + - template: nightly-E2E-run-application-template.yml + parameters: + DatabaseType: ${{ variables.DatabaseType }} + buildConfiguration: ${{ variables.buildConfiguration }} + additionalEnvironmentVariables: ${{ variables.additionalEnvironmentVariables }} - - pwsh: | - $process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log - Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" - displayName: Run application (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - workingDirectory: $(Agent.BuildDirectory)/app - - # Wait for application to start responding to requests - - pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS) - displayName: Wait for application - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Install Playwright and dependencies - - pwsh: npx playwright install chromium - displayName: Install Playwright only with Chromium browser - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Test - - pwsh: $(testCommand) - displayName: Run Playwright tests - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - env: - CI: true - CommitId: $(Build.SourceVersion) - AgentOs: $(Agent.OS) - - # Stop application - - bash: kill -15 $(AcceptanceTestProcessId) - displayName: Stop application (Linux) - condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux')) - - - pwsh: Stop-Process -Id $(AcceptanceTestProcessId) - displayName: Stop application (Windows) - condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT')) - - # Copy artifacts - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse - } - displayName: Copy Playwright results - condition: succeededOrFailed() - - # Copy console error log - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory) - } - displayName: Copy console error log - condition: succeededOrFailed() - - # Publish test artifacts - - task: PublishPipelineArtifact@1 - displayName: Publish test artifacts - condition: succeededOrFailed() - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" - - # Publish test results - - task: PublishTestResults@2 - displayName: "Publish test results" - condition: succeededOrFailed() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: '*.xml' - searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" - testRunTitle: "$(Agent.JobName)" + # Run tests Template + - template: nightly-E2E-run-tests-template.yml + parameters: + testCommand: $(testCommand) + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + DatabaseType: ${{ variables.DatabaseType }} - job: displayName: E2E Smoke Tests (SQL Server) @@ -683,354 +595,78 @@ stages: # Connection string CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient + SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) + DatabaseType: SQLServer + additionalEnvironmentVariables: false strategy: matrix: ${{ if eq(parameters.sqlServerLinuxAcceptanceTests, True) }}: LinuxPart1Of3: testCommand: "npm run smokeTest -- --shard=1/3" vmImage: "ubuntu-latest" - SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) + testFolder: "DefaultConfig" CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" LinuxPart2Of3: testCommand: "npm run smokeTest -- --shard=2/3" vmImage: "ubuntu-latest" - SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) + testFolder: "DefaultConfig" CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" LinuxPart3Of3: testCommand: "npm run smokeTest -- --shard=3/3" vmImage: "ubuntu-latest" - SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) + testFolder: "DefaultConfig" CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" WindowsPart1Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTest -- --shard=1/3" WindowsPart2Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTest -- --shard=2/3" WindowsPart3Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run smokeTest -- --shard=3/3" pool: vmImage: $(vmImage) steps: - # Setup test environment - - task: DownloadPipelineArtifact@2 - displayName: Download NuGet artifacts - inputs: - artifact: nupkg - path: $(Agent.BuildDirectory)/app/nupkg - - - task: NodeTool@0 - displayName: Use Node.js $(nodeVersion) - inputs: - versionSpec: $(nodeVersion) - - - task: UseDotNet@2 - displayName: Use .NET SDK from global.json - inputs: - useGlobalJson: true + # Setup test environment Template + - template: nightly-E2E-setup-template.yml + parameters: + nodeVersion: ${{ variables.nodeVersion }} + PlaywrightUserEmail: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL }} + PlaywrightPassword: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD }} + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + npm_config_cache: ${{ variables.npm_config_cache }} - pwsh: | - "UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL) - UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) - URL=$(ASPNETCORE_URLS) - STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json - CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env - displayName: Generate .env - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - - # Cache and restore NPM packages - - task: Cache@2 - displayName: Cache NPM packages - inputs: - key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' - restoreKeys: | - npm_e2e | "$(Agent.OS)" - npm_e2e - path: $(npm_config_cache) - - - script: npm ci --no-fund --no-audit --prefer-offline - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - displayName: Restore NPM packages - - # Build application - - pwsh: | - $cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g" - dotnet new nugetconfig - dotnet nuget add source ./nupkg --name Local - dotnet new install Umbraco.Templates::$cmsVersion - dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check dotnet restore UmbracoProject cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject - dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore + displayName: Restore project + workingDirectory: $(Agent.BuildDirectory)/app + + - pwsh: | + dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore dotnet dev-certs https displayName: Build application workingDirectory: $(Agent.BuildDirectory)/app + condition: succeeded() - # Start SQL Server - - powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest - displayName: Start SQL Server Docker image (Linux) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + # Run application Template + - template: nightly-E2E-run-application-template.yml + parameters: + SA_PASSWORD: ${{ variables.SA_PASSWORD }} + buildConfiguration: ${{ variables.buildConfiguration }} + DatabaseType: ${{ variables.DatabaseType }} + additionalEnvironmentVariables: ${{ variables.additionalEnvironmentVariables }} - - pwsh: SqlLocalDB start MSSQLLocalDB - displayName: Start SQL Server LocalDB (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - - # Run application - - bash: | - nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & - echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!" - displayName: Run application (Linux) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - workingDirectory: $(Agent.BuildDirectory)/app - - - pwsh: | - $process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log - Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" - displayName: Run application (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - workingDirectory: $(Agent.BuildDirectory)/app - - # Wait for application to start responding to requests - - pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS) - displayName: Wait for application - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Install Playwright and dependencies - - pwsh: npx playwright install chromium - displayName: Install Playwright only with Chromium browser - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Test - - pwsh: $(testCommand) - displayName: Run Playwright tests - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - env: - CI: true - CommitId: $(Build.SourceVersion) - AgentOs: $(Agent.OS) - - # Stop application - - bash: kill -15 $(AcceptanceTestProcessId) - displayName: Stop application (Linux) - condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux')) - - - pwsh: Stop-Process -Id $(AcceptanceTestProcessId) - displayName: Stop application (Windows) - condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT')) - - # Stop SQL Server - - pwsh: docker stop mssql - displayName: Stop SQL Server Docker image (Linux) - condition: eq(variables['Agent.OS'], 'Linux') - - - pwsh: SqlLocalDB stop MSSQLLocalDB - displayName: Stop SQL Server LocalDB (Windows) - condition: eq(variables['Agent.OS'], 'Windows_NT') - - # Copy artifacts - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse - } - displayName: Copy Playwright results - condition: succeededOrFailed() - - # Copy console error log - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory) - } - displayName: Copy console error log - condition: succeededOrFailed() - - # Publish test artifacts - - task: PublishPipelineArtifact@1 - displayName: Publish test artifacts - condition: succeededOrFailed() - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" - - # Publish test results - - task: PublishTestResults@2 - displayName: "Publish test results" - condition: succeededOrFailed() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: '*.xml' - searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" - testRunTitle: "$(Agent.JobName)" - - - job: - displayName: E2E Release Tests (SQL Server) - variables: - # Connection string - CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True - CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient - condition: eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True') - strategy: - matrix: - WindowsPart1Of3: - vmImage: "windows-latest" - testCommand: "npm run releaseTest -- --shard=1/3" - WindowsPart2Of3: - vmImage: "windows-latest" - testCommand: "npm run releaseTest -- --shard=2/3" - WindowsPart3Of3: - vmImage: "windows-latest" - testCommand: "npm run releaseTest -- --shard=3/3" - pool: - vmImage: $(vmImage) - steps: - # Setup test environment - - task: DownloadPipelineArtifact@2 - displayName: Download NuGet artifacts - inputs: - artifact: nupkg - path: $(Agent.BuildDirectory)/app/nupkg - - - task: NodeTool@0 - displayName: Use Node.js $(nodeVersion) - inputs: - versionSpec: $(nodeVersion) - - - task: UseDotNet@2 - displayName: Use .NET SDK from global.json - inputs: - useGlobalJson: true - - - pwsh: | - "UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL) - UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) - URL=$(ASPNETCORE_URLS) - STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json - CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env - displayName: Generate .env - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - - # Cache and restore NPM packages - - task: Cache@2 - displayName: Cache NPM packages - inputs: - key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' - restoreKeys: | - npm_e2e | "$(Agent.OS)" - npm_e2e - path: $(npm_config_cache) - - - script: npm ci --no-fund --no-audit --prefer-offline - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - displayName: Restore NPM packages - - # Build application - - pwsh: | - $cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g" - dotnet new nugetconfig - dotnet nuget add source ./nupkg --name Local - dotnet new install Umbraco.Templates::$cmsVersion - dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check - dotnet restore UmbracoProject - cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject - dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore - dotnet dev-certs https - displayName: Build application - workingDirectory: $(Agent.BuildDirectory)/app - - # Start SQL Server - - powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest - displayName: Start SQL Server Docker image (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')) - - # Run application - - bash: | - nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & - echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!" - displayName: Run application (Linux) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - workingDirectory: $(Agent.BuildDirectory)/app - - - pwsh: | - $process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log - Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" - displayName: Run application (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - workingDirectory: $(Agent.BuildDirectory)/app - - # Wait for application to start responding to requests - - pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS) - displayName: Wait for application - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Install Playwright and dependencies - - pwsh: npx playwright install chromium - displayName: Install Playwright only with Chromium browser - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Test - - pwsh: $(testCommand) - displayName: Run Playwright tests - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - env: - CI: true - CommitId: $(Build.SourceVersion) - AgentOs: $(Agent.OS) - - # Stop application - - bash: kill -15 $(AcceptanceTestProcessId) - displayName: Stop application (Linux) - condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux')) - - - pwsh: Stop-Process -Id $(AcceptanceTestProcessId) - displayName: Stop application (Windows) - condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT')) - - # Stop SQL Server - - pwsh: docker stop mssql - displayName: Stop SQL Server Docker image (Linux) - condition: eq(variables['Agent.OS'], 'Linux') - - - pwsh: SqlLocalDB stop MSSQLLocalDB - displayName: Stop SQL Server LocalDB (Windows) - condition: eq(variables['Agent.OS'], 'Windows_NT') - - # Copy artifacts - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse - } - displayName: Copy Playwright results - condition: succeededOrFailed() - - # Copy console error log - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory) - } - displayName: Copy console error log - condition: succeededOrFailed() - - # Publish test artifacts - - task: PublishPipelineArtifact@1 - displayName: Publish test artifacts - condition: succeededOrFailed() - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" - - # Publish test results - - task: PublishTestResults@2 - displayName: "Publish test results" - condition: succeededOrFailed() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: '*.xml' - searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" - testRunTitle: "$(Agent.JobName)" + # Run tests Template + - template: nightly-E2E-run-tests-template.yml + parameters: + testCommand: $(testCommand) + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + DatabaseType: ${{ variables.DatabaseType }} ############################################### ## Release @@ -1237,4 +873,4 @@ stages: storage: umbracoapidocs ContainerName: "$web" BlobPrefix: v$(umbracoMajorVersion)/ui-api - CleanTargetBeforeCopy: true \ No newline at end of file + CleanTargetBeforeCopy: true diff --git a/build/nightly-E2E-build-template.yml b/build/nightly-E2E-build-template.yml new file mode 100644 index 0000000000..4ec5f299be --- /dev/null +++ b/build/nightly-E2E-build-template.yml @@ -0,0 +1,74 @@ +parameters: + - name: testFolder + type: string + default: '' + + - name: buildConfiguration + type: string + default: '' + + - name: additionalEnvironmentVariables + type: string + default: 'false' + +steps: + - pwsh: | + dotnet restore UmbracoProject + cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject + displayName: Restore project + workingDirectory: $(Agent.BuildDirectory)/app + + # Update application to use necessary app settings + - pwsh: | + $sourcePath = "$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/tests/${{ parameters.testFolder }}/AdditionalSetup" + $destinationPath = "UmbracoProject" + $jsonFiles = Get-ChildItem -Path $sourcePath -Filter "*.json" + if ($jsonFiles) { + $jsonFiles | ForEach-Object { + Write-Host "Copying: $($_.FullName)" + Copy-Item -Path $_.FullName -Destination $destinationPath -Force + } + } else { + Write-Host "No JSON files found." + } + displayName: Update application to use necessary app settings + workingDirectory: $(Agent.BuildDirectory)/app + + # Update application to use necessary App_Plugins + - pwsh: | + $sourcePath = "$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/tests/${{ parameters.testFolder }}/AdditionalSetup" + $destinationPath = "UmbracoProject" + $appPluginsFolders = Get-ChildItem -Path $sourcePath -Directory -Filter "App_Plugins" + if ($appPluginsFolders) { + foreach ($folder in $appPluginsFolders) { + Write-Host "Copying folder: $($folder.FullName)" + Copy-Item -Path $folder.FullName -Destination $destinationPath -Recurse -Force + } + } else { + Write-Host "No App_Plugins found." + } + displayName: Update application to use necessary app plugins + workingDirectory: $(Agent.BuildDirectory)/app + + # Update application to use necessary classes + - pwsh: | + $sourcePath = "$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/tests/${{ parameters.testFolder }}/AdditionalSetup" + $destinationPath = "UmbracoProject" + $csharpFiles = Get-ChildItem -Path $sourcePath -Filter "*.cs" + if ($csharpFiles) { + $csharpFiles | ForEach-Object { + Write-Host "Copying: $($_.FullName)" + Copy-Item -Path $_.FullName -Destination $destinationPath -Force + } + } else { + Write-Host "No C# files found." + } + displayName: Update application to use necessary classes + workingDirectory: $(Agent.BuildDirectory)/app + + - pwsh: | + dotnet build UmbracoProject --configuration ${{ parameters.buildConfiguration }} --no-restore + dotnet dev-certs https + displayName: Build application + workingDirectory: $(Agent.BuildDirectory)/app + condition: and(succeeded(), eq(variables['additionalEnvironmentVariables'], 'false')) diff --git a/build/nightly-E2E-run-application-template.yml b/build/nightly-E2E-run-application-template.yml new file mode 100644 index 0000000000..5301ddeaf1 --- /dev/null +++ b/build/nightly-E2E-run-application-template.yml @@ -0,0 +1,45 @@ +parameters: + - name: SA_PASSWORD + type: string + default: '' + + - name: buildConfiguration + type: string + default: '' + + - name: additionalEnvironmentVariables + type: string + default: 'false' + + - name: DatabaseType + type: string + default: '' + +steps: + # Skips the SQLServer setup if the databaseType does not match + - ${{ if eq(parameters.DatabaseType, 'SQLServer') }}: + # Start SQL Server Linux + - powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=${{ parameters.SA_PASSWORD }}" mcr.microsoft.com/mssql/server:2022-latest + displayName: Start SQL Server Docker image (Linux) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + + # Start SQL Server LocalDB Windows + - pwsh: SqlLocalDB start MSSQLLocalDB + displayName: Start SQL Server LocalDB (Windows) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + + # Run application for Linux + - bash: | + nohup dotnet run --project UmbracoProject --configuration ${{ parameters.buildConfiguration }} --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & + echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!" + displayName: Run application (Linux) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['additionalEnvironmentVariables'], 'false')) + workingDirectory: $(Agent.BuildDirectory)/app + + # Run application for Windows + - pwsh: | + $process = Start-Process dotnet "run --project UmbracoProject --configuration ${{ parameters.buildConfiguration }} --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log + Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" + displayName: Run application (Windows) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['additionalEnvironmentVariables'], 'false')) + workingDirectory: $(Agent.BuildDirectory)/app diff --git a/build/nightly-E2E-run-tests-template.yml b/build/nightly-E2E-run-tests-template.yml new file mode 100644 index 0000000000..c424931d8a --- /dev/null +++ b/build/nightly-E2E-run-tests-template.yml @@ -0,0 +1,106 @@ +parameters: + - name: ASPNETCORE_URLS + type: string + default: '' + + - name: testCommand + type: string + default: '' + + - name: port + type: string + default: '' + + - name: AZUREB2CTESTUSEREMAIL + type: string + default: '' + + - name: AZUREB2CTESTUSERPASSWORD + type: string + default: '' + + - name: DatabaseType + type: string + default: '' + +steps: + # Ensures we have the package wait-on installed + - pwsh: npm install wait-on + displayName: Install wait-on package + + # Wait for either the port of the aspnetcore url + - pwsh: | + $Port = "${{ parameters.port }}" + $Url = "${{ parameters.ASPNETCORE_URLS }}" + + if ($Port -ne "") { + Write-Host "Waiting on TCP port $Port" + npx wait-on -v --interval 1000 --timeout 120000 "tcp:$Port" + } else { + Write-Host "Waiting on URL $Url" + npx wait-on -v --interval 1000 --timeout 120000 "$Url" + } + displayName: Wait for application + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + + # Install Playwright and dependencies + - pwsh: npx playwright install chromium + displayName: Install Playwright only with Chromium browser + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + + # Test + - pwsh: ${{ parameters.testCommand }} + displayName: Run Playwright tests + continueOnError: true + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + env: + CI: true + CommitId: $(Build.SourceVersion) + AgentOs: $(Agent.OS) + AZUREADB2CTESTUSEREMAIL: ${{ parameters.AZUREB2CTESTUSEREMAIL }} + AZUREADB2CTESTUSERPASSWORD: ${{ parameters.AZUREB2CTESTUSERPASSWORD }} + + # Stop application + - bash: kill -15 $(AcceptanceTestProcessId) + displayName: Stop application (Linux) + condition: and(succeededOrFailed(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux')) + + - pwsh: Stop-Process -Id $(AcceptanceTestProcessId) + displayName: Stop application (Windows) + condition: and(succeededOrFailed(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT')) + + - ${{ if eq(parameters.DatabaseType, 'SQLServer') }}: + # Stop SQL Server + - pwsh: docker stop mssql + displayName: Stop SQL Server Docker image (Linux) + condition: and(succeededOrFailed(), eq(variables['Agent.OS'], 'Linux')) + + - pwsh: SqlLocalDB stop MSSQLLocalDB + displayName: Stop SQL Server LocalDB (Windows) + condition: and(succeededOrFailed(), eq(variables['Agent.OS'], 'Windows_NT')) + + # Copy artifacts + - pwsh: | + if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) { + Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse + } + displayName: Copy Playwright results + condition: succeededOrFailed() + + # Publish + - task: PublishPipelineArtifact@1 + displayName: Publish test artifacts + condition: succeededOrFailed() + inputs: + targetPath: $(Build.ArtifactStagingDirectory) + artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" + + # Publish test results + - task: PublishTestResults@2 + displayName: "Publish test results" + condition: succeededOrFailed() + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: '*.xml' + searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" + testRunTitle: "$(Agent.JobName)" diff --git a/build/nightly-E2E-setup-template.yml b/build/nightly-E2E-setup-template.yml new file mode 100644 index 0000000000..8085561900 --- /dev/null +++ b/build/nightly-E2E-setup-template.yml @@ -0,0 +1,70 @@ +parameters: + - name: nodeVersion + type: string + default: '' + + - name: PlaywrightUserEmail + type: string + default: '' + + - name: PlaywrightPassword + type: string + default: '' + + - name: ASPNETCORE_URLS + type: string + default: '' + + - name: npm_config_cache + type: string + default: '' + +steps: + - task: DownloadPipelineArtifact@2 + displayName: Download NuGet artifacts + inputs: + artifact: nupkg + path: $(Agent.BuildDirectory)/app/nupkg + + - task: NodeTool@0 + displayName: Use Node.js $(nodeVersion) + inputs: + versionSpec: $(nodeVersion) + + - task: UseDotNet@2 + displayName: Use .NET SDK from global.json + inputs: + useGlobalJson: true + + - pwsh: | + "UMBRACO_USER_LOGIN=${{ parameters.PlaywrightUserEmail }} + UMBRACO_USER_PASSWORD=${{ parameters.PlaywrightPassword }} + URL=${{ parameters.ASPNETCORE_URLS }} + STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json + CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env + displayName: Generate .env + workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest + + # Cache and restore NPM packages + - task: Cache@2 + displayName: Cache NPM packages + inputs: + key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' + restoreKeys: | + npm_e2e | "$(Agent.OS)" + npm_e2e + path: ${{ parameters.npm_config_cache }} + + - script: npm ci --no-fund --no-audit --prefer-offline + workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest + displayName: Restore NPM packages + + # Install Template + - pwsh: | + $cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g" + dotnet new nugetconfig + dotnet nuget add source ./nupkg --name Local + dotnet new install Umbraco.Templates::$cmsVersion + dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check + displayName: Install Template + workingDirectory: $(Agent.BuildDirectory)/app diff --git a/build/nightly-E2E-test-pipelines.yml b/build/nightly-E2E-test-pipelines.yml index 73034b6d7c..7bbdec8e14 100644 --- a/build/nightly-E2E-test-pipelines.yml +++ b/build/nightly-E2E-test-pipelines.yml @@ -12,9 +12,18 @@ schedules: - main parameters: - # Skipped due to DB locks - - name: sqliteAcceptanceTests - displayName: Run SQLite Acceptance Tests + - name: skipIntegrationTests + displayName: Skip integration tests + type: boolean + default: false + + - name: differentAppSettingsAcceptanceTests + displayName: Run acceptance tests with different app settings + type: boolean + default: false + + - name: skipDefaultConfigAcceptanceTests + displayName: Skip tests with DefaultConfig type: boolean default: false @@ -100,8 +109,191 @@ stages: targetPath: $(Build.ArtifactStagingDirectory)/npm artifactName: npm - - stage: E2E - displayName: E2E Tests + - stage: Integration + displayName: Integration Tests + dependsOn: Build + condition: ${{ eq(parameters.skipIntegrationTests, false) }} + jobs: + # Integration Tests (SQLite) + - job: + timeoutInMinutes: 180 + displayName: Integration Tests (SQLite) + strategy: + matrix: + # Windows: + # vmImage: 'windows-latest' + # We split the tests into 3 parts for each OS to reduce the time it takes to run them on the pipeline + LinuxPart1Of3: + vmImage: "ubuntu-latest" + # Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace + testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)" + LinuxPart2Of3: + vmImage: "ubuntu-latest" + # Filter tests that are part of the Umbraco.Infrastructure.Service namespace + testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)" + LinuxPart3Of3: + vmImage: "ubuntu-latest" + # Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace + testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)" + macOSPart1Of3: + vmImage: "macOS-latest" + # Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace + testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)" + macOSPart2Of3: + vmImage: "macOS-latest" + # Filter tests that are part of the Umbraco.Infrastructure.Service namespace + testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)" + macOSPart3Of3: + vmImage: "macOS-latest" + # Filter tests that are not part of the Umbraco.Infrastructure namespace. + testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)" + pool: + vmImage: $(vmImage) + variables: + Tests__Database__DatabaseType: "Sqlite" + steps: + - checkout: self + submodules: false + lfs: false, + fetchDepth: 1 + fetchFilter: tree:0 + # Setup test environment + - task: DownloadPipelineArtifact@2 + displayName: Download build artifacts + inputs: + artifact: build_output + path: $(Build.SourcesDirectory) + + - task: UseDotNet@2 + displayName: Use .NET SDK from global.json + inputs: + useGlobalJson: true + + # Test + - task: DotNetCoreCLI@2 + displayName: Run dotnet test + inputs: + command: test + projects: "tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj" + testRunTitle: Integration Tests SQLite - $(Agent.OS) + arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build' + + # Integration Tests (SQL Server) + - job: + timeoutInMinutes: 180 + displayName: Integration Tests (SQL Server) + variables: + SA_PASSWORD: UmbracoAcceptance123! + strategy: + matrix: + # We split the tests into 3 parts for each OS to reduce the time it takes to run them on the pipeline + WindowsPart1Of3: + vmImage: "windows-latest" + Tests__Database__DatabaseType: LocalDb + Tests__Database__SQLServerMasterConnectionString: N/A + # Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace + testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)" + WindowsPart2Of3: + vmImage: "windows-latest" + Tests__Database__DatabaseType: LocalDb + Tests__Database__SQLServerMasterConnectionString: N/A + # Filter tests that are part of the Umbraco.Infrastructure.Service namespace + testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)" + WindowsPart3Of3: + vmImage: "windows-latest" + Tests__Database__DatabaseType: LocalDb + Tests__Database__SQLServerMasterConnectionString: N/A + # Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace + testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)" + LinuxPart1Of3: + vmImage: "ubuntu-latest" + Tests__Database__DatabaseType: SqlServer + Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" + # Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace + testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)" + LinuxPart2Of3: + vmImage: "ubuntu-latest" + Tests__Database__DatabaseType: SqlServer + Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" + # Filter tests that are part of the Umbraco.Infrastructure.Service namespace + testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)" + LinuxPart3Of3: + vmImage: "ubuntu-latest" + Tests__Database__DatabaseType: SqlServer + Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" + # Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace + testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)" + pool: + vmImage: $(vmImage) + steps: + # Setup test environment + - task: DownloadPipelineArtifact@2 + displayName: Download build artifacts + inputs: + artifact: build_output + path: $(Build.SourcesDirectory) + + - task: UseDotNet@2 + displayName: Use .NET SDK from global.json + inputs: + useGlobalJson: true + + # Start SQL Server + - powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest + 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')) + + # Test + - task: DotNetCoreCLI@2 + displayName: Run dotnet test + inputs: + command: test + projects: "tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj" + testRunTitle: Integration Tests SQL Server - $(Agent.OS) + arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build' + + # Stop SQL Server + - pwsh: docker stop mssql + displayName: Stop SQL Server Docker image (Linux) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + + - pwsh: SqlLocalDB stop MSSQLLocalDB + displayName: Stop SQL Server LocalDB (Windows) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + + - stage: DefaultConfigE2E + displayName: Default Config E2E Tests dependsOn: Build variables: npm_config_cache: $(Pipeline.Workspace)/.npm_e2e @@ -128,369 +320,304 @@ stages: # E2E Tests - job: displayName: E2E Tests (SQLite) - condition: eq(${{parameters.sqliteAcceptanceTests}}, True) timeoutInMinutes: 180 + condition: ${{ eq(parameters.skipDefaultConfigAcceptanceTests, false) }} variables: # Connection string CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=Umbraco;Mode=Memory;Cache=Shared;Foreign Keys=True;Pooling=True CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.Sqlite + DatabaseType: SQLite + additionalEnvironmentVariables: false strategy: matrix: LinuxPart1Of3: vmImage: "ubuntu-latest" + testFolder: "DefaultConfig" testCommand: "npm run testSqlite -- --shard=1/3" LinuxPart2Of3: vmImage: "ubuntu-latest" + testFolder: "DefaultConfig" testCommand: "npm run testSqlite -- --shard=2/3" LinuxPart3Of3: vmImage: "ubuntu-latest" + testFolder: "DefaultConfig" testCommand: "npm run testSqlite -- --shard=3/3" WindowsPart1Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run testSqlite -- --shard=1/3" WindowsPart2Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run testSqlite -- --shard=2/3" WindowsPart3Of3: vmImage: "windows-latest" + testFolder: "DefaultConfig" testCommand: "npm run testSqlite -- --shard=3/3" pool: vmImage: $(vmImage) steps: - # Setup test environment - - task: DownloadPipelineArtifact@2 - displayName: Download NuGet artifacts - inputs: - artifact: nupkg - path: $(Agent.BuildDirectory)/app/nupkg - - - task: NodeTool@0 - displayName: Use Node.js $(nodeVersion) - retryCountOnTaskFailure: 3 - inputs: - versionSpec: $(nodeVersion) - - - task: UseDotNet@2 - displayName: Use .NET SDK from global.json - inputs: - useGlobalJson: true + # Setup test environment Template + - template: nightly-E2E-setup-template.yml + parameters: + nodeVersion: ${{ variables.nodeVersion }} + PlaywrightUserEmail: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL }} + PlaywrightPassword: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD }} + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + npm_config_cache: ${{ variables.npm_config_cache }} - pwsh: | - "UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL) - UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) - URL=$(ASPNETCORE_URLS) - STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json - CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env - displayName: Generate .env - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - - # Cache and restore NPM packages - - task: Cache@2 - displayName: Cache NPM packages - inputs: - key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' - restoreKeys: | - npm_e2e | "$(Agent.OS)" - npm_e2e - path: $(npm_config_cache) - - - script: npm ci --no-fund --no-audit --prefer-offline - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - displayName: Restore NPM packages - - # Build application - - pwsh: | - $cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g" - dotnet new nugetconfig - dotnet nuget add source ./nupkg --name Local - dotnet new install Umbraco.Templates::$cmsVersion - dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check dotnet restore UmbracoProject cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject - dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore + displayName: Restore project + workingDirectory: $(Agent.BuildDirectory)/app + + - pwsh: | + dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore dotnet dev-certs https displayName: Build application workingDirectory: $(Agent.BuildDirectory)/app + condition: succeeded() - # Run application - - bash: | - nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & - echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!" - displayName: Run application (Linux) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - workingDirectory: $(Agent.BuildDirectory)/app + # Run application Template + - template: nightly-E2E-run-application-template.yml + parameters: + DatabaseType: ${{ variables.DatabaseType }} + buildConfiguration: ${{ variables.buildConfiguration }} + additionalEnvironmentVariables: ${{ variables.additionalEnvironmentVariables }} - - pwsh: | - $process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log - Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" - displayName: Run application (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - workingDirectory: $(Agent.BuildDirectory)/app - - # Ensures we have the package wait-on installed - - pwsh: npm install wait-on - displayName: Install wait-on package - - # Wait for application to start responding to requests - - pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS) - displayName: Wait for application - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Install Playwright and dependencies - - pwsh: npx playwright install chromium - displayName: Install Playwright only with Chromium browser - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Test - - pwsh: $(testCommand) - displayName: Run Playwright tests - continueOnError: true - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - env: - CI: true - CommitId: $(Build.SourceVersion) - AgentOs: $(Agent.OS) - - # Stop application - - bash: kill -15 $(AcceptanceTestProcessId) - displayName: Stop application (Linux) - condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux')) - - - pwsh: Stop-Process -Id $(AcceptanceTestProcessId) - displayName: Stop application (Windows) - condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT')) - - # Copy artifacts - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse - } - displayName: Copy Playwright results - condition: succeededOrFailed() - - # Copy console error log - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory) - } - displayName: Copy console error log - condition: succeededOrFailed() - - # Publish - - task: PublishPipelineArtifact@1 - displayName: Publish test artifacts - condition: succeededOrFailed() - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" - - # Publish test results - - task: PublishTestResults@2 - displayName: "Publish test results" - condition: succeededOrFailed() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: '*.xml' - searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" - testRunTitle: "$(Agent.JobName)" + # Run tests Template + - template: nightly-E2E-run-tests-template.yml + parameters: + testCommand: $(testCommand) + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + DatabaseType: ${{ variables.DatabaseType }} - job: displayName: E2E Tests (SQL Server) timeoutInMinutes: 180 + condition: ${{ eq(parameters.skipDefaultConfigAcceptanceTests, false) }} variables: # Connection string CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient + DatabaseType: SQLServer + SA_PASSWORD: UmbracoAcceptance123! + additionalEnvironmentVariables: false strategy: matrix: LinuxPart1Of3: testCommand: "npm run test -- --shard=1/3" + testFolder: "DefaultConfig" vmImage: "ubuntu-latest" - SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" LinuxPart2Of3: testCommand: "npm run test -- --shard=2/3" + testFolder: "DefaultConfig" vmImage: "ubuntu-latest" - SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" LinuxPart3Of3: testCommand: "npm run test -- --shard=3/3" + testFolder: "DefaultConfig" vmImage: "ubuntu-latest" - SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True" WindowsPart1Of3: - vmImage: "windows-latest" testCommand: "npm run test -- --shard=1/3" + testFolder: "DefaultConfig" + vmImage: "windows-latest" WindowsPart2Of3: - vmImage: "windows-latest" testCommand: "npm run test -- --shard=2/3" - WindowsPart3Of3: + testFolder: "DefaultConfig" vmImage: "windows-latest" + WindowsPart3Of3: testCommand: "npm run test -- --shard=3/3" + testFolder: "DefaultConfig" + vmImage: "windows-latest" pool: vmImage: $(vmImage) steps: - # Setup test environment - - task: DownloadPipelineArtifact@2 - displayName: Download NuGet artifacts - inputs: - artifact: nupkg - path: $(Agent.BuildDirectory)/app/nupkg - - - task: NodeTool@0 - displayName: Use Node.js $(nodeVersion) - inputs: - versionSpec: $(nodeVersion) - - - task: UseDotNet@2 - displayName: Use .NET SDK from global.json - inputs: - useGlobalJson: true + # Setup test environment Template + - template: nightly-E2E-setup-template.yml + parameters: + nodeVersion: ${{ variables.nodeVersion }} + PlaywrightUserEmail: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL }} + PlaywrightPassword: ${{ variables.UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD }} + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + npm_config_cache: ${{ variables.npm_config_cache }} - pwsh: | - "UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL) - UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) - URL=$(ASPNETCORE_URLS) - STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json - CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env - displayName: Generate .env - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - - # Cache and restore NPM packages - - task: Cache@2 - displayName: Cache NPM packages - inputs: - key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' - restoreKeys: | - npm_e2e | "$(Agent.OS)" - npm_e2e - path: $(npm_config_cache) - - - script: npm ci --no-fund --no-audit --prefer-offline - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest - displayName: Restore NPM packages - - # Build application - - pwsh: | - $cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g" - dotnet new nugetconfig - dotnet nuget add source ./nupkg --name Local - dotnet new install Umbraco.Templates::$cmsVersion - dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check dotnet restore UmbracoProject cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject - dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore + displayName: Restore project + workingDirectory: $(Agent.BuildDirectory)/app + + - pwsh: | + dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore dotnet dev-certs https displayName: Build application workingDirectory: $(Agent.BuildDirectory)/app + condition: succeeded() - # Start SQL Server - - powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest - displayName: Start SQL Server Docker image (Linux) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + # Run application Template + - template: nightly-E2E-run-application-template.yml + parameters: + SA_PASSWORD: ${{ variables.SA_PASSWORD }} + buildConfiguration: ${{ variables.buildConfiguration }} + DatabaseType: ${{ variables.DatabaseType }} + additionalEnvironmentVariables: ${{ variables.additionalEnvironmentVariables }} - - pwsh: SqlLocalDB start MSSQLLocalDB - displayName: Start SQL Server LocalDB (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + # Run tests Template + - template: nightly-E2E-run-tests-template.yml + parameters: + testCommand: $(testCommand) + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + DatabaseType: ${{ variables.DatabaseType }} - # Run application + - stage: AdditionalConfigE2E + displayName: Additional Config E2E Tests + dependsOn: Build + variables: + npm_config_cache: $(Pipeline.Workspace)/.npm_e2e + ASPNETCORE_URLS: https://localhost:44331 + PlaywrightPassword: UmbracoAcceptance123! + PlaywrightUserEmail: playwright@umbraco.com + jobs: + - job: + displayName: E2E Tests with Different App settings (SQL Server) + condition: ${{ or(eq(parameters.differentAppSettingsAcceptanceTests, true), eq(parameters.skipDefaultConfigAcceptanceTests, true)) }} + timeoutInMinutes: 180 + variables: + SA_PASSWORD: UmbracoAcceptance123! + DatabaseType: SQLServer + strategy: + matrix: + # UnattendedInstallConfig + WindowsUnattendedInstallConfig: + vmImage: "windows-latest" + testFolder: "UnattendedInstallConfig" + testCommand: "npx playwright test --project=unattendedInstallConfig --grep=InstallSQLServer" + port: 44331 + additionalEnvironmentVariables: false + # DeliveryApiConfig + WindowsDeliveryApiConfig: + vmImage: "windows-latest" + testFolder: "DeliveryApi" + port: '' + testCommand: "npx playwright test --project=deliveryApi" + CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True + CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient + additionalEnvironmentVariables: false + LinuxDeliveryApiConfig: + vmImage: "ubuntu-latest" + testFolder: "DeliveryApi" + port: '' + testCommand: "npx playwright test --project=deliveryApi" + CONNECTIONSTRINGS__UMBRACODBDSN: Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True + CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient + additionalEnvironmentVariables: false + # ExternalLogin AzureADB2C + WindowsExternalLoginAzureADB2C: + vmImage: "windows-latest" + testFolder: "ExternalLogin\\AzureADB2C" + testCommand: "npx playwright test --project=externalLoginAzureADB2C" + port: 44331 + packageName: "Microsoft.AspNetCore.Authentication.OpenIdConnect" + packageVersion: "9.0.8" + CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True + CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient + additionalEnvironmentVariables: true + pool: + vmImage: $(vmImage) + steps: + # Setup test environment Template + - template: nightly-E2E-setup-template.yml + parameters: + nodeVersion: ${{ variables.nodeVersion }} + PlaywrightUserEmail: ${{ variables.PlaywrightUserEmail }} + PlaywrightPassword: ${{ variables.PlaywrightPassword }} + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + npm_config_cache: ${{ variables.npm_config_cache }} + + # Install NuGet package if specified in the matrix + - pwsh: | + Write-Host "Installing package $(packageName) version $(packageVersion)" + dotnet add package $(packageName) --version $(packageVersion) + displayName: "Install NuGet package: $(packageName)" + workingDirectory: $(Agent.BuildDirectory)/app/UmbracoProject + condition: and(succeeded(), ne(variables['packageName'], ''), ne(variables['packageVersion'], '')) + + # Build application Template + - template: nightly-E2E-build-template.yml + parameters: + testFolder: $(testFolder) + buildConfiguration: ${{ variables.buildConfiguration }} + additionalEnvironmentVariables: $(additionalEnvironmentVariables) + + # Build application for AzureADB2C + - pwsh: | + dotnet build UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-restore + dotnet dev-certs https + displayName: Build application for AzureADB2C + workingDirectory: $(Agent.BuildDirectory)/app + env: + AZUREADB2CDOMAIN: $(AZUREB2CDOMAIN) + AZUREADB2CTENANT: $(AZUREB2CTENANT) + AZUREADB2CPOLICY: $(AZUREB2CPOLICY) + AZUREADB2CCLIENTID: $(AZUREB2CCLIENTID) + AZUREADB2CCLIENTSECRET: $(AZUREB2CCLIENTSECRET) + condition: and(succeeded(), eq(variables['testFolder'], 'ExternalLogin\AzureADB2C')) + + # Run application Template + - template: nightly-E2E-run-application-template.yml + parameters: + SA_PASSWORD: ${{ variables.SA_PASSWORD }} + additionalEnvironmentVariables: $(additionalEnvironmentVariables) + buildConfiguration: ${{ variables.buildConfiguration }} + DatabaseType: ${{ variables.DatabaseType }} + + # Run application for Linux with additional Environment Variables for Azure AD - bash: | - nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & + nohup dotnet run --project UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!" displayName: Run application (Linux) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['testFolder'], 'ExternalLogin\AzureADB2C')) workingDirectory: $(Agent.BuildDirectory)/app + env: + AZUREADB2CDOMAIN: $(AZUREB2CDOMAIN) + AZUREADB2CTENANT: $(AZUREB2CTENANT) + AZUREADB2CPOLICY: $(AZUREB2CPOLICY) + AZUREADB2CCLIENTID: $(AZUREB2CCLIENTID) + AZUREADB2CCLIENTSECRET: $(AZUREB2CCLIENTSECRET) + # Run application for Windows with additional Environment Variables for Azure AD - pwsh: | - $process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log + $process = Start-Process dotnet "run --project UmbracoProject --configuration ${{ variables.buildConfiguration }} --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" displayName: Run application (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['testFolder'], 'ExternalLogin\AzureADB2C')) workingDirectory: $(Agent.BuildDirectory)/app - - # Ensures we have the package wait-on installed - - pwsh: npm install wait-on - displayName: Install wait-on package - - # Wait for application to start responding to requests - - pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS) - displayName: Wait for application - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Install Playwright and dependencies - - pwsh: npx playwright install chromium - displayName: Install Playwright only with Chromium browser - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - - # Test - - pwsh: $(testCommand) - displayName: Run Playwright tests - continueOnError: true - workingDirectory: tests/Umbraco.Tests.AcceptanceTest env: - CI: true - CommitId: $(Build.SourceVersion) - AgentOs: $(Agent.OS) + AZUREADB2CDOMAIN: $(AZUREB2CDOMAIN) + AZUREADB2CTENANT: $(AZUREB2CTENANT) + AZUREADB2CPOLICY: $(AZUREB2CPOLICY) + AZUREADB2CCLIENTID: $(AZUREB2CCLIENTID) + AZUREADB2CCLIENTSECRET: $(AZUREB2CCLIENTSECRET) - # Stop application - - bash: kill -15 $(AcceptanceTestProcessId) - displayName: Stop application (Linux) - condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux')) - - - pwsh: Stop-Process -Id $(AcceptanceTestProcessId) - displayName: Stop application (Windows) - condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT')) - - # Stop SQL Server - - pwsh: docker stop mssql - displayName: Stop SQL Server Docker image (Linux) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - - - pwsh: SqlLocalDB stop MSSQLLocalDB - displayName: Stop SQL Server LocalDB (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - - # Copy artifacts - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse - } - displayName: Copy Playwright results - condition: succeededOrFailed() - - # Copy console error log - - pwsh: | - if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) { - Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory) - } - displayName: Copy console error log - condition: succeededOrFailed() - - # Publish - - task: PublishPipelineArtifact@1 - displayName: Publish test artifacts - condition: succeededOrFailed() - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" - - # Publish test results - - task: PublishTestResults@2 - displayName: "Publish test results" - condition: succeededOrFailed() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: '*.xml' - searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" - testRunTitle: "$(Agent.JobName)" + # Run tests Template + - template: nightly-E2E-run-tests-template.yml + parameters: + testCommand: $(testCommand) + ASPNETCORE_URLS: ${{ variables.ASPNETCORE_URLS }} + port: $(port) + AZUREB2CTESTUSEREMAIL: $(AZUREB2CTESTUSEREMAIL) + AZUREB2CTESTUSERPASSWORD: $(AZUREB2CTESTUSERPASSWORD) + DatabaseType: ${{ variables.DatabaseType }} - stage: NotifySlackBot displayName: Notify Slack on Failure - dependsOn: E2E + dependsOn: DefaultConfigE2E # This stage will only run if the E2E tests fail or succeed with issues - condition: or( - eq(dependencies.E2E.result, 'failed'), - eq(dependencies.E2E.result, 'succeededWithIssues')) + condition: or(eq(dependencies.DefaultConfigE2E.result, 'failed'), eq(dependencies.DefaultConfigE2E.result, 'succeededWithIssues')) jobs: - job: PostToSlack displayName: Send Slack Notification diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 478655791d..fbeda87922 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -8,7 +8,7 @@ "hasInstallScript": true, "dependencies": { "@umbraco/json-models-builders": "^2.0.38", - "@umbraco/playwright-testhelpers": "^16.0.42", + "@umbraco/playwright-testhelpers": "^16.0.46", "camelize": "^1.0.0", "dotenv": "^16.3.1", "node-fetch": "^2.6.7" @@ -58,21 +58,21 @@ } }, "node_modules/@umbraco/json-models-builders": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.38.tgz", - "integrity": "sha512-6nC1Y1xn+8zyqU3iqHubRo18L53TdZkhHIY4z68VSLcA6YoAzdxtjw+zx7yDIMV+epoQ4NCG2ooAa0gBhHqQgg==", + "version": "2.0.39", + "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.39.tgz", + "integrity": "sha512-YcgZ+WJ3HANBUaffSzZVRlJNLjXOaWOQNIuGf/A0lGH1khd5Kkv2JGln1bq2bNzIbIYQM+f2vYAnmYXmJFN7Vg==", "license": "MIT", "dependencies": { "camelize": "^1.0.1" } }, "node_modules/@umbraco/playwright-testhelpers": { - "version": "16.0.42", - "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-16.0.42.tgz", - "integrity": "sha512-ePKl8gtELoIMEV57E3N4VumfKNkuOTFo/LYH7ePhseCcm5oUh1Cc/RVqvlXYsdfBTiMfZ7x7Nu4lOSv15D2Z3Q==", + "version": "16.0.46", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-16.0.46.tgz", + "integrity": "sha512-2C76pXp8ixbrOj4kcSzwyXCPSXMsubPcR6wClBdVx6ZiR4LgkAzQ8WwRca/K5pKVm2Uh6HogdRE6bg+qv6klxQ==", "license": "MIT", "dependencies": { - "@umbraco/json-models-builders": "2.0.38", + "@umbraco/json-models-builders": "2.0.39", "node-fetch": "^2.6.7" } }, diff --git a/tests/Umbraco.Tests.AcceptanceTest/package.json b/tests/Umbraco.Tests.AcceptanceTest/package.json index adda209951..abeb17447e 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@umbraco/json-models-builders": "^2.0.38", - "@umbraco/playwright-testhelpers": "^16.0.42", + "@umbraco/playwright-testhelpers": "^16.0.46", "camelize": "^1.0.0", "dotenv": "^16.3.1", "node-fetch": "^2.6.7" diff --git a/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts b/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts index b7f9fc6ac8..c09d132505 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts @@ -44,14 +44,42 @@ export default defineConfig({ testMatch: '**/*.setup.ts', }, { - name: 'chromium', + name: 'defaultConfig', + testMatch: 'DefaultConfig/**', dependencies: ['setup'], use: { ...devices['Desktop Chrome'], // Use prepared auth state. ignoreHTTPSErrors: true, - storageState: STORAGE_STATE, + storageState: STORAGE_STATE + } + }, + { + name: 'deliveryApi', + testMatch: 'DeliveryApi/**', + dependencies: ['setup'], + use: { + ...devices['Desktop Chrome'], + // Use prepared auth state. + ignoreHTTPSErrors: true, + storageState: STORAGE_STATE }, }, + { + name: 'externalLoginAzureADB2C', + testMatch: 'ExternalLogin/AzureADB2C/**', + use: { + ...devices['Desktop Chrome'], + ignoreHTTPSErrors: true, + } + }, + // This project is used to test the install steps, for that we do not need to authenticate. + { + name: 'unattendedInstallConfig', + testMatch: 'UnattendedInstallConfig/**', + use: { + ...devices['Desktop Chrome'] + } + } ], }); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/BlockGrid/Block/BlockGridBlockAdvanced.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/BlockGrid/Block/BlockGridBlockAdvanced.spec.ts index da485d8777..7803ce7e83 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/BlockGrid/Block/BlockGridBlockAdvanced.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/BlockGrid/Block/BlockGridBlockAdvanced.spec.ts @@ -248,7 +248,7 @@ test('can add a thumbnail to a block', {tag: '@smoke'}, async ({umbracoApi, umbr const textStringData = await umbracoApi.dataType.getByName(dataTypeName); const contentElementTypeId = await umbracoApi.documentType.createDefaultElementType(elementTypeName, groupName, dataTypeName, textStringData.id); await umbracoApi.dataType.createBlockGridWithABlock(blockGridEditorName, contentElementTypeId); - const mediaUrl = await umbracoApi.media.getMediaUrl(mediaId); + const mediaUrl = await umbracoApi.media.getFullMediaUrl(mediaId); // Act await umbracoUi.dataType.goToDataType(blockGridEditorName); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/BlockListEditor/BlockListBlocks.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/BlockListEditor/BlockListBlocks.spec.ts index 49f502c462..2196ef194b 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/BlockListEditor/BlockListBlocks.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataType/BlockListEditor/BlockListBlocks.spec.ts @@ -422,7 +422,7 @@ test('can add a thumbnail to a block', {tag: '@release'}, async ({umbracoApi, um const textStringData = await umbracoApi.dataType.getByName(dataTypeName); const contentElementTypeId = await umbracoApi.documentType.createDefaultElementType(elementTypeName, groupName, dataTypeName, textStringData.id); await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListEditorName, contentElementTypeId); - const mediaUrl = await umbracoApi.media.getMediaUrl(mediaId); + const mediaUrl = await umbracoApi.media.getFullMediaUrl(mediaId); // Act await umbracoUi.dataType.goToDataType(blockListEditorName); 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 251f455072..fba1dd167c 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/Media.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/Media.spec.ts @@ -73,7 +73,7 @@ for (const mediaFileType of mediaFileTypes) { // Assert await umbracoUi.media.waitForMediaItemToBeCreated(); const mediaData = await umbracoApi.media.getByName(mediaFileType.fileName); - const mediaUrl = await umbracoApi.media.getMediaUrl(mediaData.id); + const mediaUrl = await umbracoApi.media.getFullMediaUrl(mediaData.id); await umbracoUi.media.doesMediaHaveThumbnail(mediaData.id, mediaFileType.thumbnail, mediaUrl); await umbracoUi.media.isMediaTreeItemVisible(mediaFileType.fileName); expect(await umbracoApi.media.doesNameExist(mediaFileType.fileName)).toBeTruthy(); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/appsettings.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/appsettings.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/AdditionalSetup/Program.cs b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/AdditionalSetup/Program.cs new file mode 100644 index 0000000000..854d98c154 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/AdditionalSetup/Program.cs @@ -0,0 +1,27 @@ +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + +builder.CreateUmbracoBuilder() + .AddBackOffice() + .AddWebsite() + .AddDeliveryApi() + .AddComposers() + .Build(); + +WebApplication app = builder.Build(); + +await app.BootUmbracoAsync(); + + +app.UseUmbraco() + .WithMiddleware(u => + { + u.UseBackOffice(); + u.UseWebsite(); + }) + .WithEndpoints(u => + { + u.UseBackOfficeEndpoints(); + u.UseWebsiteEndpoints(); + }); + +await app.RunAsync(); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/AdditionalSetup/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/AdditionalSetup/appsettings.json new file mode 100644 index 0000000000..44dc8b93d2 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/AdditionalSetup/appsettings.json @@ -0,0 +1,64 @@ +{ + "$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": { + "DeliveryApi": { + "Enabled": true, + "Media": { + "Enabled": true + } + }, + "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/" + } + } + } +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/DeliveryApi.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/DeliveryApi.spec.ts new file mode 100644 index 0000000000..5d3ae153eb --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/DeliveryApi.spec.ts @@ -0,0 +1,42 @@ +import {expect} from '@playwright/test'; +import {AliasHelper, test} from '@umbraco/playwright-testhelpers'; + +test('can get content from delivery api', async ({umbracoApi}) => { + // Arrange + const documentTypeName = 'TestDocumentType'; + const contentName = 'TestContent'; + const dataTypeName = 'Textstring'; + const textStringValue = 'This is a test text string value'; + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); + const dataType = await umbracoApi.dataType.getByName(dataTypeName); + const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataType.id, 'TestGroup'); + const documentId = await umbracoApi.document.createDocumentWithTextContent(contentName, documentTypeId, textStringValue, dataTypeName); + const propertyValue = { + dataTypeName: AliasHelper.toAlias(dataTypeName), + dataTypeValue: textStringValue + } + + // Act + await umbracoApi.document.publish(documentId); + + // Assert + expect(await umbracoApi.contentDeliveryApi.doesContentItemWithIdContainValues(documentId, contentName, AliasHelper.toAlias(documentTypeName), [propertyValue])).toBeTruthy(); + + // Clean + await umbracoApi.documentType.ensureNameNotExists(documentTypeName); +}); + +test('can get media image from delivery api', async ({umbracoApi}) => { + // Arrange + const mediaName = 'TestMedia'; + const mediaTypeName = 'File'; + await umbracoApi.media.ensureNameNotExists(mediaName); + const mediaId = await umbracoApi.media.createDefaultMediaFile(mediaName); + const mediaUrl = await umbracoApi.media.getMediaUrlWithoutBaseUrl(mediaId); + + // Assert + expect(await umbracoApi.mediaDeliveryApi.doesMediaItemWithIdContainValues(mediaId, mediaName, mediaTypeName, mediaUrl)).toBeTruthy(); + + // Clean + await umbracoApi.media.ensureNameNotExists(mediaName); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/appsettings.json new file mode 100644 index 0000000000..44dc8b93d2 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DeliveryApi/appsettings.json @@ -0,0 +1,64 @@ +{ + "$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": { + "DeliveryApi": { + "Enabled": true, + "Media": { + "Enabled": true + } + }, + "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/" + } + } + } +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/App_Plugins/Login/umbraco-package.json b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/App_Plugins/Login/umbraco-package.json new file mode 100644 index 0000000000..929a1446ac --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/App_Plugins/Login/umbraco-package.json @@ -0,0 +1,25 @@ +{ + "$schema": "../../umbraco-package-schema.json", + "name": "Azure B2C Login", + "allowPublicAccess": true, + "extensions": [ + { + "type": "authProvider", + "alias": "Test.AzureB2C", + "name": "Azure AD B2C", + "forProviderName": "Umbraco.AzureB2C", + "meta": { + "label": "Sign in with Azure AD B2C", + "defaultView": { + "icon": "icon-cloud" + }, + "behavior": { + "autoRedirect": false + }, + "linking": { + "allowManualLinking": true + } + } + } + ] +} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CAuthenticationExtensions.cs b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CAuthenticationExtensions.cs new file mode 100644 index 0000000000..53db531b52 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CAuthenticationExtensions.cs @@ -0,0 +1,73 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Umbraco.Cms.Core; + +namespace Umbraco.Cms.Tests.AcceptanceTest.ExternalLogin.AzureADB2C +{ + public static class AzureB2CAuthenticationExtensions + { + public static IUmbracoBuilder ConfigureAuthentication(this IUmbracoBuilder builder, + IConfiguration configuration) + { + var b2cSettings = new AzureB2CSettings(); + + builder.AddBackOfficeExternalLogins(logins => + { + const string schemeName = AzureB2COptions.SchemeName; + var backOfficeScheme = Constants.Security.BackOfficeExternalAuthenticationTypePrefix + schemeName; + + logins.AddBackOfficeLogin(backOfficeAuth => + { + backOfficeAuth.AddOpenIdConnect(backOfficeScheme, options => + { + options.RequireHttpsMetadata = true; + options.SaveTokens = true; + options.ClientId = b2cSettings.ClientId; + options.ClientSecret = b2cSettings.ClientSecret; + options.CallbackPath = "/umbraco-b2c-users-signin"; + options.MetadataAddress = + $"https://{b2cSettings.Domain}/{b2cSettings.Tenant}/{b2cSettings.Policy}/v2.0/.well-known/openid-configuration"; + + options.ResponseType = OpenIdConnectResponseType.Code; + options.TokenValidationParameters.SaveSigninToken = true; + options.GetClaimsFromUserInfoEndpoint = true; + options.TokenValidationParameters.NameClaimType = "name"; + options.TokenValidationParameters.RoleClaimType = "role"; + + options.Events = new OpenIdConnectEvents + { + OnTokenResponseReceived = context => + { + if (string.IsNullOrEmpty(context.TokenEndpointResponse.AccessToken)) + { + context.TokenEndpointResponse.AccessToken = "empty_access_token"; + } + + return Task.CompletedTask; + }, + + OnTokenValidated = context => + { + var identity = context.Principal!.Identities.First(); + + var email = identity.FindFirst("emails")?.Value + ?? identity.FindFirst(ClaimTypes.Email)?.Value; + + if (!string.IsNullOrWhiteSpace(email)) + { + identity.AddClaim(new Claim(ClaimTypes.Email, email)); + identity.AddClaim(new Claim("email", email)); + } + + return Task.CompletedTask; + } + }; + }); + }); + }); + + return builder; + } + } +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CComposer.cs b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CComposer.cs new file mode 100644 index 0000000000..ee4787143f --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CComposer.cs @@ -0,0 +1,14 @@ +using Umbraco.Cms.Core.Composing; + +namespace Umbraco.Cms.Tests.AcceptanceTest.ExternalLogin.AzureADB2C +{ + public class AzureB2CComposer : IComposer + { + public void Compose(IUmbracoBuilder builder) + { + builder.Services.ConfigureOptions(); + + builder.ConfigureAuthentication(builder.Config); + } + } +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2COptions.cs b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2COptions.cs new file mode 100644 index 0000000000..3464b73cc4 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2COptions.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Options; +using Umbraco.Cms.Api.Management.Security; +using Umbraco.Cms.Core; + +namespace Umbraco.Cms.Tests.AcceptanceTest.ExternalLogin.AzureADB2C +{ + public class AzureB2COptions : IConfigureNamedOptions + { + public const string SchemeName = "AzureB2C"; + + public void Configure(string? name, BackOfficeExternalLoginProviderOptions options) + { + if (name != Constants.Security.BackOfficeExternalAuthenticationTypePrefix + SchemeName) + return; + + options.AutoLinkOptions = new ExternalSignInAutoLinkOptions( + autoLinkExternalAccount: true, + defaultUserGroups: [Constants.Security.AdminGroupAlias], + defaultCulture: "en-US", + allowManualLinking: true + ) + { + OnAutoLinking = (user, loginInfo) => { user.IsApproved = true; }, + OnExternalLogin = (user, loginInfo) => { return true; } + }; + } + + public void Configure(BackOfficeExternalLoginProviderOptions options) => + Configure(Constants.Security.BackOfficeExternalAuthenticationTypePrefix + SchemeName, options); + } +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CSettings.cs b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CSettings.cs new file mode 100644 index 0000000000..48aa5a476b --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/AzureB2CSettings.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Cms.Tests.AcceptanceTest.ExternalLogin.AzureADB2C +{ + public class AzureB2CSettings + { + public string Domain { get; set; } = Environment.GetEnvironmentVariable("AZUREADB2CDOMAIN") ?? string.Empty; + public string Tenant { get; set; } = Environment.GetEnvironmentVariable("AZUREADB2CTENANT") ?? string.Empty; + public string Policy { get; set; } = Environment.GetEnvironmentVariable("AZUREADB2CPOLICY") ?? string.Empty; + public string ClientId { get; set; } = Environment.GetEnvironmentVariable("AZUREADB2CCLIENTID") ?? string.Empty; + public string ClientSecret { get; set; } = Environment.GetEnvironmentVariable("AZUREADB2CCLIENTSECRET") ?? string.Empty; + } +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/AdditionalSetup/appsettings.json new file mode 100644 index 0000000000..e978258b9a --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/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/" + } + } + } +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/Login.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/Login.spec.ts new file mode 100644 index 0000000000..385dfbccab --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/ExternalLogin/AzureADB2C/Login.spec.ts @@ -0,0 +1,20 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +const azureEmail = process.env.AZUREADB2CTESTUSEREMAIL; +const azurePassword = process.env.AZUREADB2CTESTUSERPASSWORD; + +// Really simple test to check if we can log in using Azure AD B2C +test('Log in to Umbraco using Azure AD B2C', async ({umbracoUi}) => { + test.slow(); + // Arrange + await umbracoUi.goToBackOffice(); + + // Act + await umbracoUi.externalLogin.clickSignInWithAzureADB2CButton(); + await umbracoUi.externalLogin.enterAzureADB2CEmail(azureEmail); + await umbracoUi.externalLogin.enterAzureADB2CPassword(azurePassword); + await umbracoUi.externalLogin.clickSignInButton(); + + // Assert + await umbracoUi.content.goToSection(ConstantHelper.sections.content); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/AdditionalSetup/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/AdditionalSetup/appsettings.json new file mode 100644 index 0000000000..3aa310742c --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/AdditionalSetup/appsettings.json @@ -0,0 +1,54 @@ +{ + "$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": false + }, + "Content": { + "ContentVersionCleanupPolicy": { + "EnableCleanup": false + } + }, + "Global": { + "DisableElectionForSingleServer": true, + "Id": "00000000-0000-0000-0000-000000000042", + "VersionCheckPeriod": 0, + "UseHttps": true + }, + "HealthChecks": { + "Notification": { + "Enabled": false + } + }, + "KeepAlive": { + "DisableKeepAliveTask": true + }, + "WebRouting": { + "UmbracoApplicationUrl": "https://localhost:44331/" + } + } + } +} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/Install/InstallSQLServer.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/Install/InstallSQLServer.spec.ts new file mode 100644 index 0000000000..1ae0714e21 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/Install/InstallSQLServer.spec.ts @@ -0,0 +1,28 @@ +// To be able to test different databases, we need to set an additional UnattendedInstallConfig up because we would have to start from scratch, otherwise we would be using the same database. +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +const name = 'TestName'; +const email = process.env.UMBRACO_USER_LOGIN; +const password = process.env.UMBRACO_USER_PASSWORD; + +test('Install Umbraco using SQLServer Express', async ({umbracoUi}) => { + test.slow(); + // Arrange + await umbracoUi.install.goToInstallPage(); + + // Act + await umbracoUi.install.enterName(name); + await umbracoUi.install.enterEmail(email); + await umbracoUi.install.enterPassword(password); + await umbracoUi.install.clickNextButton(); + await umbracoUi.install.clickNextButton(); + await umbracoUi.install.setDatabaseType('SQL Server Express LocalDB'); + await umbracoUi.install.doesDatabaseHaveType('SQL Server Express LocalDB'); + await umbracoUi.install.clickInstallButton(); + + // Assert + await umbracoUi.login.enterEmail(email); + await umbracoUi.login.enterPassword(password); + await umbracoUi.login.clickLoginButton(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/Install/InstallSQLite.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/Install/InstallSQLite.spec.ts new file mode 100644 index 0000000000..69bc110782 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/UnattendedInstallConfig/Install/InstallSQLite.spec.ts @@ -0,0 +1,27 @@ +// To be able to test different databases, we need to set an additional UnattendedInstallConfig up because we would have to start from scratch, otherwise we would be using the same database. +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +const name = 'TestName'; +const email = process.env.UMBRACO_USER_LOGIN; +const password = process.env.UMBRACO_USER_PASSWORD; + +test('Install Umbraco using SQLite', async ({umbracoUi}) => { + test.slow(); + // Arrange + await umbracoUi.install.goToInstallPage(); + + // Act + await umbracoUi.install.enterName(name); + await umbracoUi.install.enterEmail(email); + await umbracoUi.install.enterPassword(password); + await umbracoUi.install.clickNextButton(); + await umbracoUi.install.clickNextButton(); + await umbracoUi.install.doesDatabaseHaveType('SQLite'); + await umbracoUi.install.clickInstallButton(); + + // Assert + await umbracoUi.login.enterEmail(email); + await umbracoUi.login.enterPassword(password); + await umbracoUi.login.clickLoginButton(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); +}); diff --git a/umbraco.sln b/umbraco.sln index 74fcc8dc3f..0568efabec 100644 --- a/umbraco.sln +++ b/umbraco.sln @@ -189,6 +189,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Cms.Api.Management" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.PublishedCache.HybridCache", "src\Umbraco.PublishedCache.HybridCache\Umbraco.PublishedCache.HybridCache.csproj", "{CB0B9817-EDBC-4D6D-B4D2-969019C4606D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nightly-e2e-templates", "nightly-e2e-templates", "{E90531F6-E32D-40DA-BCB2-55FA94D5AB19}" + ProjectSection(SolutionItems) = preProject + build\nightly-E2E-build-template.yml = build\nightly-E2E-build-template.yml + build\nightly-E2E-run-application-template.yml = build\nightly-E2E-run-application-template.yml + build\nightly-E2E-run-tests-template.yml = build\nightly-E2E-run-tests-template.yml + build\nightly-E2E-setup-template.yml = build\nightly-E2E-setup-template.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -382,6 +390,7 @@ Global {25AECCB5-B187-4406-844B-91B8FF0FCB37} = {2B47AD9F-FFF1-448A-88F1-D4F568811738} {EA628ABD-624E-4AF3-B548-6710D4D66531} = {2B47AD9F-FFF1-448A-88F1-D4F568811738} {A13FF0A0-69FA-468A-9F79-565401D5C341} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} + {E90531F6-E32D-40DA-BCB2-55FA94D5AB19} = {20CE9C97-9314-4A19-BCF1-D12CF49B7205} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC}