Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59f9bf290d | ||
|
|
b6b8f54c65 | ||
|
|
50139ee506 | ||
|
|
62947b2862 | ||
|
|
1748a08f7b | ||
|
|
3264d58f13 | ||
|
|
b6be468b3a | ||
|
|
3ec88aab65 | ||
|
|
da43086017 | ||
|
|
d62fe5e315 | ||
|
|
852192a5d2 | ||
|
|
f4498c3d05 | ||
|
|
b045b33049 | ||
|
|
32a4fbebd5 | ||
|
|
cc2f1df78e | ||
|
|
9b2fd1253b | ||
|
|
024697f3bf | ||
|
|
d8b76575ca | ||
|
|
5b00312158 | ||
|
|
d920e93d1e | ||
|
|
feba51a49d | ||
|
|
853c1acf7e | ||
|
|
3f10bd8c21 | ||
|
|
b13eb8aaf8 | ||
|
|
275478066e | ||
|
|
b01def0872 | ||
|
|
b106305167 | ||
|
|
103631de15 | ||
|
|
1b19ca7cdb | ||
|
|
0ad020f0ce | ||
|
|
fc1455e0d8 | ||
|
|
793534c627 | ||
|
|
15a8b4066a | ||
|
|
38f6e2b0da | ||
|
|
14f60a108a | ||
|
|
10518ed454 | ||
|
|
375a0b4388 | ||
|
|
6300ccc211 | ||
|
|
5b6f544d2a | ||
|
|
a1379f13a0 | ||
|
|
38e9781fc5 | ||
|
|
dfc6ead6d5 | ||
|
|
30a431f870 | ||
|
|
e932fa5404 | ||
|
|
3579c4ac9e | ||
|
|
671751b618 | ||
|
|
3b6e4a96f1 | ||
|
|
83e580c3a7 | ||
|
|
31ee35e721 | ||
|
|
cf0f3f1380 | ||
|
|
31c3f5ae0c | ||
|
|
15643135ba | ||
|
|
cd71e5666a |
@@ -4,11 +4,11 @@ contact_links:
|
||||
url: https://github.com/umbraco/Umbraco-CMS/discussions/new?category=features-and-ideas
|
||||
about: Start a new discussion when you have ideas or feature requests, eventually discussions can turn into plans
|
||||
- name: ⁉️ Support Question
|
||||
url: https://our.umbraco.com
|
||||
url: https://forum.umbraco.com
|
||||
about: This issue tracker is NOT meant for support questions. If you have a question, please join us on the forum.
|
||||
- name: 📖 Documentation Issue
|
||||
url: https://github.com/umbraco/UmbracoDocs/issues
|
||||
about: Documentation issues should be reported on the Umbraco documentation repository.
|
||||
- name: 🔐 Security Issue
|
||||
url: https://umbraco.com/about-us/trust-center/security-and-umbraco/how-to-report-a-vulnerability-in-umbraco/
|
||||
url: https://umbraco.com/trust-center/security-and-umbraco/how-to-report-a-vulnerability-in-umbraco/
|
||||
about: Discovered a Security Issue in Umbraco?
|
||||
|
||||
+1
-1
@@ -4,8 +4,8 @@
|
||||
[](https://www.nuget.org/packages/Umbraco.Cms)
|
||||
[](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=301)
|
||||
[](CONTRIBUTING.md)
|
||||
[](https://forum.umbraco.com)
|
||||
[](https://discord.gg/umbraco)
|
||||
[](https://discord-chats.umbraco.com)
|
||||

|
||||
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
|
||||
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
||||
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
|
||||
<PackageVersion Include="Examine" Version="3.7.0" />
|
||||
<PackageVersion Include="Examine.Core" Version="3.7.0" />
|
||||
<PackageVersion Include="Examine" Version="3.7.1" />
|
||||
<PackageVersion Include="Examine.Core" Version="3.7.1" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.74" />
|
||||
<PackageVersion Include="JsonPatch.Net" Version="3.1.1" />
|
||||
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
|
||||
@@ -100,4 +100,4 @@
|
||||
<!-- Examine.Lucene brings in a vulnerable version of Lucene.Net.Replicator -->
|
||||
<PackageVersion Include="Lucene.Net.Replicator" Version="4.8.0-beta00017" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+105
-254
@@ -313,8 +313,8 @@ stages:
|
||||
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)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
macOSPart1Of3:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace
|
||||
@@ -325,8 +325,8 @@ stages:
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
macOSPart3Of3:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
variables:
|
||||
@@ -389,8 +389,8 @@ stages:
|
||||
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)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
LinuxPart1Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
SA_PASSWORD: UmbracoIntegration123!
|
||||
@@ -410,8 +410,8 @@ stages:
|
||||
SA_PASSWORD: UmbracoIntegration123!
|
||||
Tests__Database__DatabaseType: SqlServer
|
||||
Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);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)"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
@@ -432,6 +432,33 @@ stages:
|
||||
displayName: Start SQL Server Docker image (Linux)
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
|
||||
|
||||
- powershell: |
|
||||
$maxAttempts = 12
|
||||
$attempt = 0
|
||||
$status = ""
|
||||
|
||||
while (($status -ne 'running') -and ($attempt -lt $maxAttempts)) {
|
||||
Start-Sleep -Seconds 5
|
||||
# We use the docker inspect command to check the status of the container. If the container is not running, we wait 5 seconds and try again. And if reaches 12 attempts, we fail the build.
|
||||
$status = docker inspect -f '{{.State.Status}}' mssql
|
||||
|
||||
if ($status -ne 'running') {
|
||||
Write-Host "Waiting for SQL Server to be ready... Attempt $($attempt + 1)"
|
||||
$attempt++
|
||||
}
|
||||
}
|
||||
|
||||
if ($status -eq 'running') {
|
||||
Write-Host "SQL Server container is running"
|
||||
docker ps -a
|
||||
} else {
|
||||
Write-Host "SQL Server did not become ready in time. Last known status: $status"
|
||||
docker logs mssql
|
||||
exit 1
|
||||
}
|
||||
displayName: Wait for SQL Server to be ready (Linux)
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
|
||||
|
||||
- pwsh: SqlLocalDB start MSSQLLocalDB
|
||||
displayName: Start SQL Server LocalDB (Windows)
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
@@ -495,151 +522,70 @@ 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
|
||||
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" | 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 }}
|
||||
|
||||
- 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()
|
||||
|
||||
# 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 Tests (SQL Server)
|
||||
@@ -647,175 +593,76 @@ 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
|
||||
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" | 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 }}
|
||||
|
||||
- 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()
|
||||
|
||||
# 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
|
||||
@@ -829,6 +676,8 @@ stages:
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.myGetDeploy}}))
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
|
||||
displayName: Push to pre-release feed
|
||||
steps:
|
||||
- checkout: none
|
||||
@@ -890,6 +739,8 @@ stages:
|
||||
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.nuGetDeploy}}))
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
|
||||
displayName: Push to NuGet
|
||||
steps:
|
||||
- checkout: none
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
parameters:
|
||||
- name: testFolder
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
- name: buildConfiguration
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
- name: additionalEnvironmentVariables
|
||||
type: boolean
|
||||
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
|
||||
|
||||
- ${{ if eq(parameters.additionalEnvironmentVariables, False) }}:
|
||||
- pwsh: |
|
||||
dotnet build UmbracoProject --configuration ${{ parameters.buildConfiguration }} --no-restore
|
||||
dotnet dev-certs https
|
||||
displayName: Build application
|
||||
workingDirectory: $(Agent.BuildDirectory)/app
|
||||
condition: succeeded()
|
||||
@@ -0,0 +1,47 @@
|
||||
parameters:
|
||||
- name: SA_PASSWORD
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
- name: buildConfiguration
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
- name: additionalEnvironmentVariables
|
||||
type: boolean
|
||||
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'))
|
||||
|
||||
# If we want to add additional environment variables to the run step, then we will skip these
|
||||
- ${{ if eq(parameters.additionalEnvironmentVariables, False) }}:
|
||||
# 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'))
|
||||
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'))
|
||||
workingDirectory: $(Agent.BuildDirectory)/app
|
||||
@@ -0,0 +1,105 @@
|
||||
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
|
||||
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)"
|
||||
@@ -0,0 +1,69 @@
|
||||
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" | 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
|
||||
@@ -3,13 +3,29 @@ name: Nightly_E2E_Test_$(TeamProject)_$(Build.DefinitionName)_$(SourceBranchName
|
||||
pr: none
|
||||
trigger: none
|
||||
|
||||
schedules:
|
||||
- cron: '0 0 * * *'
|
||||
displayName: Daily midnight build
|
||||
branches:
|
||||
include:
|
||||
- v14/dev
|
||||
- v15/dev
|
||||
# schedules:
|
||||
# - cron: '0 0 * * *'
|
||||
# displayName: Daily midnight build
|
||||
# branches:
|
||||
# include:
|
||||
# - v14/dev
|
||||
# - v15/dev
|
||||
|
||||
parameters:
|
||||
- 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
|
||||
|
||||
- name: skipIntegrationTests
|
||||
displayName: Skip integration tests
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
variables:
|
||||
nodeVersion: 20
|
||||
@@ -32,25 +48,17 @@ stages:
|
||||
- job: A
|
||||
displayName: Build Umbraco CMS
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: "windows-latest"
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 0
|
||||
submodules: true
|
||||
submodules: false
|
||||
lfs: false,
|
||||
fetchDepth: 500
|
||||
- template: templates/backoffice-install.yml
|
||||
- task: UseDotNet@2
|
||||
displayName: Use .NET SDK from global.json
|
||||
inputs:
|
||||
useGlobalJson: true
|
||||
- template: templates/backoffice-install.yml
|
||||
- script: npm run build:for:cms
|
||||
displayName: Run build (Bellissima)
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
- script: npm ci --no-fund --no-audit --prefer-offline
|
||||
displayName: Run npm ci (Login)
|
||||
workingDirectory: src/Umbraco.Web.UI.Login
|
||||
- script: npm run build
|
||||
displayName: Run npm build (Login)
|
||||
workingDirectory: src/Umbraco.Web.UI.Login
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet restore
|
||||
inputs:
|
||||
@@ -62,7 +70,7 @@ stages:
|
||||
inputs:
|
||||
command: build
|
||||
projects: $(solution)
|
||||
arguments: '--configuration $(buildConfiguration) --no-restore --property:ContinuousIntegrationBuild=true --property:GeneratePackageOnBuild=true --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg'
|
||||
arguments: "--configuration $(buildConfiguration) --no-restore --property:ContinuousIntegrationBuild=true --property:GeneratePackageOnBuild=true --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg"
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish nupkg
|
||||
inputs:
|
||||
@@ -74,8 +82,237 @@ stages:
|
||||
targetPath: $(Build.SourcesDirectory)
|
||||
artifactName: build_output
|
||||
|
||||
- stage: E2E
|
||||
displayName: E2E Tests
|
||||
- job: B
|
||||
displayName: Build Bellissima Package
|
||||
pool:
|
||||
vmImage: "ubuntu-latest"
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: false
|
||||
lfs: false,
|
||||
fetchDepth: 500
|
||||
- template: templates/backoffice-install.yml
|
||||
- script: npm run build:for:npm
|
||||
displayName: Run build:for:npm
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
- bash: |
|
||||
echo "##[command]Running npm pack"
|
||||
echo "##[debug]Output directory: $(Build.ArtifactStagingDirectory)"
|
||||
mkdir $(Build.ArtifactStagingDirectory)/npm
|
||||
npm pack --pack-destination $(Build.ArtifactStagingDirectory)/npm
|
||||
mv .npmrc $(Build.ArtifactStagingDirectory)/npm/
|
||||
displayName: Run npm pack
|
||||
workingDirectory: src/Umbraco.Web.UI.Client
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: Publish Bellissima npm artifact
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/npm
|
||||
artifactName: npm
|
||||
- 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 4 parts for each OS to reduce the time it takes to run them on the pipeline
|
||||
LinuxPart1Of4:
|
||||
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)"
|
||||
LinuxPart2Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
LinuxPart3Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
LinuxPart4Of4:
|
||||
vmImage: "ubuntu-latest"
|
||||
# Filter tests that are part of the ManagementApi namespace
|
||||
testFilter: "(FullyQualifiedName~ManagementApi)"
|
||||
macOSPart1Of4:
|
||||
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)"
|
||||
macOSPart2Of4:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are part of the Umbraco.Infrastructure.Service namespace
|
||||
testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)"
|
||||
macOSPart3Of4:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
macOSPart4Of4:
|
||||
vmImage: "macOS-latest"
|
||||
# Filter tests that are part of the ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName~ManagementApi)"
|
||||
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 4 parts for each OS to reduce the time it takes to run them on the pipeline
|
||||
WindowsPart1Of4:
|
||||
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)"
|
||||
WindowsPart2Of4:
|
||||
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)"
|
||||
WindowsPart3Of4:
|
||||
vmImage: "windows-latest"
|
||||
Tests__Database__DatabaseType: LocalDb
|
||||
Tests__Database__SQLServerMasterConnectionString: N/A
|
||||
# Filter tests that are not part of the Umbraco.Infrastructure and ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
WindowsPart4Of4:
|
||||
vmImage: "windows-latest"
|
||||
Tests__Database__DatabaseType: LocalDb
|
||||
Tests__Database__SQLServerMasterConnectionString: N/A
|
||||
# Filter tests that are part of the ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName~ManagementApi)"
|
||||
LinuxPart1Of4:
|
||||
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)"
|
||||
LinuxPart2Of4:
|
||||
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)"
|
||||
LinuxPart3Of4:
|
||||
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 and ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure) & (FullyQualifiedName!~ManagementApi)"
|
||||
LinuxPart4Of4:
|
||||
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 ManagementApi namespace.
|
||||
testFilter: "(FullyQualifiedName~ManagementApi)"
|
||||
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
|
||||
@@ -103,338 +340,290 @@ stages:
|
||||
- job:
|
||||
displayName: E2E Tests (SQLite)
|
||||
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
|
||||
strategy:
|
||||
matrix:
|
||||
LinuxPart1Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
testCommand: "npm run test -- --shard=1/3"
|
||||
testFolder: "DefaultConfig"
|
||||
testCommand: "npm run testSqlite -- --shard=1/3"
|
||||
LinuxPart2Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
testCommand: "npm run test -- --shard=2/3"
|
||||
testFolder: "DefaultConfig"
|
||||
testCommand: "npm run testSqlite -- --shard=2/3"
|
||||
LinuxPart3Of3:
|
||||
vmImage: "ubuntu-latest"
|
||||
testCommand: "npm run test -- --shard=3/3"
|
||||
testFolder: "DefaultConfig"
|
||||
testCommand: "npm run testSqlite -- --shard=3/3"
|
||||
WindowsPart1Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run test -- --shard=1/3"
|
||||
testFolder: "DefaultConfig"
|
||||
testCommand: "npm run testSqlite -- --shard=1/3"
|
||||
WindowsPart2Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run test -- --shard=2/3"
|
||||
testFolder: "DefaultConfig"
|
||||
testCommand: "npm run testSqlite -- --shard=2/3"
|
||||
WindowsPart3Of3:
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run test -- --shard=3/3"
|
||||
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" | 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 }}
|
||||
|
||||
- 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 --with-deps
|
||||
displayName: Install Playwright
|
||||
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()
|
||||
|
||||
# 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!
|
||||
strategy:
|
||||
matrix:
|
||||
LinuxPart1Of3:
|
||||
testCommand: "npm run testSqlite -- --shard=1/3"
|
||||
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);TrustServerCertificate=True"
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
|
||||
LinuxPart2Of3:
|
||||
testCommand: "npm run testSqlite -- --shard=2/3"
|
||||
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);TrustServerCertificate=True"
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
|
||||
LinuxPart3Of3:
|
||||
testCommand: "npm run testSqlite -- --shard=3/3"
|
||||
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);TrustServerCertificate=True"
|
||||
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
|
||||
WindowsPart1Of3:
|
||||
testCommand: "npm run test -- --shard=1/3"
|
||||
testFolder: "DefaultConfig"
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run testSqlite -- --shard=1/3"
|
||||
WindowsPart2Of3:
|
||||
testCommand: "npm run test -- --shard=2/3"
|
||||
testFolder: "DefaultConfig"
|
||||
vmImage: "windows-latest"
|
||||
testCommand: "npm run testSqlite -- --shard=2/3"
|
||||
WindowsPart3Of3:
|
||||
testCommand: "npm run test -- --shard=3/3"
|
||||
testFolder: "DefaultConfig"
|
||||
vmImage: "windows-latest"
|
||||
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)
|
||||
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" | 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 }}
|
||||
|
||||
- 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: ${{ eq(parameters.differentAppSettingsAcceptanceTests, 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: ${{ eq(variables['additionalEnvironmentVariables'], true) }}
|
||||
|
||||
# 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: ${{ eq(variables['additionalEnvironmentVariables'], true ) }}
|
||||
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 --with-deps
|
||||
displayName: Install Playwright
|
||||
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()
|
||||
|
||||
# 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 }}
|
||||
|
||||
@@ -18,7 +18,12 @@ internal sealed class DeliveryApiOutputCachePolicy : IOutputCachePolicy
|
||||
.RequestServices
|
||||
.GetRequiredService<IRequestPreviewService>();
|
||||
|
||||
context.EnableOutputCaching = requestPreviewService.IsPreview() is false;
|
||||
IApiAccessService apiAccessService = context
|
||||
.HttpContext
|
||||
.RequestServices
|
||||
.GetRequiredService<IApiAccessService>();
|
||||
|
||||
context.EnableOutputCaching = requestPreviewService.IsPreview() is false && apiAccessService.HasPublicAccess();
|
||||
context.ResponseExpirationTimeSpan = _duration;
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
@@ -5,11 +6,13 @@ using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Webhook.Logs;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Webhook.Logs;
|
||||
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Webhook}")]
|
||||
[ApiExplorerSettings(GroupName = "Webhook")]
|
||||
[Authorize(Policy = AuthorizationPolicies.TreeAccessWebhooks)]
|
||||
public class WebhookLogControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
protected PagedViewModel<WebhookLogResponseModel> CreatePagedWebhookLogResponseModel(PagedModel<WebhookLog> logs, IWebhookPresentationFactory webhookPresentationFactory)
|
||||
|
||||
@@ -127,10 +127,16 @@ internal class EFCoreScope<TDbContext> : CoreScope, IEfCoreScope<TDbContext>
|
||||
|
||||
Locks.ClearLocks(InstanceId);
|
||||
|
||||
if (ParentScope is null)
|
||||
// Since we can nest EFCoreScopes in other scopes derived from CoreScope, we should check whether our ParentScope OR the base ParentScope exists.
|
||||
// Only if neither do do we take responsibility for ensuring the locks are cleared.
|
||||
// Eventually the highest parent will clear the locks.
|
||||
// Further, these locks are a reference to the locks of the highest parent anyway (see the constructor of CoreScope).
|
||||
#pragma warning disable SA1100 // Do not prefix calls with base unless local implementation exists (justification: provides additional clarify here that this is defined on the base class).
|
||||
if (ParentScope is null && base.HasParentScope is false)
|
||||
{
|
||||
Locks.EnsureLocksCleared(InstanceId);
|
||||
}
|
||||
#pragma warning restore SA1100 // Do not prefix calls with base unless local implementation exists
|
||||
|
||||
_efCoreScopeProvider.PopAmbientScope();
|
||||
|
||||
|
||||
@@ -26,23 +26,36 @@
|
||||
<ProjectReference Include="..\Umbraco.Web.Website\Umbraco.Web.Website.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Restore and build backoffice project -->
|
||||
<!-- General ignored files -->
|
||||
<ItemGroup>
|
||||
<Content Remove="wwwroot\umbraco\assets\README.md" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<!-- BEGIN: Restore and build backoffice project -->
|
||||
<PropertyGroup>
|
||||
<BackofficeProjectDirectory Condition="'$(BackofficeProjectDirectory)' == ''">..\Umbraco.Web.UI.Client\</BackofficeProjectDirectory>
|
||||
<BackofficeAssetsPath>wwwroot\umbraco\backoffice</BackofficeAssetsPath>
|
||||
<BackofficeAssetsPath>$(ProjectDir)wwwroot\umbraco\backoffice</BackofficeAssetsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<BackofficeAssetsInputs Include="$(BackofficeProjectDirectory)package.json;$(BackofficeProjectDirectory)package-lock.json;$(BackofficeProjectDirectory)src\**" Exclude="$(DefaultItemExcludes)" />
|
||||
<Content Remove="$(BackofficeAssetsPath)\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="RestoreBackoffice" Inputs="$(BackofficeProjectDirectory)package-lock.json" Outputs="$(BackofficeProjectDirectory)node_modules\.package-lock.json">
|
||||
<Message Importance="high" Text="Restoring Backoffice NPM packages..." />
|
||||
<Exec Command="npm ci --no-fund --no-audit --prefer-offline" WorkingDirectory="$(BackofficeProjectDirectory)" />
|
||||
<Target Name="BuildStaticAssetsPreconditions" BeforeTargets="AssignTargetPaths">
|
||||
<Message Text="Skip BuildBackoffice target because UmbracoBuild is '$(UmbracoBuild)' (this is not Visual Studio)" Importance="high" Condition="'$(UmbracoBuild)' != ''" />
|
||||
<Message Text="Skip BuildBackoffice target because '$(BackofficeAssetsPath)' already exists" Importance="high" Condition="Exists('$(BackofficeAssetsPath)')" />
|
||||
<Message Text="Call BuildBackoffice target because UmbracoBuild is empty (this is Visual Studio) and '$(BackofficeAssetsPath)' doesn't exist" Importance="high" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BackofficeAssetsPath)')" />
|
||||
<CallTarget Targets="BuildBackoffice" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BackofficeAssetsPath)')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildBackoffice" DependsOnTargets="RestoreBackoffice" BeforeTargets="AssignTargetPaths" Inputs="@(BackofficeAssetsInputs)" Outputs="$(IntermediateOutputPath)backoffice.complete.txt">
|
||||
<Target Name="RestoreBackoffice" Inputs="$(BackofficeProjectDirectory)package-lock.json" Outputs="$(BackofficeProjectDirectory)node_modules\.package-lock.json">
|
||||
<Message Importance="high" Text="Restoring Backoffice NPM packages..." />
|
||||
<Exec Command="npm i --no-fund --no-audit" WorkingDirectory="$(BackofficeProjectDirectory)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildBackoffice" DependsOnTargets="RestoreBackoffice">
|
||||
<Message Importance="high" Text="Executing Backoffice NPM build script..." />
|
||||
<Exec Command="npm run build:for:cms" WorkingDirectory="$(BackofficeProjectDirectory)" />
|
||||
<ItemGroup>
|
||||
@@ -61,27 +74,46 @@
|
||||
</DefineStaticWebAssets>
|
||||
</Target>
|
||||
|
||||
<!-- Restore and build login project -->
|
||||
<Target Name="CleanStaticAssetsPreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
|
||||
<Message Text="Skip CleanBackoffice target because '$(BackofficeAssetsPath)' doesn't exist" Importance="high" Condition="!Exists('$(BackofficeAssetsPath)')" />
|
||||
<Message Text="Skip CleanBackoffice target because preserve.backoffice marker file exists" Importance="high" Condition="Exists('$(BackofficeAssetsPath)') and Exists('$(SolutionDir)preserve.backoffice')" />
|
||||
<Message Text="Call CleanBackoffice target because '$(BackofficeAssetsPath)' exists and preserve.backoffice marker file doesn't exist" Importance="high" Condition="Exists('$(BackofficeAssetsPath)') and !Exists('$(SolutionDir)preserve.backoffice')" />
|
||||
<CallTarget Targets="CleanBackoffice" Condition="Exists('$(BackofficeAssetsPath)') and !Exists('$(SolutionDir)preserve.backoffice')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanBackoffice">
|
||||
<ItemGroup>
|
||||
<BackofficeDirectories Include="$(BackofficeAssetsPath)" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(BackofficeDirectories)" />
|
||||
</Target>
|
||||
<!-- END: Restore and build backoffice project -->
|
||||
|
||||
|
||||
|
||||
<!-- BEGIN: Restore and build login project -->
|
||||
<PropertyGroup>
|
||||
<LoginProjectDirectory Condition="'$(LoginProjectDirectory)' == ''">..\Umbraco.Web.UI.Login\</LoginProjectDirectory>
|
||||
<LoginAssetsPath>wwwroot\umbraco\login</LoginAssetsPath>
|
||||
<LoginAssetsPath>$(ProjectDir)wwwroot\umbraco\login</LoginAssetsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<LoginAssetsInputs Include="$(LoginProjectDirectory)**" Exclude="$(DefaultItemExcludes)" />
|
||||
<Content Remove="$(LoginAssetsPath)\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="wwwroot\umbraco\assets\README.md" />
|
||||
</ItemGroup>
|
||||
<Target Name="BuildLoginStaticAssetsPreconditions" BeforeTargets="AssignTargetPaths">
|
||||
<Message Text="Skip BuildLogin target because UmbracoBuild is '$(UmbracoBuild)' (this is not Visual Studio)" Importance="high" Condition="'$(UmbracoBuild)' != ''" />
|
||||
<Message Text="Skip BuildLogin target because '$(LoginAssetsPath)' already exists" Importance="high" Condition="Exists('$(LoginAssetsPath)')" />
|
||||
<Message Text="Call BuildLogin target because UmbracoBuild is empty (this is Visual Studio) and '$(LoginAssetsPath)' doesn't exist" Importance="high" Condition="'$(UmbracoBuild)' == '' and !Exists('$(LoginAssetsPath)')" />
|
||||
<CallTarget Targets="BuildLogin" Condition="'$(UmbracoBuild)' == '' and !Exists('$(LoginAssetsPath)')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="RestoreLogin" Inputs="$(LoginProjectDirectory)package-lock.json" Outputs="$(LoginProjectDirectory)node_modules/.package-lock.json">
|
||||
<Message Importance="high" Text="Restoring Login NPM packages..." />
|
||||
<Exec Command="npm ci --no-fund --no-audit --prefer-offline" WorkingDirectory="$(LoginProjectDirectory)" />
|
||||
<Exec Command="npm i --no-fund --no-audit" WorkingDirectory="$(LoginProjectDirectory)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildLogin" DependsOnTargets="RestoreLogin" BeforeTargets="AssignTargetPaths" Inputs="@(LoginAssetsInputs)" Outputs="$(IntermediateOutputPath)login.complete.txt">
|
||||
<Target Name="BuildLogin" DependsOnTargets="RestoreLogin">
|
||||
<Message Importance="high" Text="Executing Login NPM build script..." />
|
||||
<Exec Command="npm run build" WorkingDirectory="$(LoginProjectDirectory)" />
|
||||
<ItemGroup>
|
||||
@@ -99,4 +131,19 @@
|
||||
<Output TaskParameter="Assets" ItemName="StaticWebAsset" />
|
||||
</DefineStaticWebAssets>
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanLoginStaticAssetsPreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
|
||||
<Message Text="Skip CleanLogin target because '$(LoginAssetsPath)' doesn't exist" Importance="high" Condition="!Exists('$(LoginAssetsPath)')" />
|
||||
<Message Text="Skip CleanLogin target because preserve.login marker file exists" Importance="high" Condition="Exists('$(LoginAssetsPath)') and Exists('$(SolutionDir)preserve.login')" />
|
||||
<Message Text="Call CleanLogin target because '$(LoginAssetsPath)' exists and preserve.login marker file doesn't exist" Importance="high" Condition="Exists('$(LoginAssetsPath)') and !Exists('$(SolutionDir)preserve.login')" />
|
||||
<CallTarget Targets="CleanLogin" Condition="Exists('$(LoginAssetsPath)') and !Exists('$(SolutionDir)preserve.login')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanLogin">
|
||||
<ItemGroup>
|
||||
<LoginDirectories Include="$(LoginAssetsPath)" />
|
||||
</ItemGroup>
|
||||
<RemoveDir Directories="@(LoginDirectories)" />
|
||||
</Target>
|
||||
<!-- END: Restore and build login project -->
|
||||
</Project>
|
||||
|
||||
@@ -22,4 +22,6 @@ public static class CacheKeys
|
||||
|
||||
public const string PreviewPropertyCacheKeyPrefix = "Cache.Property.CacheValues[D:";
|
||||
public const string PropertyCacheKeyPrefix = "Cache.Property.CacheValues[P:";
|
||||
|
||||
public const string MemberUserNameCachePrefix = "uRepo_userNameKey+";
|
||||
}
|
||||
|
||||
@@ -71,11 +71,22 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCache
|
||||
foreach (JsonPayload p in payloads)
|
||||
{
|
||||
_idKeyMap.ClearCache(p.Id);
|
||||
if (memberCache.Success)
|
||||
if (memberCache.Success is false)
|
||||
{
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
|
||||
continue;
|
||||
}
|
||||
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
|
||||
|
||||
// This specific cache key was introduced to fix an issue where the member username could not be the same as the member id, because the cache keys collided.
|
||||
// This is done in a bit of a hacky way, because the cache key is created internally in the repository, but we need to clear it here.
|
||||
// Ideally, we want to use a shared way of generating the key between this and the repository.
|
||||
// Additionally, the RepositoryCacheKeys actually caches the string to avoid re-allocating memory; we would like to also use this in the repository
|
||||
// See:
|
||||
// https://github.com/umbraco/Umbraco-CMS/pull/17350
|
||||
// https://github.com/umbraco/Umbraco-CMS/pull/17815
|
||||
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(CacheKeys.MemberUserNameCachePrefix + p.Username));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,17 @@ namespace Umbraco.Extensions;
|
||||
public static class ContentSettingsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if file extension is allowed for upload based on (optional) white list and black list
|
||||
/// held in settings.
|
||||
/// Allow upload if extension is whitelisted OR if there is no whitelist and extension is NOT blacklisted.
|
||||
/// Determines if file extension is allowed for upload based on (optional) allow list and deny list held in settings.
|
||||
/// Disallowed file extensions are only considered if there are no allowed file extensions.
|
||||
/// </summary>
|
||||
public static bool IsFileAllowedForUpload(this ContentSettings contentSettings, string extension) =>
|
||||
contentSettings.AllowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension)) ||
|
||||
(contentSettings.AllowedUploadedFileExtensions.Any() == false &&
|
||||
contentSettings.DisallowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension)) == false);
|
||||
/// <param name="contentSettings">The content settings.</param>
|
||||
/// <param name="extension">The file extension.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the file extension is allowed for upload; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool IsFileAllowedForUpload(this ContentSettings contentSettings, string extension)
|
||||
=> contentSettings.AllowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension.Trim())) ||
|
||||
(contentSettings.AllowedUploadedFileExtensions.Any() == false && contentSettings.DisallowedUploadedFileExtensions.Any(x => x.InvariantEquals(extension.Trim())) == false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the auto-fill configuration for a specified property alias.
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Cms.Core.HostedServices;
|
||||
public interface IBackgroundTaskQueue
|
||||
{
|
||||
/// <summary>
|
||||
/// Enqueue a work item to be executed on in the background.
|
||||
/// Enqueue a work item to be executed in the background.
|
||||
/// </summary>
|
||||
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ namespace Umbraco.Cms.Core.IO
|
||||
|
||||
// nothing prevents us to reach the file, security-wise, yet it is outside
|
||||
// this filesystem's root - throw
|
||||
throw new UnauthorizedAccessException($"File original: [{originalPath}] full: [{path}] is outside this filesystem's root.");
|
||||
throw new UnauthorizedAccessException($"Requested path {originalPath} is outside this filesystem's root.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Editors;
|
||||
|
||||
@@ -12,14 +15,27 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase<IDataVa
|
||||
// TODO: We could further reduce circular dependencies with PropertyEditorCollection by not having IDataValueReference implemented
|
||||
// by property editors and instead just use the already built in IDataValueReferenceFactory and/or refactor that into a more normal collection
|
||||
|
||||
private readonly ILogger<DataValueReferenceFactoryCollection> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataValueReferenceFactoryCollection" /> class.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
|
||||
public DataValueReferenceFactoryCollection(Func<IEnumerable<IDataValueReferenceFactory>> items)
|
||||
: base(items)
|
||||
: this(
|
||||
items,
|
||||
StaticServiceProvider.Instance.GetRequiredService<ILogger<DataValueReferenceFactoryCollection>>())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataValueReferenceFactoryCollection" /> class.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public DataValueReferenceFactoryCollection(Func<IEnumerable<IDataValueReferenceFactory>> items, ILogger<DataValueReferenceFactoryCollection> logger)
|
||||
: base(items) => _logger = logger;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all unique references from the specified properties.
|
||||
/// </summary>
|
||||
@@ -33,7 +49,7 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase<IDataVa
|
||||
var references = new HashSet<UmbracoEntityReference>();
|
||||
|
||||
// Group by property editor alias to avoid duplicate lookups and optimize value parsing
|
||||
foreach (var propertyValuesByPropertyEditorAlias in properties.GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Values))
|
||||
foreach (IGrouping<string, IReadOnlyCollection<IPropertyValue>> propertyValuesByPropertyEditorAlias in properties.GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Values))
|
||||
{
|
||||
if (!propertyEditors.TryGet(propertyValuesByPropertyEditorAlias.Key, out IDataEditor? dataEditor))
|
||||
{
|
||||
@@ -48,7 +64,7 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase<IDataVa
|
||||
values.Add(propertyValue.PublishedValue);
|
||||
}
|
||||
|
||||
references.UnionWith(GetReferences(dataEditor, values));
|
||||
references.UnionWith(GetReferences(dataEditor, values, propertyValuesByPropertyEditorAlias.Key));
|
||||
}
|
||||
|
||||
return references;
|
||||
@@ -74,14 +90,18 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase<IDataVa
|
||||
/// The references.
|
||||
/// </returns>
|
||||
public ISet<UmbracoEntityReference> GetReferences(IDataEditor dataEditor, IEnumerable<object?> values) =>
|
||||
GetReferencesEnumerable(dataEditor, values).ToHashSet();
|
||||
private IEnumerable<UmbracoEntityReference> GetReferencesEnumerable(IDataEditor dataEditor, IEnumerable<object?> values)
|
||||
GetReferencesEnumerable(dataEditor, values, null).ToHashSet();
|
||||
|
||||
private ISet<UmbracoEntityReference> GetReferences(IDataEditor dataEditor, IEnumerable<object?> values, string propertyEditorAlias) =>
|
||||
GetReferencesEnumerable(dataEditor, values, propertyEditorAlias).ToHashSet();
|
||||
|
||||
private IEnumerable<UmbracoEntityReference> GetReferencesEnumerable(IDataEditor dataEditor, IEnumerable<object?> values, string? propertyEditorAlias)
|
||||
{
|
||||
// TODO: We will need to change this once we support tracking via variants/segments
|
||||
// for now, we are tracking values from ALL variants
|
||||
if (dataEditor.GetValueEditor() is IDataValueReference dataValueReference)
|
||||
{
|
||||
foreach (UmbracoEntityReference reference in values.SelectMany(dataValueReference.GetReferences))
|
||||
foreach (UmbracoEntityReference reference in GetReferencesFromPropertyValues(values, dataValueReference, propertyEditorAlias))
|
||||
{
|
||||
yield return reference;
|
||||
}
|
||||
@@ -107,6 +127,38 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase<IDataVa
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<UmbracoEntityReference> GetReferencesFromPropertyValues(IEnumerable<object?> values, IDataValueReference dataValueReference, string? propertyEditorAlias)
|
||||
{
|
||||
var result = new List<UmbracoEntityReference>();
|
||||
foreach (var value in values)
|
||||
{
|
||||
// When property editors on data types are changed, we could have values that are incompatible with the new editor.
|
||||
// Leading to issues such as:
|
||||
// - https://github.com/umbraco/Umbraco-CMS/issues/17628
|
||||
// - https://github.com/umbraco/Umbraco-CMS/issues/17725
|
||||
// Although some changes like this are not intended to be compatible, we should handle them gracefully and not
|
||||
// error in retrieving references, which would prevent manipulating or deleting the content that uses the data type.
|
||||
try
|
||||
{
|
||||
IEnumerable<UmbracoEntityReference> references = dataValueReference.GetReferences(value);
|
||||
result.AddRange(references);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the exception but don't throw, continue with the next value.
|
||||
_logger.LogError(
|
||||
ex,
|
||||
"Error getting references from value {Value} with data editor {DataEditor} and property editor alias {PropertyEditorAlias}.",
|
||||
value,
|
||||
dataValueReference.GetType().FullName,
|
||||
propertyEditorAlias ?? "n/a");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all relation type aliases that are automatically tracked.
|
||||
/// </summary>
|
||||
@@ -117,6 +169,7 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase<IDataVa
|
||||
[Obsolete("Use GetAllAutomaticRelationTypesAliases. This will be removed in Umbraco 15.")]
|
||||
public ISet<string> GetAutomaticRelationTypesAliases(PropertyEditorCollection propertyEditors) =>
|
||||
GetAllAutomaticRelationTypesAliases(propertyEditors);
|
||||
|
||||
public ISet<string> GetAllAutomaticRelationTypesAliases(PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
// Always add default automatic relation types
|
||||
|
||||
@@ -101,7 +101,7 @@ public interface IPublishedCache
|
||||
/// </summary>
|
||||
/// <param name="id">The content type unique identifier.</param>
|
||||
/// <returns>The content type, or null.</returns>
|
||||
[Obsolete("Please use the IContentTypeCacheService instead, scheduled for removal in V16")]
|
||||
[Obsolete("Please use the IPublishedContentTypeCache instead, scheduled for removal in V16")]
|
||||
IPublishedContentType? GetContentType(int id);
|
||||
|
||||
/// <summary>
|
||||
@@ -110,7 +110,7 @@ public interface IPublishedCache
|
||||
/// <param name="alias">The content type alias.</param>
|
||||
/// <returns>The content type, or null.</returns>
|
||||
/// <remarks>The alias is case-insensitive.</remarks>
|
||||
[Obsolete("Please use the IContentTypeCacheService instead, scheduled for removal in V16")]
|
||||
[Obsolete("Please use the IPublishedContentTypeCache instead, scheduled for removal in V16")]
|
||||
IPublishedContentType? GetContentType(string alias);
|
||||
|
||||
/// <summary>
|
||||
@@ -126,6 +126,6 @@ public interface IPublishedCache
|
||||
/// </summary>
|
||||
/// <param name="key">The content type key.</param>
|
||||
/// <returns>The content type, or null.</returns>
|
||||
[Obsolete("Please use the IContentTypeCacheService instead, scheduled for removal in V16")]
|
||||
[Obsolete("Please use the IPublishedContentTypeCache instead, scheduled for removal in V16")]
|
||||
IPublishedContentType? GetContentType(Guid key);
|
||||
}
|
||||
|
||||
@@ -250,6 +250,8 @@ public class CoreScope : ICoreScope
|
||||
_parentScope = coreScope;
|
||||
}
|
||||
|
||||
protected bool HasParentScope => _parentScope is not null;
|
||||
|
||||
protected void HandleScopedNotifications() => _notificationPublisher?.ScopeExit(Completed.HasValue && Completed.Value);
|
||||
|
||||
private void EnsureNotDisposed()
|
||||
|
||||
@@ -44,26 +44,51 @@ public class DocumentUrlService : IDocumentUrlService
|
||||
/// <summary>
|
||||
/// Model used to cache a single published document along with all it's URL segments.
|
||||
/// </summary>
|
||||
private class PublishedDocumentUrlSegments
|
||||
/// <remarks>Internal for the purpose of unit and benchmark testing.</remarks>
|
||||
internal class PublishedDocumentUrlSegments
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the document key.
|
||||
/// </summary>
|
||||
public required Guid DocumentKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the language Id.
|
||||
/// </summary>
|
||||
public required int LanguageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of <see cref="UrlSegment"/> for the document, language and state.
|
||||
/// </summary>
|
||||
public required IList<UrlSegment> UrlSegments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the document is a draft version or not.
|
||||
/// </summary>
|
||||
public required bool IsDraft { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Model used to represent a URL segment for a document in the cache.
|
||||
/// </summary>
|
||||
public class UrlSegment
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlSegment"/> class.
|
||||
/// </summary>
|
||||
public UrlSegment(string segment, bool isPrimary)
|
||||
{
|
||||
Segment = segment;
|
||||
IsPrimary = isPrimary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL segment string.
|
||||
/// </summary>
|
||||
public string Segment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this URL segment is the primary one for the document, language and state.
|
||||
/// </summary>
|
||||
public bool IsPrimary { get; }
|
||||
}
|
||||
}
|
||||
@@ -168,45 +193,40 @@ public class DocumentUrlService : IDocumentUrlService
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
private static IEnumerable<PublishedDocumentUrlSegments> ConvertToCacheModel(IEnumerable<PublishedDocumentUrlSegment> publishedDocumentUrlSegments)
|
||||
/// <summary>
|
||||
/// Converts a collection of <see cref="PublishedDocumentUrlSegment"/> to a collection of <see cref="PublishedDocumentUrlSegments"/> for caching purposes.
|
||||
/// </summary>
|
||||
/// <param name="publishedDocumentUrlSegments">The collection of <see cref="PublishedDocumentUrlSegment"/> retrieved from the database on startup.</param>
|
||||
/// <returns>The collection of cache models.</returns>
|
||||
/// <remarks>Internal for the purpose of unit and benchmark testing.</remarks>
|
||||
internal static IEnumerable<PublishedDocumentUrlSegments> ConvertToCacheModel(IEnumerable<PublishedDocumentUrlSegment> publishedDocumentUrlSegments)
|
||||
{
|
||||
var cacheModels = new List<PublishedDocumentUrlSegments>();
|
||||
var cacheModels = new Dictionary<(Guid DocumentKey, int LanguageId, bool IsDraft), PublishedDocumentUrlSegments>();
|
||||
|
||||
foreach (PublishedDocumentUrlSegment model in publishedDocumentUrlSegments)
|
||||
{
|
||||
PublishedDocumentUrlSegments? existingCacheModel = GetModelFromCache(cacheModels, model);
|
||||
if (existingCacheModel is null)
|
||||
(Guid DocumentKey, int LanguageId, bool IsDraft) key = (model.DocumentKey, model.LanguageId, model.IsDraft);
|
||||
|
||||
if (!cacheModels.TryGetValue(key, out PublishedDocumentUrlSegments? existingCacheModel))
|
||||
{
|
||||
cacheModels.Add(new PublishedDocumentUrlSegments
|
||||
cacheModels[key] = new PublishedDocumentUrlSegments
|
||||
{
|
||||
DocumentKey = model.DocumentKey,
|
||||
LanguageId = model.LanguageId,
|
||||
UrlSegments = [new PublishedDocumentUrlSegments.UrlSegment(model.UrlSegment, model.IsPrimary)],
|
||||
IsDraft = model.IsDraft,
|
||||
});
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
existingCacheModel.UrlSegments = GetUpdatedUrlSegments(existingCacheModel.UrlSegments, model.UrlSegment, model.IsPrimary);
|
||||
if (existingCacheModel.UrlSegments.Any(x => x.Segment == model.UrlSegment) is false)
|
||||
{
|
||||
existingCacheModel.UrlSegments.Add(new PublishedDocumentUrlSegments.UrlSegment(model.UrlSegment, model.IsPrimary));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cacheModels;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static PublishedDocumentUrlSegments? GetModelFromCache(List<PublishedDocumentUrlSegments> cacheModels, PublishedDocumentUrlSegment model)
|
||||
=> cacheModels
|
||||
.SingleOrDefault(x => x.DocumentKey == model.DocumentKey && x.LanguageId == model.LanguageId && x.IsDraft == model.IsDraft);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static IList<PublishedDocumentUrlSegments.UrlSegment> GetUpdatedUrlSegments(IList<PublishedDocumentUrlSegments.UrlSegment> urlSegments, string segment, bool isPrimary)
|
||||
{
|
||||
if (urlSegments.FirstOrDefault(x => x.Segment == segment) is null)
|
||||
{
|
||||
urlSegments.Add(new PublishedDocumentUrlSegments.UrlSegment(segment, isPrimary));
|
||||
}
|
||||
|
||||
return urlSegments;
|
||||
return cacheModels.Values;
|
||||
}
|
||||
|
||||
private void RemoveFromCache(IScopeContext scopeContext, Guid documentKey, string isoCode, bool isDraft)
|
||||
|
||||
@@ -9,4 +9,13 @@ namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
[Obsolete("This has been relocated into Umbraco.Cms.Core. This definition in Umbraco.Cms.Infrastructure is scheduled for removal in Umbraco 17.")]
|
||||
public interface IBackgroundTaskQueue : Core.HostedServices.IBackgroundTaskQueue
|
||||
{
|
||||
/// <summary>
|
||||
/// Enqueue a work item to be executed in the background.
|
||||
/// </summary>
|
||||
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
|
||||
|
||||
/// <summary>
|
||||
/// Dequeue the first item on the queue.
|
||||
/// </summary>
|
||||
Task<Func<CancellationToken, Task>?> DequeueAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
|
||||
private readonly ITagRepository _tagRepository;
|
||||
private bool _passwordConfigInitialized;
|
||||
private string? _passwordConfigJson;
|
||||
private const string UsernameCacheKey = "uRepo_userNameKey+";
|
||||
|
||||
public MemberRepository(
|
||||
IScopeAccessor scopeAccessor,
|
||||
@@ -327,7 +326,7 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
|
||||
}
|
||||
|
||||
public IMember? GetByUsername(string? username) =>
|
||||
_memberByUsernameCachePolicy.GetByUserName(UsernameCacheKey, username, PerformGetByUsername, PerformGetAllByUsername);
|
||||
_memberByUsernameCachePolicy.GetByUserName(CacheKeys.MemberUserNameCachePrefix, username, PerformGetByUsername, PerformGetAllByUsername);
|
||||
|
||||
public int[] GetMemberIds(string[] usernames)
|
||||
{
|
||||
@@ -609,7 +608,7 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
|
||||
|
||||
protected override void PersistDeletedItem(IMember entity)
|
||||
{
|
||||
_memberByUsernameCachePolicy.DeleteByUserName(UsernameCacheKey, entity.Username);
|
||||
_memberByUsernameCachePolicy.DeleteByUserName(CacheKeys.MemberUserNameCachePrefix, entity.Username);
|
||||
base.PersistDeletedItem(entity);
|
||||
}
|
||||
|
||||
@@ -943,7 +942,7 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
|
||||
|
||||
OnUowRefreshedEntity(new MemberRefreshNotification(entity, new EventMessages()));
|
||||
|
||||
_memberByUsernameCachePolicy.DeleteByUserName(UsernameCacheKey, entity.Username);
|
||||
_memberByUsernameCachePolicy.DeleteByUserName(CacheKeys.MemberUserNameCachePrefix, entity.Username);
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
@@ -99,17 +99,17 @@ public abstract class BlockValuePropertyValueEditorBase<TValue, TLayout> : DataV
|
||||
continue;
|
||||
}
|
||||
|
||||
var districtValues = valuesByPropertyEditorAlias.Distinct().ToArray();
|
||||
var distinctValues = valuesByPropertyEditorAlias.Distinct().ToArray();
|
||||
|
||||
if (dataEditor.GetValueEditor() is IDataValueReference reference)
|
||||
{
|
||||
foreach (UmbracoEntityReference value in districtValues.SelectMany(reference.GetReferences))
|
||||
foreach (UmbracoEntityReference value in distinctValues.SelectMany(reference.GetReferences))
|
||||
{
|
||||
result.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<UmbracoEntityReference> references = _dataValueReferenceFactoryCollection.GetReferences(dataEditor, districtValues);
|
||||
IEnumerable<UmbracoEntityReference> references = _dataValueReferenceFactoryCollection.GetReferences(dataEditor, distinctValues);
|
||||
|
||||
foreach (UmbracoEntityReference value in references)
|
||||
{
|
||||
|
||||
+141
-141
@@ -1,32 +1,32 @@
|
||||
{
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"version": "15.4.0-rc2",
|
||||
"version": "15.5.0-rc",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"version": "15.4.0-rc2",
|
||||
"version": "15.5.0-rc",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"./src/packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@tiptap/core": "2.11.5",
|
||||
"@tiptap/extension-character-count": "^2.11.5",
|
||||
"@tiptap/extension-image": "2.11.5",
|
||||
"@tiptap/extension-link": "2.11.5",
|
||||
"@tiptap/extension-placeholder": "2.11.5",
|
||||
"@tiptap/extension-subscript": "2.11.5",
|
||||
"@tiptap/extension-superscript": "2.11.5",
|
||||
"@tiptap/extension-table": "2.11.5",
|
||||
"@tiptap/extension-table-cell": "2.11.5",
|
||||
"@tiptap/extension-table-header": "2.11.5",
|
||||
"@tiptap/extension-table-row": "2.11.5",
|
||||
"@tiptap/extension-text-align": "2.11.5",
|
||||
"@tiptap/extension-underline": "2.11.5",
|
||||
"@tiptap/pm": "2.11.5",
|
||||
"@tiptap/starter-kit": "2.11.5",
|
||||
"@tiptap/core": "2.11.7",
|
||||
"@tiptap/extension-character-count": "2.11.7",
|
||||
"@tiptap/extension-image": "2.11.7",
|
||||
"@tiptap/extension-link": "2.11.7",
|
||||
"@tiptap/extension-placeholder": "2.11.7",
|
||||
"@tiptap/extension-subscript": "2.11.7",
|
||||
"@tiptap/extension-superscript": "2.11.7",
|
||||
"@tiptap/extension-table": "2.11.7",
|
||||
"@tiptap/extension-table-cell": "2.11.7",
|
||||
"@tiptap/extension-table-header": "2.11.7",
|
||||
"@tiptap/extension-table-row": "2.11.7",
|
||||
"@tiptap/extension-text-align": "2.11.7",
|
||||
"@tiptap/extension-underline": "2.11.7",
|
||||
"@tiptap/pm": "2.11.7",
|
||||
"@tiptap/starter-kit": "2.11.7",
|
||||
"@types/diff": "^7.0.1",
|
||||
"@umbraco-ui/uui": "^1.13.0",
|
||||
"@umbraco-ui/uui-css": "^1.13.0",
|
||||
@@ -2534,9 +2534,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/core": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.11.5.tgz",
|
||||
"integrity": "sha512-jb0KTdUJaJY53JaN7ooY3XAxHQNoMYti/H6ANo707PsLXVeEqJ9o8+eBup1JU5CuwzrgnDc2dECt2WIGX9f8Jw==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.11.7.tgz",
|
||||
"integrity": "sha512-zN+NFFxLsxNEL8Qioc+DL6b8+Tt2bmRbXH22Gk6F6nD30x83eaUSFlSv3wqvgyCq3I1i1NO394So+Agmayx6rQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2547,9 +2547,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-blockquote": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.11.5.tgz",
|
||||
"integrity": "sha512-MZfcRIzKRD8/J1hkt/eYv49060GTL6qGR3NY/oTDuw2wYzbQXXLEbjk8hxAtjwNn7G+pWQv3L+PKFzZDxibLuA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.11.7.tgz",
|
||||
"integrity": "sha512-liD8kWowl3CcYCG9JQlVx1eSNc/aHlt6JpVsuWvzq6J8APWX693i3+zFqyK2eCDn0k+vW62muhSBe3u09hA3Zw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2560,9 +2560,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bold": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.11.5.tgz",
|
||||
"integrity": "sha512-OAq03MHEbl7MtYCUzGuwb0VpOPnM0k5ekMbEaRILFU5ZC7cEAQ36XmPIw1dQayrcuE8GZL35BKub2qtRxyC9iA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.11.7.tgz",
|
||||
"integrity": "sha512-VTR3JlldBixXbjpLTFme/Bxf1xeUgZZY3LTlt5JDlCW3CxO7k05CIa+kEZ8LXpog5annytZDUVtWqxrNjmsuHQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2573,9 +2573,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bullet-list": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.11.5.tgz",
|
||||
"integrity": "sha512-VXwHlX6A/T6FAspnyjbKDO0TQ+oetXuat6RY1/JxbXphH42nLuBaGWJ6pgy6xMl6XY8/9oPkTNrfJw/8/eeRwA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.11.7.tgz",
|
||||
"integrity": "sha512-WbPogE2/Q3e3/QYgbT1Sj4KQUfGAJNc5pvb7GrUbvRQsAh7HhtuO8hqdDwH8dEdD/cNUehgt17TO7u8qV6qeBw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2586,9 +2586,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-character-count": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.11.5.tgz",
|
||||
"integrity": "sha512-Da2VGb7ClmKwXdQdQC2735qylYD8/MQAPA0skPEcHxcDTDuI8ibyIDnMPnczgS/hR5g0TYE2DQp/dkhJXeovkQ==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.11.7.tgz",
|
||||
"integrity": "sha512-gcVbKou+uxzg8N0BBKceLwtpWvN8g2TIjTuCdyAcAPukX63DqVWOkofFHn1RqZbstJmtF4pTGZs9OH/GJrp27Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2600,9 +2600,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.11.5.tgz",
|
||||
"integrity": "sha512-xOvHevNIQIcCCVn9tpvXa1wBp0wHN/2umbAZGTVzS+AQtM7BTo0tz8IyzwxkcZJaImONcUVYLOLzt2AgW1LltA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.11.7.tgz",
|
||||
"integrity": "sha512-VpPO1Uy/eF4hYOpohS/yMOcE1C07xmMj0/D989D9aS1x95jWwUVrSkwC+PlWMUBx9PbY2NRsg1ZDwVvlNKZ6yQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2613,9 +2613,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code-block": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.11.5.tgz",
|
||||
"integrity": "sha512-ksxMMvqLDlC+ftcQLynqZMdlJT1iHYZorXsXw/n+wuRd7YElkRkd6YWUX/Pq/njFY6lDjKiqFLEXBJB8nrzzBA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.11.7.tgz",
|
||||
"integrity": "sha512-To/y/2H04VWqiANy53aXjV7S6fA86c2759RsH1hTIe57jA1KyE7I5tlAofljOLZK/covkGmPeBddSPHGJbz++Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2627,9 +2627,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-document": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.11.5.tgz",
|
||||
"integrity": "sha512-7I4BRTpIux2a0O2qS3BDmyZ5LGp3pszKbix32CmeVh7lN9dV7W5reDqtJJ9FCZEEF+pZ6e1/DQA362dflwZw2g==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.11.7.tgz",
|
||||
"integrity": "sha512-95ouJXPjdAm9+VBRgFo4lhDoMcHovyl/awORDI8gyEn0Rdglt+ZRZYoySFzbVzer9h0cre+QdIwr9AIzFFbfdA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2640,9 +2640,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-dropcursor": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.11.5.tgz",
|
||||
"integrity": "sha512-uIN7L3FU0904ec7FFFbndO7RQE/yiON4VzAMhNn587LFMyWO8US139HXIL4O8dpZeYwYL3d1FnDTflZl6CwLlg==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.11.7.tgz",
|
||||
"integrity": "sha512-63mL+nxQILizsr5NbmgDeOjFEWi34BLt7evwL6UUZEVM15K8V1G8pD9Y0kCXrZYpHWz0tqFRXdrhDz0Ppu8oVw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2654,9 +2654,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-gapcursor": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.11.5.tgz",
|
||||
"integrity": "sha512-kcWa+Xq9cb6lBdiICvLReuDtz/rLjFKHWpW3jTTF3FiP3wx4H8Rs6bzVtty7uOVTfwupxZRiKICAMEU6iT0xrQ==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.11.7.tgz",
|
||||
"integrity": "sha512-EceesmPG7FyjXZ8EgeJPUov9G1mAf2AwdypxBNH275g6xd5dmU/KvjoFZjmQ0X1ve7mS+wNupVlGxAEUYoveew==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2668,9 +2668,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-hard-break": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.11.5.tgz",
|
||||
"integrity": "sha512-q9doeN+Yg9F5QNTG8pZGYfNye3tmntOwch683v0CCVCI4ldKaLZ0jG3NbBTq+mosHYdgOH2rNbIORlRRsQ+iYQ==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.11.7.tgz",
|
||||
"integrity": "sha512-zTkZSA6q+F5sLOdCkiC2+RqJQN0zdsJqvFIOVFL/IDVOnq6PZO5THzwRRLvOSnJJl3edRQCl/hUgS0L5sTInGQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2681,9 +2681,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-heading": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.11.5.tgz",
|
||||
"integrity": "sha512-x/MV53psJ9baRcZ4k4WjnCUBMt8zCX7mPlKVT+9C/o+DEs/j/qxPLs95nHeQv70chZpSwCQCt93xMmuF0kPoAg==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.11.7.tgz",
|
||||
"integrity": "sha512-8kWh7y4Rd2fwxfWOhFFWncHdkDkMC1Z60yzIZWjIu72+6yQxvo8w3yeb7LI7jER4kffbMmadgcfhCHC/fkObBA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2694,9 +2694,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-history": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.11.5.tgz",
|
||||
"integrity": "sha512-b+wOS33Dz1azw6F1i9LFTEIJ/gUui0Jwz5ZvmVDpL2ZHBhq1Ui0/spTT+tuZOXq7Y/uCbKL8Liu4WoedIvhboQ==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.11.7.tgz",
|
||||
"integrity": "sha512-Cu5x3aS13I040QSRoLdd+w09G4OCVfU+azpUqxufZxeNs9BIJC+0jowPLeOxKDh6D5GGT2A8sQtxc6a/ssbs8g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2708,9 +2708,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.11.5.tgz",
|
||||
"integrity": "sha512-3up2r1Du8/5/4ZYzTC0DjTwhgPI3dn8jhOCLu73m5F3OGvK/9whcXoeWoX103hYMnGDxBlfOje71yQuN35FL4A==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.11.7.tgz",
|
||||
"integrity": "sha512-uVmQwD2dzZ5xwmvUlciy0ItxOdOfQjH6VLmu80zyJf8Yu7mvwP8JyxoXUX0vd1xHpwAhgQ9/ozjIWYGIw79DPQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2722,9 +2722,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-image": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.11.5.tgz",
|
||||
"integrity": "sha512-HbUq9AL8gb8eSuQfY/QKkvMc66ZFN/b6jvQAILGArNOgalUfGizoC6baKTJShaExMSPjBZlaAHtJiQKPaGRHaA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.11.7.tgz",
|
||||
"integrity": "sha512-YvCmTDB7Oo+A56tR4S/gcNaYpqU4DDlSQcRp5IQvmQV5EekSe0lnEazGDoqOCwsit9qQhj4MPQJhKrnaWrJUrg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2735,9 +2735,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-italic": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.11.5.tgz",
|
||||
"integrity": "sha512-9VGfb2/LfPhQ6TjzDwuYLRvw0A6VGbaIp3F+5Mql8XVdTBHb2+rhELbyhNGiGVR78CaB/EiKb6dO9xu/tBWSYA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.11.7.tgz",
|
||||
"integrity": "sha512-r985bkQfG0HMpmCU0X0p/Xe7U1qgRm2mxvcp6iPCuts2FqxaCoyfNZ8YnMsgVK1mRhM7+CQ5SEg2NOmQNtHvPw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2748,9 +2748,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-link": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.11.5.tgz",
|
||||
"integrity": "sha512-4Iu/aPzevbYpe50xDI0ZkqRa6nkZ9eF270Ue2qaF3Ab47nehj+9Jl78XXzo8+LTyFMnrETI73TAs1aC/IGySeQ==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.11.7.tgz",
|
||||
"integrity": "sha512-qKIowE73aAUrnQCIifYP34xXOHOsZw46cT/LBDlb0T60knVfQoKVE4ku08fJzAV+s6zqgsaaZ4HVOXkQYLoW7g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linkifyjs": "^4.2.0"
|
||||
@@ -2765,9 +2765,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-item": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.11.5.tgz",
|
||||
"integrity": "sha512-Mp5RD/pbkfW1vdc6xMVxXYcta73FOwLmblQlFNn/l/E5/X1DUSA4iGhgDDH4EWO3swbs03x2f7Zka/Xoj3+WLg==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.11.7.tgz",
|
||||
"integrity": "sha512-6ikh7Y+qAbkSuIHXPIINqfzmWs5uIGrylihdZ9adaIyvrN1KSnWIqrZIk/NcZTg5YFIJlXrnGSRSjb/QM3WUhw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2778,9 +2778,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-ordered-list": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.11.5.tgz",
|
||||
"integrity": "sha512-Cu8KwruBNWAaEfshRQR0yOSaUKAeEwxW7UgbvF9cN/zZuKgK5uZosPCPTehIFCcRe+TBpRtZQh+06f/gNYpYYg==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.11.7.tgz",
|
||||
"integrity": "sha512-bLGCHDMB0vbJk7uu8bRg8vES3GsvxkX7Cgjgm/6xysHFbK98y0asDtNxkW1VvuRreNGz4tyB6vkcVCfrxl4jKw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2791,9 +2791,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-paragraph": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.11.5.tgz",
|
||||
"integrity": "sha512-YFBWeg7xu/sBnsDIF/+nh9Arf7R0h07VZMd0id5Ydd2Qe3c1uIZwXxeINVtH0SZozuPIQFAT8ICe9M0RxmE+TA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.11.7.tgz",
|
||||
"integrity": "sha512-Pl3B4q6DJqTvvAdraqZaNP9Hh0UWEHL5nNdxhaRNuhKaUo7lq8wbDSIxIW3lvV0lyCs0NfyunkUvSm1CXb6d4Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2804,9 +2804,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-placeholder": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.11.5.tgz",
|
||||
"integrity": "sha512-Pr+0Ju/l2ZvXMd9VQxtaoSZbs0BBp1jbBDqwms88ctpyvQFRfLSfSkqudQcSHyw2ROOz2E31p/7I7fpI8Y0CLA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.11.7.tgz",
|
||||
"integrity": "sha512-/06zXV4HIjYoiaUq1fVJo/RcU8pHbzx21evOpeG/foCfNpMI4xLU/vnxdUi6/SQqpZMY0eFutDqod1InkSOqsg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2818,9 +2818,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-strike": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.11.5.tgz",
|
||||
"integrity": "sha512-PVfUiCqrjvsLpbIoVlegSY8RlkR64F1Rr2RYmiybQfGbg+AkSZXDeO0eIrc03//4gua7D9DfIozHmAKv1KN3ow==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.11.7.tgz",
|
||||
"integrity": "sha512-D6GYiW9F24bvAY7XMOARNZbC8YGPzdzWdXd8VOOJABhf4ynMi/oW4NNiko+kZ67jn3EGaKoz32VMJzNQgYi1HA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2831,9 +2831,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-subscript": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.11.5.tgz",
|
||||
"integrity": "sha512-VpaSzxku/Bcvf4SgDB2K5d0E+FNA/56iJHMygg/WXsq2F4tMMUEivQHI/n+17ndUEO4Wybz0wItnM1G2JfRuLQ==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.11.7.tgz",
|
||||
"integrity": "sha512-I25ZexCddFJ9701DCCtQbX3Vtxzj5d9ss2GAXVweIUCdATCScaebsznyUQoN5papmhTxXsw5OD+K2ZHxP82pew==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2844,9 +2844,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-superscript": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.11.5.tgz",
|
||||
"integrity": "sha512-sK6v2G0zFfGW+j9CmYp2e+tyZ3FTa3dP0xY4kJzefgZcHhMJLlLnjxBRwHCSi/jj5ie6WdZT4KoEooxnPs1Vzw==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.11.7.tgz",
|
||||
"integrity": "sha512-dNRpCcRJs0Qvv0sZRgbH7Y5hDVbWsGSZjtwFCs/mysPrvHqmXjzo7568kYWTggxEYxnXw6n0FfkCAEHlt0N90Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2857,9 +2857,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-table": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.11.5.tgz",
|
||||
"integrity": "sha512-NKXLhKWdAdURklm98YkCd2ai4fh8jY8HS/+X2s/2QiQt8Z98CU1keCm35fJEEExM234iB/hCqG5vY4JgTc0Tvw==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.11.7.tgz",
|
||||
"integrity": "sha512-rfwWkNXz/EZuhc8lylsCWPbx0Xr5FlIhreWFyeoXYrDEO3x4ytYcVOpNmbabJYP2semfM0PvPR5o84zfFkLZyg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2871,9 +2871,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-table-cell": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.11.5.tgz",
|
||||
"integrity": "sha512-S967Au0pgeULstP3FaasOf/LEh72p61Ooh1PcUMF/az4x8EeGgpcEUARpVUxsGxLFvogv6LmhPHZdtcGgdHcBw==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.11.7.tgz",
|
||||
"integrity": "sha512-JMOkSYRckc5SJP86yGGiHzCxCR8ecrRENvTWAKib6qer2tutxs5u42W+Z8uTcHC2dRz7Fv54snOkDoqPwkf6cw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2884,9 +2884,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-table-header": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.11.5.tgz",
|
||||
"integrity": "sha512-O1iBtzZP1XZDi4h1Xmgq1T63il+fpKPvBIMZ0JJH9TyCw5i5rcrMLL2dyy5zaWK3BFRJuYBNSke4c+VWnr/g6w==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.11.7.tgz",
|
||||
"integrity": "sha512-wPRKpliS5QQXgsp//ZjXrHMdLICMkjg2fUrQinOiBa7wDL5C7Y+SehtuK4s2tjeAkyAdj+nepfftyBRIlUSMXg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2897,9 +2897,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-table-row": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.11.5.tgz",
|
||||
"integrity": "sha512-+/VWhCuW24BcM5aaIc/f0bC6ZR1Q5gnuqw13MIo7gyPx7iIY6BXK8roGiZSs8wYAN4uBEf3EKFm0bSZwQuAeyg==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.11.7.tgz",
|
||||
"integrity": "sha512-K254RiXWGXGjz5Cm835hqfQiwnYXm8aw6oOa3isDh4A1B+1Ev4DB2vEDKMrgaOor3nbTsSYmAx2iEMrZSbpaRg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2910,9 +2910,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.11.5.tgz",
|
||||
"integrity": "sha512-Gq1WwyhFpCbEDrLPIHt5A8aLSlf8bfz4jm417c8F/JyU0J5dtYdmx0RAxjnLw1i7ZHE7LRyqqAoS0sl7JHDNSQ==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.11.7.tgz",
|
||||
"integrity": "sha512-wObCn8qZkIFnXTLvBP+X8KgaEvTap/FJ/i4hBMfHBCKPGDx99KiJU6VIbDXG8d5ZcFZE0tOetK1pP5oI7qgMlQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2923,9 +2923,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text-align": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.11.5.tgz",
|
||||
"integrity": "sha512-Ei0zDpH5N9EV59ogydK4HTKa4lCPicCsQllM5n/Nf2tUJPir3aiYxzJ73FzhComD4Hpo1ANYnmssBhy8QeoPZA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.11.7.tgz",
|
||||
"integrity": "sha512-3M8zd9ROADXazVNpgR6Ejs1evSvBveN36qN4GgV71GqrNlTcjqYgQcXFLQrsd2hnE+aXir8/8bLJ+aaJXDninA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2936,9 +2936,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text-style": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.11.5.tgz",
|
||||
"integrity": "sha512-YUmYl0gILSd/u/ZkOmNxjNXVw+mu8fpC2f8G4I4tLODm0zCx09j9DDEJXSrM5XX72nxJQqtSQsCpNKnL0hfeEQ==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.11.7.tgz",
|
||||
"integrity": "sha512-LHO6DBg/9SkCQFdWlVfw9nolUmw+Cid94WkTY+7IwrpyG2+ZGQxnKpCJCKyeaFNbDoYAtvu0vuTsSXeCkgShcA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2949,9 +2949,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-underline": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.11.5.tgz",
|
||||
"integrity": "sha512-YpWHXNIkSoRSuzT2cvgKpyJ2tTz3LzqkTM64uC+uTJ8cUkvXIWUWejJR42q8ma/mTlQe4lHff4IQ0Sf58Digtw==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.11.7.tgz",
|
||||
"integrity": "sha512-NtoQw6PGijOAtXC6G+0Aq0/Z5wwEjPhNHs8nsjXogfWIgaj/aI4/zfBnA06eI3WT+emMYQTl0fTc4CUPnLVU8g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2962,9 +2962,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/pm": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.11.5.tgz",
|
||||
"integrity": "sha512-z9JFtqc5ZOsdQLd9vRnXfTCQ8v5ADAfRt9Nm7SqP6FUHII8E1hs38ACzf5xursmth/VonJYb5+73Pqxk1hGIPw==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.11.7.tgz",
|
||||
"integrity": "sha512-7gEEfz2Q6bYKXM07vzLUD0vqXFhC5geWRA6LCozTiLdVFDdHWiBrvb2rtkL5T7mfLq03zc1QhH7rI3F6VntOEA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-changeset": "^2.2.1",
|
||||
@@ -2981,7 +2981,7 @@
|
||||
"prosemirror-schema-basic": "^1.2.3",
|
||||
"prosemirror-schema-list": "^1.4.1",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-tables": "^1.6.3",
|
||||
"prosemirror-tables": "^1.6.4",
|
||||
"prosemirror-trailing-node": "^3.0.0",
|
||||
"prosemirror-transform": "^1.10.2",
|
||||
"prosemirror-view": "^1.37.0"
|
||||
@@ -2992,32 +2992,32 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/starter-kit": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.11.5.tgz",
|
||||
"integrity": "sha512-SLI7Aj2ruU1t//6Mk8f+fqW+18uTqpdfLUJYgwu0CkqBckrkRZYZh6GVLk/02k3H2ki7QkFxiFbZrdbZdng0JA==",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.11.7.tgz",
|
||||
"integrity": "sha512-K+q51KwNU/l0kqRuV5e1824yOLVftj6kGplGQLvJG56P7Rb2dPbM/JeaDbxQhnHT/KDGamG0s0Po0M3pPY163A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^2.11.5",
|
||||
"@tiptap/extension-blockquote": "^2.11.5",
|
||||
"@tiptap/extension-bold": "^2.11.5",
|
||||
"@tiptap/extension-bullet-list": "^2.11.5",
|
||||
"@tiptap/extension-code": "^2.11.5",
|
||||
"@tiptap/extension-code-block": "^2.11.5",
|
||||
"@tiptap/extension-document": "^2.11.5",
|
||||
"@tiptap/extension-dropcursor": "^2.11.5",
|
||||
"@tiptap/extension-gapcursor": "^2.11.5",
|
||||
"@tiptap/extension-hard-break": "^2.11.5",
|
||||
"@tiptap/extension-heading": "^2.11.5",
|
||||
"@tiptap/extension-history": "^2.11.5",
|
||||
"@tiptap/extension-horizontal-rule": "^2.11.5",
|
||||
"@tiptap/extension-italic": "^2.11.5",
|
||||
"@tiptap/extension-list-item": "^2.11.5",
|
||||
"@tiptap/extension-ordered-list": "^2.11.5",
|
||||
"@tiptap/extension-paragraph": "^2.11.5",
|
||||
"@tiptap/extension-strike": "^2.11.5",
|
||||
"@tiptap/extension-text": "^2.11.5",
|
||||
"@tiptap/extension-text-style": "^2.11.5",
|
||||
"@tiptap/pm": "^2.11.5"
|
||||
"@tiptap/core": "^2.11.7",
|
||||
"@tiptap/extension-blockquote": "^2.11.7",
|
||||
"@tiptap/extension-bold": "^2.11.7",
|
||||
"@tiptap/extension-bullet-list": "^2.11.7",
|
||||
"@tiptap/extension-code": "^2.11.7",
|
||||
"@tiptap/extension-code-block": "^2.11.7",
|
||||
"@tiptap/extension-document": "^2.11.7",
|
||||
"@tiptap/extension-dropcursor": "^2.11.7",
|
||||
"@tiptap/extension-gapcursor": "^2.11.7",
|
||||
"@tiptap/extension-hard-break": "^2.11.7",
|
||||
"@tiptap/extension-heading": "^2.11.7",
|
||||
"@tiptap/extension-history": "^2.11.7",
|
||||
"@tiptap/extension-horizontal-rule": "^2.11.7",
|
||||
"@tiptap/extension-italic": "^2.11.7",
|
||||
"@tiptap/extension-list-item": "^2.11.7",
|
||||
"@tiptap/extension-ordered-list": "^2.11.7",
|
||||
"@tiptap/extension-paragraph": "^2.11.7",
|
||||
"@tiptap/extension-strike": "^2.11.7",
|
||||
"@tiptap/extension-text": "^2.11.7",
|
||||
"@tiptap/extension-text-style": "^2.11.7",
|
||||
"@tiptap/pm": "^2.11.7"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"license": "MIT",
|
||||
"version": "15.4.0-rc2",
|
||||
"version": "15.5.0-rc",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": null,
|
||||
@@ -202,21 +202,21 @@
|
||||
"npm": ">=10.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tiptap/core": "2.11.5",
|
||||
"@tiptap/extension-character-count": "^2.11.5",
|
||||
"@tiptap/extension-image": "2.11.5",
|
||||
"@tiptap/extension-link": "2.11.5",
|
||||
"@tiptap/extension-placeholder": "2.11.5",
|
||||
"@tiptap/extension-subscript": "2.11.5",
|
||||
"@tiptap/extension-superscript": "2.11.5",
|
||||
"@tiptap/extension-table": "2.11.5",
|
||||
"@tiptap/extension-table-cell": "2.11.5",
|
||||
"@tiptap/extension-table-header": "2.11.5",
|
||||
"@tiptap/extension-table-row": "2.11.5",
|
||||
"@tiptap/extension-text-align": "2.11.5",
|
||||
"@tiptap/extension-underline": "2.11.5",
|
||||
"@tiptap/pm": "2.11.5",
|
||||
"@tiptap/starter-kit": "2.11.5",
|
||||
"@tiptap/core": "2.11.7",
|
||||
"@tiptap/extension-character-count": "2.11.7",
|
||||
"@tiptap/extension-image": "2.11.7",
|
||||
"@tiptap/extension-link": "2.11.7",
|
||||
"@tiptap/extension-placeholder": "2.11.7",
|
||||
"@tiptap/extension-subscript": "2.11.7",
|
||||
"@tiptap/extension-superscript": "2.11.7",
|
||||
"@tiptap/extension-table": "2.11.7",
|
||||
"@tiptap/extension-table-cell": "2.11.7",
|
||||
"@tiptap/extension-table-header": "2.11.7",
|
||||
"@tiptap/extension-table-row": "2.11.7",
|
||||
"@tiptap/extension-text-align": "2.11.7",
|
||||
"@tiptap/extension-underline": "2.11.7",
|
||||
"@tiptap/pm": "2.11.7",
|
||||
"@tiptap/starter-kit": "2.11.7",
|
||||
"@types/diff": "^7.0.1",
|
||||
"@umbraco-ui/uui": "^1.13.0",
|
||||
"@umbraco-ui/uui-css": "^1.13.0",
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
import type { ManifestElement } from '@umbraco-cms/backoffice/extension-api';
|
||||
import type { ManifestElement, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
/**
|
||||
* Header apps are displayed in the top right corner of the backoffice
|
||||
* The two provided header apps are the search and the user menu
|
||||
*/
|
||||
export interface ManifestHeaderApp extends ManifestElement {
|
||||
export interface ManifestHeaderApp extends ManifestElement, ManifestWithDynamicConditions<UmbExtensionConditionConfig> {
|
||||
type: 'headerApp';
|
||||
//meta: MetaHeaderApp;
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ export class UmbDropzoneMediaElement extends UmbInputDropzoneElement {
|
||||
z-index: 100;
|
||||
border-radius: var(--uui-border-radius);
|
||||
border: 1px solid var(--uui-color-focus);
|
||||
box-sizing:border-box;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
+28
-3
@@ -1,6 +1,15 @@
|
||||
import type { UmbTiptapExtensionApi } from '../../extensions/types.js';
|
||||
import type { UmbTiptapStatusbarValue, UmbTiptapToolbarValue } from '../types.js';
|
||||
import { css, customElement, html, map, property, state, unsafeCSS, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import {
|
||||
css,
|
||||
customElement,
|
||||
html,
|
||||
property,
|
||||
repeat,
|
||||
state,
|
||||
unsafeCSS,
|
||||
when,
|
||||
} from '@umbraco-cms/backoffice/external/lit';
|
||||
import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { Editor } from '@umbraco-cms/backoffice/external/tiptap';
|
||||
@@ -16,6 +25,12 @@ import './tiptap-statusbar.element.js';
|
||||
|
||||
const TIPTAP_CORE_EXTENSION_ALIAS = 'Umb.Tiptap.RichTextEssentials';
|
||||
|
||||
/**
|
||||
* The root path for the stylesheets on the server.
|
||||
* This is used to load the stylesheets from the server as a workaround until the server supports virtual paths.
|
||||
*/
|
||||
const STYLESHEET_ROOT_PATH = '/css';
|
||||
|
||||
@customElement('umb-input-tiptap')
|
||||
export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof UmbLitElement, string>(UmbLitElement) {
|
||||
#stylesheets = new Set(['/umbraco/backoffice/css/rte-content.css']);
|
||||
@@ -129,7 +144,13 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
|
||||
|
||||
const stylesheets = this.configuration?.getValueByAlias<Array<string>>('stylesheets');
|
||||
if (stylesheets?.length) {
|
||||
stylesheets.forEach((x) => this.#stylesheets.add(x));
|
||||
stylesheets.forEach((stylesheet) => {
|
||||
const linkHref =
|
||||
stylesheet.startsWith('http') || stylesheet.startsWith(STYLESHEET_ROOT_PATH)
|
||||
? stylesheet
|
||||
: `${STYLESHEET_ROOT_PATH}${stylesheet}`;
|
||||
this.#stylesheets.add(linkHref);
|
||||
});
|
||||
}
|
||||
|
||||
this._toolbar = this.configuration?.getValueByAlias<UmbTiptapToolbarValue>('toolbar') ?? [[[]]];
|
||||
@@ -182,7 +203,11 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
|
||||
#renderStyles() {
|
||||
if (!this._styles?.length) return;
|
||||
return html`
|
||||
${map(this.#stylesheets, (stylesheet) => html`<link rel="stylesheet" href=${stylesheet} />`)}
|
||||
${repeat(
|
||||
this.#stylesheets,
|
||||
(stylesheet) => stylesheet,
|
||||
(stylesheet) => html`<link rel="stylesheet" href="${stylesheet}" />`,
|
||||
)}
|
||||
<style>
|
||||
${this._styles.map((style) => unsafeCSS(style))}
|
||||
</style>
|
||||
|
||||
+9
-10
@@ -7,8 +7,8 @@
|
||||
"name": "acceptancetest",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "^2.0.31",
|
||||
"@umbraco/playwright-testhelpers": "^15.0.44",
|
||||
"@umbraco/json-models-builders": "^2.0.33",
|
||||
"@umbraco/playwright-testhelpers": "^15.0.56",
|
||||
"camelize": "^1.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"node-fetch": "^2.6.7"
|
||||
@@ -58,21 +58,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@umbraco/json-models-builders": {
|
||||
"version": "2.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.32.tgz",
|
||||
"integrity": "sha512-Aw7yBu8ePNxdjS7Q61j5KPFsiOS+IGCYxBX0H4KWbjXTdvL/PsB98KiqbDHHKFnp0fF1b2ffwJAI6jmvnxPBzg==",
|
||||
"license": "MIT",
|
||||
"version": "2.0.33",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.33.tgz",
|
||||
"integrity": "sha512-FAQxQIHoY6PGxWuodp4LSQxNufnOiqnaRNmtG8Ejn01r9lJJdA27CJKJ0bCs8U0W3cdN+Z+j7jBLd8H025/THw==",
|
||||
"dependencies": {
|
||||
"camelize": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@umbraco/playwright-testhelpers": {
|
||||
"version": "15.0.44",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-15.0.44.tgz",
|
||||
"integrity": "sha512-rbStBJG0bdVfs9pzEjAhArhcPoQbuvrUpvDJLbEMBcg54lRFH0I5nzcAimdZaY4UWh2r0i9ANfjv9E25NJxRUg==",
|
||||
"version": "15.0.56",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-15.0.56.tgz",
|
||||
"integrity": "sha512-QPLNLnS96QvkCPpumpOwn9ufgIWgsv4W0UQENTU63pMniHPYfaflFMafI4qjM/euwMyXHH7k9+c4k/tGew2EQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "2.0.32",
|
||||
"@umbraco/json-models-builders": "2.0.33",
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
"typescript": "^4.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "^2.0.31",
|
||||
"@umbraco/playwright-testhelpers": "^15.0.44",
|
||||
"@umbraco/json-models-builders": "^2.0.33",
|
||||
"@umbraco/playwright-testhelpers": "^15.0.56",
|
||||
"camelize": "^1.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"node-fetch": "^2.6.7"
|
||||
|
||||
@@ -43,14 +43,43 @@ 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'],
|
||||
// Use prepared auth state.
|
||||
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']
|
||||
}
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import {test} from "@umbraco/playwright-testhelpers";
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('DataType tests', () => {
|
||||
let dataTypeId = '';
|
||||
let dataTypeFolderId = '';
|
||||
let copiedDataTypeId = '';
|
||||
|
||||
const dataTypeName = 'TestDataType';
|
||||
const folderName = 'TestDataTypeFolder';
|
||||
const editorAlias = 'Umbraco.DateTime';
|
||||
const editorUiAlias = 'Umb.PropertyEditorUi.DatePicker';
|
||||
const dataTypeData = [
|
||||
{
|
||||
"alias": "tester",
|
||||
"value": "Howdy"
|
||||
}
|
||||
];
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.dataType.ensureNameNotExists(dataTypeName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.dataType.ensureNameNotExists(dataTypeName);
|
||||
if (dataTypeFolderId != '') {
|
||||
await umbracoApi.dataType.ensureNameNotExists(folderName);
|
||||
}
|
||||
});
|
||||
|
||||
test('can create dataType', async ({umbracoApi}) => {
|
||||
// Act
|
||||
dataTypeId = await umbracoApi.dataType.create(dataTypeName, editorAlias, editorUiAlias, dataTypeData);
|
||||
|
||||
// Assert
|
||||
expect(umbracoApi.dataType.doesExist(dataTypeId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update dataType', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
dataTypeId = await umbracoApi.dataType.create(dataTypeName, editorAlias, editorUiAlias, []);
|
||||
const dataType = await umbracoApi.dataType.get(dataTypeId);
|
||||
dataType.values = dataTypeData;
|
||||
|
||||
// Act
|
||||
await umbracoApi.dataType.update(dataTypeId, dataType);
|
||||
|
||||
// Assert
|
||||
// Checks if the data type was updated
|
||||
const updatedDataType = await umbracoApi.dataType.get(dataTypeId);
|
||||
expect(updatedDataType.values).toEqual(dataTypeData);
|
||||
});
|
||||
|
||||
test('can delete dataType', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
dataTypeId = await umbracoApi.dataType.create(dataTypeName, editorAlias, editorUiAlias, dataTypeData);
|
||||
expect(await umbracoApi.dataType.doesExist(dataTypeId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.dataType.delete(dataTypeId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.dataType.doesExist(dataTypeId)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can move a dataType to a folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.dataType.ensureNameNotExists(folderName);
|
||||
dataTypeId = await umbracoApi.dataType.create(dataTypeName, editorAlias, editorUiAlias, dataTypeData);
|
||||
expect(await umbracoApi.dataType.doesNameExist(dataTypeName)).toBeTruthy();
|
||||
dataTypeFolderId = await umbracoApi.dataType.createFolder(folderName);
|
||||
expect(await umbracoApi.dataType.doesFolderExist(dataTypeFolderId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.dataType.moveToFolder(dataTypeId, dataTypeFolderId);
|
||||
|
||||
// Assert
|
||||
// Checks if the datatype has the parentId of the folder
|
||||
const dataTypeInFolder = await umbracoApi.dataType.getChildren(dataTypeFolderId);
|
||||
expect(dataTypeInFolder[0].parent.id).toEqual(dataTypeFolderId);
|
||||
});
|
||||
|
||||
test('can copy a dataType to a folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.dataType.ensureNameNotExists(folderName);
|
||||
dataTypeId = await umbracoApi.dataType.create(dataTypeName, editorAlias, editorUiAlias, dataTypeData);
|
||||
dataTypeFolderId = await umbracoApi.dataType.createFolder(folderName);
|
||||
|
||||
const dataType = await umbracoApi.dataType.get(dataTypeId);
|
||||
const dataTypeFolder = await umbracoApi.dataType.getFolder(dataTypeFolderId);
|
||||
|
||||
// Act
|
||||
copiedDataTypeId = await umbracoApi.dataType.copyToFolder(dataType.id, dataTypeFolder.id);
|
||||
|
||||
// Assert
|
||||
const copiedDataType = await umbracoApi.dataType.get(copiedDataTypeId);
|
||||
expect(copiedDataType.name).toEqual(dataTypeName + ' (copy)');
|
||||
// Checks if both dataTypes exists
|
||||
expect(await umbracoApi.dataType.doesExist(dataTypeId)).toBeTruthy();
|
||||
expect(await umbracoApi.dataType.doesExist(copiedDataTypeId)).toBeTruthy();
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.delete(copiedDataTypeId);
|
||||
});
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
import {test} from "@umbraco/playwright-testhelpers";
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('DataTypeFolder tests', () => {
|
||||
let dataTypeFolderId = '';
|
||||
const dataTypeFolderName = 'TestDataTypeFolder';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.dataType.ensureNameNotExists(dataTypeFolderName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.dataType.ensureNameNotExists(dataTypeFolderName);
|
||||
});
|
||||
|
||||
test('can create a dataType folder', async ({umbracoApi}) => {
|
||||
// Act
|
||||
dataTypeFolderId = await umbracoApi.dataType.createFolder(dataTypeFolderName);
|
||||
|
||||
// Assert
|
||||
expect(umbracoApi.dataType.doesFolderExist(dataTypeFolderId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can rename a dataType folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const wrongDataTypeFolderName = 'WrongFolderName';
|
||||
dataTypeFolderId = await umbracoApi.dataType.createFolder(wrongDataTypeFolderName);
|
||||
|
||||
// Act
|
||||
await umbracoApi.dataType.renameFolder(dataTypeFolderId, dataTypeFolderName);
|
||||
|
||||
// Assert
|
||||
expect(umbracoApi.dataType.doesFolderExist(dataTypeFolderId)).toBeTruthy();
|
||||
// Checks if the dataType folder was updated
|
||||
const newDataTypeFolderName = await umbracoApi.dataType.getFolder(dataTypeFolderId);
|
||||
expect(newDataTypeFolderName.name).toEqual(dataTypeFolderName);
|
||||
});
|
||||
|
||||
test('can delete a dataType folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.dataType.createFolder(dataTypeFolderName);
|
||||
expect(umbracoApi.dataType.doesFolderExist(dataTypeFolderId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.dataType.delete(dataTypeFolderId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.dataType.doesFolderExist(dataTypeFolderId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,106 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
test.describe('Dictionary tests', () => {
|
||||
let dictionaryId = '';
|
||||
const dictionaryName = 'TestDictionary';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.dictionary.ensureNameNotExists(dictionaryName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.dictionary.ensureNameNotExists(dictionaryName);
|
||||
});
|
||||
|
||||
test('can create a dictionary', async ({umbracoApi}) => {
|
||||
// Act
|
||||
dictionaryId = await umbracoApi.dictionary.createDefaultDictionary(dictionaryName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.dictionary.doesExist(dictionaryId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update a dictionary', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const wrongDictionaryName = 'WrongTestDictionary';
|
||||
dictionaryId = await umbracoApi.dictionary.createDefaultDictionary(wrongDictionaryName);
|
||||
expect(await umbracoApi.dictionary.doesNameExist(wrongDictionaryName)).toBeTruthy();
|
||||
const dictionary = await umbracoApi.dictionary.get(dictionaryId);
|
||||
|
||||
// Act
|
||||
dictionary.name = dictionaryName;
|
||||
await umbracoApi.dictionary.update(dictionaryId, dictionary);
|
||||
|
||||
// Assert
|
||||
// Checks if the dictionary was updated
|
||||
const newDictionary = await umbracoApi.dictionary.get(dictionaryId);
|
||||
expect(newDictionary.name).toEqual(dictionaryName);
|
||||
expect(await umbracoApi.dictionary.doesExist(dictionaryId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can delete a dictionary', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
dictionaryId = await umbracoApi.dictionary.create(dictionaryName);
|
||||
expect(await umbracoApi.dictionary.doesExist(dictionaryId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.dictionary.delete(dictionaryId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.dictionary.doesExist(dictionaryId)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can create a dictionary item in a dictionary', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const childDictionaryName = 'ChildDictionary';
|
||||
await umbracoApi.dictionary.ensureNameNotExists(dictionaryName);
|
||||
dictionaryId = await umbracoApi.dictionary.create(dictionaryName);
|
||||
|
||||
// Act
|
||||
await umbracoApi.dictionary.create(childDictionaryName, [], dictionaryId);
|
||||
|
||||
// Assert
|
||||
// Checks if the parent dictionary contains the child dictionary
|
||||
const dictionaryChildren = await umbracoApi.dictionary.getChildren(dictionaryId);
|
||||
expect(dictionaryChildren[0].name).toEqual(childDictionaryName);
|
||||
});
|
||||
|
||||
test('can export a dictionary', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
dictionaryId = await umbracoApi.dictionary.create(dictionaryName);
|
||||
expect(await umbracoApi.dictionary.doesExist(dictionaryId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
const exportResponse = await umbracoApi.dictionary.export(dictionaryId, false);
|
||||
|
||||
// Assert
|
||||
// Checks if the .udt file is exported
|
||||
expect(exportResponse.headers()['content-disposition']).toContain(".udt");
|
||||
});
|
||||
|
||||
test('can import a dictionary', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const temporaryFileId = crypto.randomUUID();
|
||||
const fileName = 'TestSingleDictionary.udt';
|
||||
const mimeType = 'application/octet-stream';
|
||||
const filePath = './fixtures/dictionary/TestSingleDictionary.udt';
|
||||
// This variable must not be changed as it is declared in the file TestDictionary.udt
|
||||
const importDictionaryName = 'TestImportDictionary';
|
||||
|
||||
// Create temporary file
|
||||
await umbracoApi.temporaryFile.create(temporaryFileId, fileName, mimeType, filePath);
|
||||
expect(await umbracoApi.temporaryFile.doesExist(temporaryFileId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
const importResponse = await umbracoApi.dictionary.import(temporaryFileId, null);
|
||||
|
||||
// Assert
|
||||
expect(importResponse.status).toBeTruthy();
|
||||
expect(await umbracoApi.dictionary.doesNameExist(importDictionaryName)).toBeTruthy();
|
||||
|
||||
// Clean
|
||||
await umbracoApi.temporaryFile.delete(temporaryFileId);
|
||||
});
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Documents tests', () => {
|
||||
let documentTypeId = '';
|
||||
let documentId = '';
|
||||
const documentName = 'TestDocument';
|
||||
const documentTypeName = 'TestDocumentType';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
documentTypeId = await umbracoApi.documentType.createDefaultDocumentTypeWithAllowAsRoot(documentTypeName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(documentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
});
|
||||
|
||||
test('can create a document', async ({umbracoApi}) => {
|
||||
// Act
|
||||
await umbracoApi.document.createDefaultDocument(documentName, documentTypeId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.document.doesNameExist(documentName)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can delete a document', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
documentId = await umbracoApi.document.createDefaultDocument(documentName, documentTypeId);
|
||||
expect(umbracoApi.document.doesExist(documentId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.document.delete(documentId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.document.doesNameExist(documentName)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can update a document', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const wrongName = 'WrongDocument';
|
||||
documentId = await umbracoApi.document.createDefaultDocument(wrongName, documentTypeId);
|
||||
expect(await umbracoApi.document.doesNameExist(wrongName)).toBeTruthy();
|
||||
let documentData = await umbracoApi.document.get(documentId);
|
||||
documentData.variants[0].name = documentName;
|
||||
|
||||
// Act
|
||||
await umbracoApi.document.update(documentData.id, documentData);
|
||||
|
||||
// Assert
|
||||
const updatedDocumentData = await umbracoApi.document.get(documentId);
|
||||
expect(updatedDocumentData.variants[0].name).toEqual(documentName);
|
||||
});
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Language tests', () => {
|
||||
const languageNameDanish = 'Dansk';
|
||||
const isoCodeDanish = 'da-DK';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.language.ensureNameNotExists(languageNameDanish);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.language.ensureNameNotExists(languageNameDanish);
|
||||
});
|
||||
|
||||
test('can create a language', async ({umbracoApi}) => {
|
||||
// Act
|
||||
await umbracoApi.language.create(languageNameDanish, false, false, isoCodeDanish);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.language.doesExist(isoCodeDanish)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update a language', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const wrongLanguageName = 'densk';
|
||||
await umbracoApi.language.create(wrongLanguageName, false, false, isoCodeDanish);
|
||||
const language = await umbracoApi.language.get(isoCodeDanish);
|
||||
|
||||
// Act
|
||||
language.name = languageNameDanish;
|
||||
await umbracoApi.language.update(isoCodeDanish, language);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.language.doesExist(isoCodeDanish)).toBeTruthy();
|
||||
// Checks if the language name was updated
|
||||
const updatedLanguage = await umbracoApi.language.get(isoCodeDanish);
|
||||
expect(updatedLanguage.name).toEqual(languageNameDanish);
|
||||
});
|
||||
|
||||
test('can delete a language', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.language.create(languageNameDanish, false, false, isoCodeDanish);
|
||||
expect(await umbracoApi.language.doesExist(isoCodeDanish)).toBeTruthy();
|
||||
|
||||
//Act
|
||||
await umbracoApi.language.delete(isoCodeDanish);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.language.doesExist(isoCodeDanish)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Member tests', () => {
|
||||
let memberId = '';
|
||||
let memberTypeId = '';
|
||||
const memberName = 'TestMember';
|
||||
const memberTypeName = 'TestMemberType';
|
||||
const username = 'testmember';
|
||||
const email = 'testmember@acceptance.test';
|
||||
const password = '0123456789';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.memberType.ensureNameNotExists(memberTypeName);
|
||||
await umbracoApi.member.ensureNameNotExists(memberName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.memberType.ensureNameNotExists(memberTypeName);
|
||||
await umbracoApi.member.ensureNameNotExists(memberName);
|
||||
});
|
||||
|
||||
test('can create a member', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
memberTypeId = await umbracoApi.memberType.createDefaultMemberType(memberTypeName);
|
||||
|
||||
// Act
|
||||
memberId = await umbracoApi.member.createDefaultMember(memberName, memberTypeId, email, username, password);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.member.doesExist(memberId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can delete a member', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
memberTypeId = await umbracoApi.memberType.createDefaultMemberType(memberTypeName);
|
||||
memberId = await umbracoApi.member.createDefaultMember(memberName, memberTypeId, email, username, password);
|
||||
|
||||
// Act
|
||||
await umbracoApi.member.delete(memberId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.member.doesExist(memberId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Member Group tests', () => {
|
||||
let memberGroupId = '';
|
||||
const memberGroupName = 'TestMemberGroup';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.memberGroup.ensureNameNotExists(memberGroupName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.memberGroup.ensureNameNotExists(memberGroupName);
|
||||
});
|
||||
|
||||
test('can create a member group', async ({umbracoApi}) => {
|
||||
// Act
|
||||
memberGroupId = await umbracoApi.memberGroup.create(memberGroupName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.memberGroup.doesExist(memberGroupId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can rename a member group', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const wrongMemberGroupName = 'Wrong Name';
|
||||
await umbracoApi.memberGroup.ensureNameNotExists(wrongMemberGroupName);
|
||||
memberGroupId = await umbracoApi.memberGroup.create(wrongMemberGroupName);
|
||||
expect(await umbracoApi.memberGroup.doesExist(memberGroupId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.memberGroup.rename(memberGroupId, memberGroupName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.memberGroup.doesNameExist(memberGroupName)).toBeTruthy();
|
||||
expect(await umbracoApi.memberGroup.doesNameExist(wrongMemberGroupName)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can delete a member group', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
memberGroupId = await umbracoApi.memberGroup.create(memberGroupName);
|
||||
expect(await umbracoApi.memberGroup.doesExist(memberGroupId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.memberGroup.delete(memberGroupId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.memberGroup.doesExist(memberGroupId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from '@playwright/test';
|
||||
|
||||
test.describe('Package tests', () => {
|
||||
let packageId = '';
|
||||
const packageName = 'TestName';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.package.ensureNameNotExists(packageName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.package.delete(packageId);
|
||||
});
|
||||
|
||||
test('can create a package', async ({umbracoApi}) => {
|
||||
// Act
|
||||
packageId = await umbracoApi.package.create(packageName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.package.doesExist(packageId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update a package', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const wrongPackageName = 'WrongName';
|
||||
await umbracoApi.package.ensureNameNotExists(wrongPackageName);
|
||||
|
||||
// Act
|
||||
packageId = await umbracoApi.package.create(wrongPackageName);
|
||||
const packageData = await umbracoApi.package.get(packageId);
|
||||
packageData.name = packageName;
|
||||
await umbracoApi.package.update(packageId, packageData);
|
||||
|
||||
// Assert
|
||||
const updatedPackageData = await umbracoApi.package.get(packageId);
|
||||
expect(updatedPackageData.name).toEqual(packageName);
|
||||
});
|
||||
|
||||
test('can delete a package', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
packageId = await umbracoApi.package.create(packageName);
|
||||
|
||||
// Act
|
||||
await umbracoApi.package.delete(packageId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.package.doesExist(packageId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,61 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Partial View tests', () => {
|
||||
let partialViewPath = '';
|
||||
const partialViewName = 'partialViewName.cshtml';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.partialView.ensureNameNotExists(partialViewName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.partialView.ensureNameNotExists(partialViewName);
|
||||
});
|
||||
|
||||
test('can create a partial view', async ({umbracoApi}) => {
|
||||
// Act
|
||||
partialViewPath = await umbracoApi.partialView.create(partialViewName, 'test');
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.partialView.doesExist(partialViewPath)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update a partial view', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const newContent = 'Howdy';
|
||||
partialViewPath = await umbracoApi.partialView.create(partialViewName, 'test');
|
||||
|
||||
// Act
|
||||
await umbracoApi.partialView.updateContent(partialViewPath, newContent);
|
||||
|
||||
// Assert
|
||||
const updatedPartialView = await umbracoApi.partialView.get(partialViewPath);
|
||||
expect(updatedPartialView.content).toEqual(newContent);
|
||||
});
|
||||
|
||||
test('can rename a partial view', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const wrongPartialViewName = 'wrongPartialViewName.cshtml';
|
||||
const wrongPartialViewPath = await umbracoApi.partialView.create(wrongPartialViewName, 'test');
|
||||
|
||||
// Act
|
||||
const updatedPartialViewPath = await umbracoApi.partialView.updateName(wrongPartialViewPath, partialViewName);
|
||||
|
||||
// Assert
|
||||
const updatedPartialView = await umbracoApi.partialView.get(updatedPartialViewPath);
|
||||
expect(updatedPartialView.name).toEqual(partialViewName);
|
||||
});
|
||||
|
||||
test('can delete a partial view', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
partialViewPath = await umbracoApi.partialView.create(partialViewName, 'test');
|
||||
expect(await umbracoApi.partialView.doesExist(partialViewPath)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.partialView.delete(partialViewPath);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.partialView.doesExist(partialViewPath)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Partial View Folder tests', () => {
|
||||
let partialViewFolderPath = '';
|
||||
const partialViewFolderName = 'partialViewFolder';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.partialView.ensureNameNotExists(partialViewFolderName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.partialView.ensureNameNotExists(partialViewFolderName);
|
||||
});
|
||||
|
||||
test('can create a partial view folder', async ({umbracoApi}) => {
|
||||
// Act
|
||||
partialViewFolderPath = await umbracoApi.partialView.createFolder(partialViewFolderName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.partialView.doesFolderExist(partialViewFolderPath)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can delete a partial view folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
partialViewFolderPath = await umbracoApi.partialView.createFolder(partialViewFolderName);
|
||||
expect(await umbracoApi.partialView.doesFolderExist(partialViewFolderPath)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.partialView.deleteFolder(partialViewFolderPath);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.partialView.doesFolderExist(partialViewFolderPath)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can add a partial view folder in another', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const childFolderName = 'childFolder';
|
||||
partialViewFolderPath = await umbracoApi.partialView.createFolder(partialViewFolderName);
|
||||
|
||||
// Act
|
||||
await umbracoApi.partialView.createFolder(childFolderName, partialViewFolderPath);
|
||||
|
||||
// Assert
|
||||
const childrenData = await umbracoApi.partialView.getChildren(partialViewFolderPath);
|
||||
expect(childrenData[0].name).toEqual(childFolderName);
|
||||
});
|
||||
|
||||
test('can add a partial view folder in a folder in another folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const childFolderName = 'childFolder';
|
||||
const childOfChildFolderName = 'childOfChildFolder';
|
||||
|
||||
// Creates parent folder
|
||||
partialViewFolderPath = await umbracoApi.partialView.createFolder(partialViewFolderName);
|
||||
|
||||
// Creates child folder in parent folder
|
||||
const childFolderPath = await umbracoApi.partialView.createFolder(childFolderName, partialViewFolderPath);
|
||||
|
||||
// Act
|
||||
// Creates childOfChild folder in child folder
|
||||
await umbracoApi.partialView.createFolder(childOfChildFolderName, childFolderPath);
|
||||
|
||||
// Assert
|
||||
// Checks if the partial views folder are in the correct folders
|
||||
const childOfParentData = await umbracoApi.partialView.getChildren(partialViewFolderPath);
|
||||
expect(childOfParentData[0].name).toEqual(childFolderName);
|
||||
const childOfChildData = await umbracoApi.partialView.getChildren(childFolderPath);
|
||||
expect(childOfChildData[0].name).toEqual(childOfChildFolderName);
|
||||
});
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Relation type tests', () => {
|
||||
const relationTypeName = 'Test Relation Type';
|
||||
const objectTypeName = 'Document';
|
||||
let relationTypeId = '';
|
||||
let objectTypeId = '';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.relationType.ensureNameNotExists(relationTypeName);
|
||||
objectTypeId = await umbracoApi.objectTypes.getIdByName(objectTypeName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.relationType.ensureNameNotExists(relationTypeName);
|
||||
});
|
||||
|
||||
// Skip this test because there is currently no endpoint available for creating a relation type anymore.
|
||||
test.skip('can create a relation type', async ({umbracoApi}) => {
|
||||
// Act
|
||||
relationTypeId = await umbracoApi.relationType.create(relationTypeName, false, false, objectTypeId, objectTypeId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.relationType.doesExist(relationTypeId)).toBeTruthy();
|
||||
});
|
||||
|
||||
// Skip this test because there is currently no endpoint available for updating a relation type anymore.
|
||||
test.skip('can update a relation type', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const wrongRelationTypeName = 'Updated Relation Type';
|
||||
relationTypeId = await umbracoApi.relationType.create(wrongRelationTypeName, false, false, objectTypeId, objectTypeId);
|
||||
const relationTypeData = await umbracoApi.relationType.get(relationTypeId);
|
||||
|
||||
// Act
|
||||
relationTypeData.name = relationTypeName;
|
||||
await umbracoApi.relationType.update(relationTypeId, relationTypeData);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.relationType.doesExist(relationTypeId)).toBeTruthy();
|
||||
// Checks if the relation type name was updated
|
||||
const updatedRelationType = await umbracoApi.relationType.get(relationTypeId);
|
||||
expect(updatedRelationType.name).toEqual(relationTypeName);
|
||||
});
|
||||
|
||||
// Skip this test because there is currently no endpoint available for deleting a relation type anymore.
|
||||
test.skip('can delete a relation type', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
relationTypeId = await umbracoApi.relationType.create(relationTypeName, false, false, objectTypeId, objectTypeId);
|
||||
expect(await umbracoApi.relationType.doesExist(relationTypeId)).toBeTruthy();
|
||||
|
||||
//Act
|
||||
await umbracoApi.relationType.delete(relationTypeId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.relationType.doesExist(relationTypeId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,66 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Script tests', () => {
|
||||
let scriptPath = '';
|
||||
const scriptName = 'scriptName.js';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.script.ensureNameNotExists(scriptName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.script.ensureNameNotExists(scriptName);
|
||||
});
|
||||
|
||||
test('can create a script', async ({umbracoApi}) => {
|
||||
// Act
|
||||
scriptPath = await umbracoApi.script.create(scriptName, 'test');
|
||||
await umbracoApi.script.get(scriptPath);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.script.doesExist(scriptPath)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update script name', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const oldName = 'RandomScriptName.js';
|
||||
await umbracoApi.script.ensureNameNotExists(oldName);
|
||||
const oldScriptPath = await umbracoApi.script.create(oldName, 'test');
|
||||
|
||||
// Act
|
||||
scriptPath = await umbracoApi.script.updateName(oldScriptPath, scriptName);
|
||||
|
||||
// Assert
|
||||
// Checks if the content was updated for the script
|
||||
const updatedScript = await umbracoApi.script.get(scriptPath);
|
||||
expect(updatedScript.name).toEqual(scriptName);
|
||||
});
|
||||
|
||||
test('can update script content', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const newContent = 'BetterContent';
|
||||
scriptPath = await umbracoApi.script.create(scriptName, 'test');
|
||||
await umbracoApi.script.get(scriptPath);
|
||||
|
||||
// Act
|
||||
await umbracoApi.script.updateContent(scriptPath, newContent);
|
||||
|
||||
// Assert
|
||||
// Checks if the content was updated for the script
|
||||
const updatedScript = await umbracoApi.script.get(scriptPath);
|
||||
expect(updatedScript.content).toEqual(newContent);
|
||||
});
|
||||
|
||||
test('can delete a script', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
scriptPath = await umbracoApi.script.create(scriptName, 'test');
|
||||
expect(await umbracoApi.script.doesExist(scriptPath)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.script.delete(scriptPath);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.script.doesExist(scriptPath)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Script Folder tests', () => {
|
||||
const scriptFolderName = 'ScriptFolder';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.script.ensureNameNotExists(scriptFolderName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.script.ensureNameNotExists(scriptFolderName);
|
||||
});
|
||||
|
||||
test('can create a script folder', async ({umbracoApi}) => {
|
||||
// Act
|
||||
await umbracoApi.script.createFolder(scriptFolderName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.script.doesFolderExist(scriptFolderName)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can delete a script folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.script.createFolder(scriptFolderName);
|
||||
expect(await umbracoApi.script.doesFolderExist(scriptFolderName)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.script.deleteFolder(scriptFolderName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.script.doesFolderExist(scriptFolderName)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can add a script folder in another folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const childFolderName = 'childFolder';
|
||||
await umbracoApi.script.createFolder(scriptFolderName);
|
||||
await umbracoApi.script.createFolder(childFolderName, scriptFolderName);
|
||||
|
||||
// Act
|
||||
const childFolder = await umbracoApi.script.getChildren(scriptFolderName);
|
||||
|
||||
// Assert
|
||||
expect(childFolder[0].name).toEqual(childFolderName);
|
||||
});
|
||||
|
||||
test('can add a script folder in a folder in another folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const childFolderName = 'childFolder';
|
||||
const childOfChildFolderName = 'childOfChildFolder';
|
||||
const childFolderPath = scriptFolderName + '/' + childFolderName;
|
||||
|
||||
// Creates parent folder
|
||||
await umbracoApi.script.createFolder(scriptFolderName);
|
||||
// Creates child folder in parent folder
|
||||
await umbracoApi.script.createFolder(childFolderName, scriptFolderName);
|
||||
|
||||
// Act
|
||||
// Creates childOfChild folder in child folder
|
||||
await umbracoApi.script.createFolder(childOfChildFolderName, childFolderPath);
|
||||
|
||||
// Assert
|
||||
// Checks if the script folder are in the correct folders
|
||||
const childOfParent = await umbracoApi.script.getChildren(scriptFolderName);
|
||||
const childOfChild = await umbracoApi.script.getChildren(childFolderPath);
|
||||
expect(childOfParent[0].name).toEqual(childFolderName);
|
||||
expect(childOfChild[0].name).toEqual(childOfChildFolderName);
|
||||
});
|
||||
});
|
||||
@@ -1,98 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Stylesheet tests', () => {
|
||||
const stylesheetName = 'Stylesheet.css';
|
||||
const folderName = 'StyledFolder';
|
||||
let stylesheetPath = '';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.stylesheet.ensureNameNotExists(stylesheetName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.stylesheet.ensureNameNotExists(stylesheetName);
|
||||
await umbracoApi.stylesheet.ensureNameNotExists(folderName);
|
||||
});
|
||||
|
||||
test('can create a stylesheet', async ({umbracoApi}) => {
|
||||
// Act
|
||||
stylesheetPath = await umbracoApi.stylesheet.create(stylesheetName, 'content');
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.stylesheet.doesExist(stylesheetPath)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update stylesheet name', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const oldStylesheetName = 'RandomStylesheetName.css';
|
||||
await umbracoApi.script.ensureNameNotExists(oldStylesheetName);
|
||||
const oldScriptPath = await umbracoApi.stylesheet.create(oldStylesheetName, 'content');
|
||||
|
||||
// Act
|
||||
stylesheetPath = await umbracoApi.stylesheet.updateName(oldScriptPath, stylesheetName);
|
||||
|
||||
// Assert
|
||||
// Checks if the name was updated for the stylesheet
|
||||
const updatedStylesheet = await umbracoApi.stylesheet.get(stylesheetPath);
|
||||
expect(updatedStylesheet.name).toEqual(stylesheetName);
|
||||
});
|
||||
|
||||
test('can update stylesheet content', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const newContent = 'BetterContent';
|
||||
stylesheetPath = await umbracoApi.stylesheet.create(stylesheetName, 'content');
|
||||
|
||||
// Act
|
||||
await umbracoApi.stylesheet.updateContent(stylesheetPath, newContent);
|
||||
|
||||
// Assert
|
||||
// Checks if the content was updated for the stylesheet
|
||||
const updatedStylesheet = await umbracoApi.stylesheet.get(stylesheetPath);
|
||||
expect(updatedStylesheet.content).toEqual(newContent);
|
||||
});
|
||||
|
||||
test('can delete a stylesheet', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
stylesheetPath = await umbracoApi.stylesheet.create(stylesheetName, 'content');
|
||||
expect(await umbracoApi.stylesheet.doesExist(stylesheetPath)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.stylesheet.delete(stylesheetName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.stylesheet.doesExist(stylesheetPath)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can create a stylesheet in a folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.stylesheet.ensureNameNotExists(folderName);
|
||||
const stylesheetFolderPath = await umbracoApi.stylesheet.createFolder(folderName);
|
||||
|
||||
// Act
|
||||
stylesheetPath = await umbracoApi.stylesheet.create(stylesheetName, 'content', folderName);
|
||||
|
||||
// Assert
|
||||
const child = await umbracoApi.stylesheet.getChildren(stylesheetFolderPath);
|
||||
expect(child[0].name).toEqual(stylesheetName);
|
||||
});
|
||||
|
||||
test('can delete a stylesheet from a folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.stylesheet.ensureNameNotExists(folderName);
|
||||
const stylesheetFolderPath = await umbracoApi.stylesheet.createFolder(folderName);
|
||||
stylesheetPath = await umbracoApi.stylesheet.create(stylesheetName, 'deleteMe', folderName);
|
||||
// Checks if the stylesheet was created
|
||||
const child = await umbracoApi.stylesheet.getChildren(stylesheetFolderPath);
|
||||
// Checks if the child is in the folder
|
||||
expect(child[0].name).toEqual(stylesheetName);
|
||||
|
||||
// Act
|
||||
await umbracoApi.stylesheet.delete(stylesheetPath);
|
||||
|
||||
// Assert
|
||||
const noChild = await umbracoApi.stylesheet.getChildren(stylesheetFolderPath);
|
||||
// Checks if the children is empty
|
||||
expect(noChild[0]).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Stylesheet Folder tests', () => {
|
||||
const stylesheetFolderName = 'StylesheetFolder';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.stylesheet.ensureNameNotExists(stylesheetFolderName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.stylesheet.ensureNameNotExists(stylesheetFolderName);
|
||||
});
|
||||
|
||||
test('can create a stylesheet folder', async ({umbracoApi}) => {
|
||||
// Act
|
||||
await umbracoApi.stylesheet.createFolder(stylesheetFolderName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.stylesheet.doesFolderExist(stylesheetFolderName)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can delete a stylesheet folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.stylesheet.createFolder(stylesheetFolderName);
|
||||
expect(await umbracoApi.stylesheet.doesFolderExist(stylesheetFolderName)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.stylesheet.deleteFolder(stylesheetFolderName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.stylesheet.doesFolderExist(stylesheetFolderName)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can add a stylesheet folder in another folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const childFolderName = 'childFolder';
|
||||
await umbracoApi.stylesheet.createFolder(stylesheetFolderName);
|
||||
|
||||
// Act
|
||||
await umbracoApi.stylesheet.createFolder(childFolderName, stylesheetFolderName);
|
||||
|
||||
// Assert
|
||||
const children = await umbracoApi.stylesheet.getChildren(stylesheetFolderName);
|
||||
expect(children[0].name).toEqual(childFolderName);
|
||||
});
|
||||
|
||||
test('can add a stylesheet folder in a folder in another folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const childFolderName = 'childFolder';
|
||||
const childOfChildFolderName = 'childOfChildFolder';
|
||||
const childFolderPath = stylesheetFolderName + '/' + childFolderName;
|
||||
// Creates parent folder
|
||||
await umbracoApi.stylesheet.createFolder(stylesheetFolderName);
|
||||
// Creates child folder in parent folder
|
||||
await umbracoApi.stylesheet.createFolder(childFolderName, stylesheetFolderName);
|
||||
|
||||
// Act
|
||||
// Creates childOfChild folder in child folder
|
||||
await umbracoApi.stylesheet.createFolder(childOfChildFolderName, childFolderPath);
|
||||
|
||||
// Assert
|
||||
// Checks if the stylesheet folder are in the correct folders
|
||||
const childOfParent = await umbracoApi.stylesheet.getChildren(stylesheetFolderName);
|
||||
const childOfChild = await umbracoApi.stylesheet.getChildren(childFolderPath);
|
||||
expect(childOfParent[0].name).toEqual(childFolderName);
|
||||
expect(childOfChild[0].name).toEqual(childOfChildFolderName);
|
||||
});
|
||||
|
||||
test('can delete a stylesheet folder from another folder', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const childFolderName = 'childFolder';
|
||||
const stylesheetParentFolderPath = await umbracoApi.stylesheet.createFolder(stylesheetFolderName);
|
||||
const stylesheetChildFolderPath = await umbracoApi.stylesheet.createFolder(childFolderName, stylesheetFolderName);
|
||||
// Checks if a child exists in the parent folder with the name
|
||||
const child = await umbracoApi.stylesheet.getChildren(stylesheetParentFolderPath);
|
||||
expect(child[0].name).toEqual(childFolderName);
|
||||
expect(child[0].isFolder).toBe(true);
|
||||
expect(await umbracoApi.stylesheet.doesFolderExist(stylesheetChildFolderPath)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.stylesheet.deleteFolder(stylesheetChildFolderPath);
|
||||
|
||||
// Assert
|
||||
const noChild = await umbracoApi.stylesheet.getChildren(stylesheetParentFolderPath);
|
||||
expect(noChild[0]).toBe(undefined);
|
||||
});
|
||||
});
|
||||
@@ -1,54 +0,0 @@
|
||||
import {AliasHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('Template tests', () => {
|
||||
let templateId = '';
|
||||
const templateName = 'TemplateTester';
|
||||
const templateAlias = AliasHelper.toAlias(templateName);
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.template.ensureNameNotExists(templateName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.template.ensureNameNotExists(templateName);
|
||||
})
|
||||
|
||||
test('can create a template', async ({umbracoApi}) => {
|
||||
// Act
|
||||
templateId = await umbracoApi.template.create(templateName, templateAlias, 'Template Stuff');
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.template.doesExist(templateId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update a template', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const newTemplateAlias = 'betterAlias';
|
||||
templateId = await umbracoApi.template.create(templateName, templateAlias, 'Template Stuff');
|
||||
const templateData = await umbracoApi.template.get(templateId);
|
||||
|
||||
// Act
|
||||
// Updates the template
|
||||
templateData.alias = newTemplateAlias;
|
||||
await umbracoApi.template.update(templateId, templateData);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.template.doesExist(templateId)).toBeTruthy();
|
||||
// Checks if the template alias was updated
|
||||
const updatedTemplate = await umbracoApi.template.get(templateId);
|
||||
expect(updatedTemplate.alias).toEqual(newTemplateAlias);
|
||||
});
|
||||
|
||||
test('can delete template', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
templateId = await umbracoApi.template.create(templateName, templateAlias, 'More Template Stuff');
|
||||
expect(await umbracoApi.template.doesExist(templateId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.template.delete(templateId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.template.doesExist(templateId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
import {test} from "@umbraco/playwright-testhelpers";
|
||||
import {expect} from "@playwright/test";
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
test.describe('Temporary File tests', () => {
|
||||
const temporaryFileId = crypto.randomUUID();
|
||||
const fileName = 'Umbraco.png';
|
||||
const mimeType = 'image/png';
|
||||
const filePath = './fixtures/mediaLibrary/Umbraco.png';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.temporaryFile.delete(temporaryFileId);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.temporaryFile.delete(temporaryFileId);
|
||||
});
|
||||
|
||||
test('can create temporary file', async ({umbracoApi}) => {
|
||||
// Act
|
||||
await umbracoApi.temporaryFile.create(temporaryFileId, fileName, mimeType, filePath);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.temporaryFile.doesExist(temporaryFileId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can delete temporary file', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
await umbracoApi.temporaryFile.create(temporaryFileId, fileName, mimeType, filePath);
|
||||
expect(await umbracoApi.temporaryFile.get(temporaryFileId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.temporaryFile.delete(temporaryFileId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.temporaryFile.doesExist(temporaryFileId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,60 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('User Tests', () => {
|
||||
let userId = '';
|
||||
let userGroupId = '';
|
||||
const userEmail = 'user@acceptance.test';
|
||||
const userName = 'UserTests';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.user.ensureNameNotExists(userName);
|
||||
const userGroupData = await umbracoApi.userGroup.getByName('Writers');
|
||||
userGroupId = userGroupData.id;
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.user.ensureNameNotExists(userName);
|
||||
});
|
||||
|
||||
test('can create a user', async ({umbracoApi}) => {
|
||||
// Act
|
||||
userId = await umbracoApi.user.createDefaultUser(userName, userEmail, [userGroupId]);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.user.doesExist(userId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update a user', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
const anotherUserGroup = await umbracoApi.userGroup.getByName("Translators");
|
||||
userId = await umbracoApi.user.createDefaultUser(userName, userEmail, [userGroupId]);
|
||||
const userData = await umbracoApi.user.get(userId);
|
||||
const newUserGroupData = [
|
||||
userGroupId,
|
||||
anotherUserGroup.id
|
||||
];
|
||||
userData.userGroupIds = newUserGroupData;
|
||||
|
||||
// Act
|
||||
await umbracoApi.user.update(userId, userData);
|
||||
|
||||
// Assert
|
||||
await umbracoApi.user.doesExist(userId);
|
||||
// Checks if the user was updated with another userGroupID
|
||||
const updatedUser = await umbracoApi.user.get(userId);
|
||||
expect(updatedUser.userGroupIds.toString()).toEqual(newUserGroupData.toString());
|
||||
});
|
||||
|
||||
test('can delete a user', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
userId = await umbracoApi.user.createDefaultUser(userName, userEmail, [userGroupId]);
|
||||
expect(await umbracoApi.user.doesExist(userId)).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.user.delete(userId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.user.doesExist(userId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,61 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
test.describe('User Avatar Tests', () => {
|
||||
// User
|
||||
let userId = '';
|
||||
let userGroupId = '';
|
||||
const userEmail = 'userAvatar@acceptance.test';
|
||||
const userName = 'UserAvatarTests';
|
||||
// Avatar
|
||||
const avatarFileId = crypto.randomUUID();
|
||||
const avatarName = 'Umbraco.png';
|
||||
const mimeType = 'image/png';
|
||||
const avatarFilePath = './fixtures/mediaLibrary/Umbraco.png';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.user.ensureNameNotExists(userName);
|
||||
await umbracoApi.temporaryFile.delete(avatarFileId);
|
||||
const userGroupData = await umbracoApi.userGroup.getByName('Writers');
|
||||
userGroupId = userGroupData.id;
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.user.ensureNameNotExists(userName);
|
||||
await umbracoApi.temporaryFile.delete(avatarFileId);
|
||||
});
|
||||
|
||||
test('can add an avatar to a user', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
userId = await umbracoApi.user.createDefaultUser(userName, userEmail, userGroupId);
|
||||
await umbracoApi.temporaryFile.create(avatarFileId, avatarName, mimeType, avatarFilePath);
|
||||
|
||||
// Act
|
||||
await umbracoApi.user.addAvatar(userId, avatarFileId);
|
||||
|
||||
// Assert
|
||||
// Checks if the avatar was added to the user
|
||||
const userDataWithAvatar = await umbracoApi.user.get(userId);
|
||||
expect(userDataWithAvatar.avatarUrls.length !== 0).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can remove an avatar from a user', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
userId = await umbracoApi.user.createDefaultUser(userName, userEmail, userGroupId);
|
||||
await umbracoApi.temporaryFile.create(avatarFileId, avatarName, mimeType, avatarFilePath);
|
||||
await umbracoApi.user.addAvatar(userId, avatarFileId);
|
||||
|
||||
// Checks if the avatar was added to the user
|
||||
const userDataWithAvatar = await umbracoApi.user.get(userId);
|
||||
expect(userDataWithAvatar.avatarUrls.length !== 0).toBeTruthy();
|
||||
|
||||
// Act
|
||||
await umbracoApi.user.removeAvatar(userId);
|
||||
|
||||
// Assert
|
||||
// Checks if the avatar was removed from the user
|
||||
const userDataWithoutAvatar = await umbracoApi.user.get(userId);
|
||||
expect(userDataWithoutAvatar.avatarUrls.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
import {test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
test.describe('User Group Tests', () => {
|
||||
let userGroupId = '';
|
||||
const userGroupName = 'UserGroupTest';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.userGroup.ensureNameNotExists(userGroupName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.userGroup.delete(userGroupId);
|
||||
});
|
||||
|
||||
test('can create a user group', async ({umbracoApi}) => {
|
||||
// Act
|
||||
userGroupId = await umbracoApi.userGroup.createEmptyUserGroup(userGroupName);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.userGroup.doesExist(userGroupId)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can update a user group', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
userGroupId = await umbracoApi.userGroup.createEmptyUserGroup('WrongUserGroupName');
|
||||
const userGroupData = await umbracoApi.userGroup.get(userGroupId);
|
||||
userGroupData.name = userGroupName;
|
||||
|
||||
// Act
|
||||
await umbracoApi.userGroup.update(userGroupId, userGroupData);
|
||||
|
||||
// Assert
|
||||
const updatedUserGroupData = await umbracoApi.userGroup.get(userGroupId);
|
||||
expect(updatedUserGroupData.name).toEqual(userGroupName);
|
||||
});
|
||||
|
||||
test('can delete a user group', async ({umbracoApi}) => {
|
||||
// Arrange
|
||||
userGroupId = await umbracoApi.userGroup.createEmptyUserGroup(userGroupName);
|
||||
|
||||
// Act
|
||||
await umbracoApi.userGroup.delete(userGroupId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.userGroup.doesExist(userGroupId)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
import {expect} from '@playwright/test';
|
||||
import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
|
||||
// DocumentType
|
||||
const documentTypeName = 'TestDocumentType';
|
||||
let documentTypeId = '';
|
||||
const groupName = 'TestGroup';
|
||||
|
||||
// Content
|
||||
const contentName = 'TestContent';
|
||||
let contentId = '';
|
||||
|
||||
// Property Value
|
||||
const wrongPropertyValue = 'This is a test with wrong value**';
|
||||
const correctPropertyValue = 'Test';
|
||||
|
||||
// ElementTypes
|
||||
// TextString Element Type (for Block List)
|
||||
const textStringElementGroupName = 'TextStringElementGroup';
|
||||
const textStringElementTypeName = 'TestElementWithTextString';
|
||||
const textStringElementRegex = '^[a-zA-Z0-9]*$';
|
||||
let textStringElementTypeId = '';
|
||||
// Area Element Type (for Block Grid)
|
||||
const areaElementTypeName = 'TestElementArea';
|
||||
const areaAlias = 'testArea';
|
||||
let areaElementTypeId = '';
|
||||
// Rich Text Editor Element Type (for Block Grid)
|
||||
const richTextEditorElementGroupName = 'RichTextEditorElementGroup';
|
||||
const richTextEditorElementTypeName = 'RichTextEditorTestElement';
|
||||
let richTextEditorElementTypeId = '';
|
||||
// Block List Element Type
|
||||
const blockListElementTypeName = 'BlockListElement';
|
||||
const blockListGroupName = 'BlockListGroup';
|
||||
let blockListElementTypeId = '';
|
||||
|
||||
// DataTypes
|
||||
const blockGridDataTypeName = 'TestBlockGridEditor';
|
||||
const blockListDataTypeName = 'TestBlockListEditor';
|
||||
const textStringElementDataTypeName = 'Textstring';
|
||||
const richTextDataTypeName = 'Rich Text Editor';
|
||||
let blockListDataTypeId = '';
|
||||
let blockGridDataTypeId = '';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(areaElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(richTextEditorElementTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(areaElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(richTextEditorElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName);
|
||||
});
|
||||
|
||||
test('can update property value nested in a block grid area with an RTE with a block list editor', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => {
|
||||
test.slow();
|
||||
// Arrange
|
||||
// ElementType with Textstring And REGEX only accept letters and numbers
|
||||
const textStringElementDataType = await umbracoApi.dataType.getByName(textStringElementDataTypeName);
|
||||
textStringElementTypeId = await umbracoApi.documentType.createElementTypeWithRegexValidation(textStringElementTypeName, textStringElementGroupName, textStringElementDataTypeName, textStringElementDataType.id, textStringElementRegex);
|
||||
// Block List Editor with Textstring
|
||||
blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, textStringElementTypeId);
|
||||
// ElementType with Block List Editor
|
||||
blockListElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockListElementTypeName, blockListGroupName, blockListDataTypeName, blockListDataTypeId);
|
||||
// Rich Text Editor in an ElementType, with a Block(Element Type), the block contains a Block List Editor
|
||||
const richTextEditorId = await umbracoApi.dataType.createRichTextEditorWithABlock(richTextDataTypeName, blockListElementTypeId);
|
||||
richTextEditorElementTypeId = await umbracoApi.documentType.createDefaultElementType(richTextEditorElementTypeName, richTextEditorElementGroupName, richTextDataTypeName, richTextEditorId);
|
||||
// ElementType Area that is Empty
|
||||
areaElementTypeId = await umbracoApi.documentType.createEmptyElementType(areaElementTypeName);
|
||||
// Block Grid with 2 blocks, one with RTE and Inline, and one with areas
|
||||
blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockWithInlineEditingModeAndABlockWithAnArea(blockGridDataTypeName, richTextEditorElementTypeId, true, areaElementTypeId, areaAlias);
|
||||
// Document Type with the following
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridDataTypeName, blockGridDataTypeId, groupName);
|
||||
// Creates Content
|
||||
contentId = await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickAddBlockGridElementWithName(areaElementTypeName);
|
||||
await umbracoUi.content.clickSelectBlockElementWithName(areaElementTypeName);
|
||||
await umbracoUi.content.clickAddBlockGridElementWithName(richTextEditorElementTypeName);
|
||||
await umbracoUi.content.clickExactLinkWithName(richTextEditorElementTypeName);
|
||||
await umbracoUi.content.clickInsertBlockButton();
|
||||
await umbracoUi.content.clickExactLinkWithName(blockListElementTypeName);
|
||||
await umbracoUi.content.clickAddBlockGridElementWithName(textStringElementTypeName);
|
||||
await umbracoUi.content.clickExactLinkWithName(textStringElementTypeName);
|
||||
// Enter text in the textstring block that won't match regex
|
||||
await umbracoUi.content.enterPropertyValue(textStringElementDataTypeName, wrongPropertyValue);
|
||||
await umbracoUi.content.clickCreateButtonForModalWithElementTypeNameAndGroupName(textStringElementTypeName, textStringElementGroupName);
|
||||
await umbracoUi.content.clickCreateButtonForModalWithElementTypeNameAndGroupName(blockListElementTypeName, blockListGroupName);
|
||||
await umbracoUi.content.clickCreateButtonForModalWithElementTypeNameAndGroupName(richTextEditorElementTypeName, richTextEditorElementGroupName);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
// Checks that the error notification is shown since the textstring block has the wrong value
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved, true, true);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished, true, true);
|
||||
// Updates the textstring block with the correct value
|
||||
await umbracoUi.waitForTimeout(1000);
|
||||
await umbracoUi.content.clickBlockElementWithName(blockListElementTypeName);
|
||||
await umbracoUi.content.clickEditBlockListEntryWithName(textStringElementTypeName);
|
||||
await umbracoUi.content.enterPropertyValue(textStringElementDataTypeName, correctPropertyValue);
|
||||
await umbracoUi.content.clickUpdateButtonForModalWithElementTypeNameAndGroupName(textStringElementTypeName, textStringElementGroupName);
|
||||
await umbracoUi.content.clickUpdateButtonForModalWithElementTypeNameAndGroupName(blockListElementTypeName, blockListGroupName);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved, true, true);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published, true, true);
|
||||
// Checks if published
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe('Published');
|
||||
// Checks if the textstring block has the correct value after reloading the page
|
||||
await umbracoUi.reloadPage();
|
||||
// Waits to make sure the page has loaded
|
||||
await umbracoUi.waitForTimeout(2000);
|
||||
await umbracoUi.content.clickBlockElementWithName(blockListElementTypeName);
|
||||
// Needs to wait to make sure it has loaded
|
||||
await umbracoUi.waitForTimeout(2000);
|
||||
await umbracoUi.content.clickEditBlockListEntryWithName(textStringElementTypeName);
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringElementDataTypeName, correctPropertyValue);
|
||||
});
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
import {AliasHelper, ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
// Content Name
|
||||
const contentName = 'ContentName';
|
||||
|
||||
// Document Type
|
||||
const documentTypeName = 'DocumentTypeName';
|
||||
let documentTypeId = null;
|
||||
const documentTypeGroupName = 'DocumentGroup';
|
||||
|
||||
// Block Grid
|
||||
const blockGridDataTypeName = 'BlockGridName';
|
||||
let blockGridDataTypeId = null;
|
||||
|
||||
// Text String
|
||||
const textStringElementTypeName = 'TextStringElementName';
|
||||
let textStringElementTypeId = null;
|
||||
let textStringGroupName = 'TextGroup';
|
||||
const textStringDataTypeName = 'Textstring';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName);
|
||||
});
|
||||
|
||||
test('can publish a block grid editor with a rich text editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const richTextEditorValue = 'Hello World';
|
||||
const expectedRichTextEditorOutputValue = '<p>Hello World</p>';
|
||||
const richTextDataTypeName = 'RichTextDataTypeName';
|
||||
const richTextElementTypeName = 'RichTextElementName';
|
||||
const richTextElementGroupName = 'RichTextElementGroupName';
|
||||
await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(richTextElementTypeName);
|
||||
|
||||
const richTextEditorDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(richTextDataTypeName);
|
||||
const richTextElementTypeId = await umbracoApi.documentType.createDefaultElementType(richTextElementTypeName, richTextElementGroupName, richTextDataTypeName, richTextEditorDataTypeId);
|
||||
blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridDataTypeName, richTextElementTypeId, true);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridDataTypeName, blockGridDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(richTextElementTypeName, true);
|
||||
await umbracoUi.content.enterRTETipTapEditor(richTextEditorValue);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the RTE is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockGridDataTypeName));
|
||||
expect(documentValues.value.contentData[0].values[0].value.markup).toContain(expectedRichTextEditorOutputValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(richTextElementTypeName);
|
||||
});
|
||||
|
||||
test('can publish a block grid editor with a block list editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const textStringValue = 'Hello World';
|
||||
const blockListDataTypeName = 'BlockListName';
|
||||
const blockListElementTypeName = 'BlockListElementName';
|
||||
const blockListElementGroupName = 'BlockListElementGroupName';
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName);
|
||||
|
||||
const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName);
|
||||
textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id);
|
||||
const blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, textStringElementTypeId);
|
||||
const blockListElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockListElementTypeName, blockListElementGroupName, blockListDataTypeName, blockListDataTypeId);
|
||||
blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridDataTypeName, blockListElementTypeId, true);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridDataTypeName, blockGridDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(blockListElementTypeName, true);
|
||||
await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName);
|
||||
await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true);
|
||||
await umbracoUi.content.enterTextstring(textStringValue);
|
||||
await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the BlockList is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockGridDataTypeName));
|
||||
expect(documentValues.value.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName);
|
||||
});
|
||||
|
||||
test('can publish a block grid editor with a block grid editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const textStringValue = 'Hello World';
|
||||
const secondBlockGridDataTypeName = 'SecondBlockGridDataTypeName';
|
||||
const blockGridElementTypeName = 'BlockGridElementTypeName';
|
||||
const blockGridElementGroupName = 'BlockGridElementGroupName';
|
||||
await umbracoApi.dataType.ensureNameNotExists(secondBlockGridDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName);
|
||||
|
||||
const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName);
|
||||
textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id);
|
||||
const secondBlockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(secondBlockGridDataTypeName, textStringElementTypeId);
|
||||
const blockGridElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockGridElementTypeName, blockGridElementGroupName, secondBlockGridDataTypeName, secondBlockGridDataTypeId);
|
||||
blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridDataTypeName, blockGridElementTypeId, true);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridDataTypeName, blockGridDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(blockGridElementTypeName, true);
|
||||
await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName);
|
||||
await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true);
|
||||
await umbracoUi.content.enterTextstring(textStringValue);
|
||||
await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the BlockGrid is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockGridDataTypeName));
|
||||
expect(documentValues.value.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(secondBlockGridDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName);
|
||||
});
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
import {AliasHelper, ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
// Content Name
|
||||
const contentName = 'ContentName';
|
||||
|
||||
// Document Type
|
||||
const documentTypeName = 'DocumentTypeName';
|
||||
let documentTypeId = null;
|
||||
const documentTypeGroupName = 'DocumentGroup';
|
||||
|
||||
// Block List
|
||||
const blockListDataTypeName = 'BlockListName';
|
||||
let blockListDataTypeId = null;
|
||||
|
||||
// Text String
|
||||
const textStringElementTypeName = 'TextStringElementName';
|
||||
let textStringElementTypeId = null;
|
||||
let textStringGroupName = 'TextGroup';
|
||||
const textStringDataTypeName = 'Textstring';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName);
|
||||
});
|
||||
|
||||
test('can publish a block list editor with a rich text editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const richTextEditorValue = 'Hello World';
|
||||
const expectedRichTextEditorOutputValue = '<p>Hello World</p>';
|
||||
const richTextDataTypeName = 'RichTextName';
|
||||
const richTextElementTypeName = 'RichTextElementName';
|
||||
const richTextElementGroupName = 'RTEElementGroup';
|
||||
await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(richTextElementTypeName);
|
||||
|
||||
const richTextEditorDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(richTextDataTypeName);
|
||||
const richTextElementTypeId = await umbracoApi.documentType.createDefaultElementType(richTextElementTypeName, richTextElementGroupName, richTextDataTypeName, richTextEditorDataTypeId);
|
||||
blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, richTextElementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListDataTypeName, blockListDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(richTextElementTypeName, true);
|
||||
await umbracoUi.content.enterRTETipTapEditor(richTextEditorValue);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the RTE is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
const documentRichTextValues = documentData.values[0].value.contentData[0].values.find(value => value.alias === AliasHelper.toAlias(richTextDataTypeName));
|
||||
expect(documentRichTextValues.value.markup).toContain(expectedRichTextEditorOutputValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(richTextElementTypeName);
|
||||
});
|
||||
|
||||
test('can publish a block list editor with a block grid editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const textStringValue = 'Hello World';
|
||||
const blockGridDataTypeName = 'BlockGridDataTypeName';
|
||||
const blockGridElementTypeName = 'BlockGridElementName';
|
||||
const blockGridElementGroupName = 'GridElementGroup';
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
|
||||
const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName);
|
||||
textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id);
|
||||
const blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridDataTypeName, textStringElementTypeId);
|
||||
const blockGridElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockGridElementTypeName, blockGridElementGroupName, blockGridDataTypeName, blockGridDataTypeId);
|
||||
blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, blockGridElementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListDataTypeName, blockListDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(blockGridElementTypeName, true);
|
||||
await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName);
|
||||
await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true);
|
||||
await umbracoUi.content.enterTextstring(textStringValue);
|
||||
await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the BlockGrid is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockListDataTypeName));
|
||||
expect(documentValues.value.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeId);
|
||||
});
|
||||
|
||||
test('can publish a block list editor with a block list editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const textStringValue = 'Hello World';
|
||||
const secondBlockListDataTypeName = 'SecondBlockListName';
|
||||
const blockListElementTypeName = 'BlockListElementName';
|
||||
const blockListElementGroupName = 'ListElementGroup';
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(secondBlockListDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName);
|
||||
|
||||
const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName);
|
||||
textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id);
|
||||
const secondBlockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(secondBlockListDataTypeName, textStringElementTypeId);
|
||||
const blockListElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockListElementTypeName, blockListElementGroupName, secondBlockListDataTypeName, secondBlockListDataTypeId);
|
||||
blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, blockListElementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListDataTypeName, blockListDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(blockListElementTypeName, true);
|
||||
await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName);
|
||||
await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true);
|
||||
await umbracoUi.content.enterTextstring(textStringValue);
|
||||
await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the BlockList is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(blockListDataTypeName));
|
||||
expect(documentValues.value.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(secondBlockListDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName);
|
||||
});
|
||||
+2
@@ -55,6 +55,7 @@ test('can create child content in a collection', async ({umbracoApi, umbracoUi})
|
||||
await umbracoUi.content.chooseDocumentType(childDocumentTypeName);
|
||||
await umbracoUi.content.enterContentName(firstChildContentName);
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
|
||||
// Assert
|
||||
const childData = await umbracoApi.document.getChildren(contentId);
|
||||
@@ -87,6 +88,7 @@ test('can create multiple child nodes in a collection', async ({umbracoApi, umbr
|
||||
await umbracoUi.content.chooseDocumentType(childDocumentTypeName);
|
||||
await umbracoUi.content.enterContentName(secondChildContentName);
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
|
||||
// Assert
|
||||
const childData = await umbracoApi.document.getChildren(contentId);
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ test('can publish content with the media picker data type', async ({umbracoApi,
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.created);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
@@ -159,4 +159,4 @@ test('can not publish a mandatory media picker with an empty value', async ({umb
|
||||
expect(contentData.values[0].value[0].mediaTypeAlias).toEqual(mediaTypeName);
|
||||
expect(contentData.values[0].value[0].focalPoint).toBeNull();
|
||||
expect(contentData.values[0].value[0].crops).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ test('cannot input the text that exceeds the allowed amount of characters', asyn
|
||||
// Arrange
|
||||
const maxChars = 100;
|
||||
const textExceedMaxChars = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam mattis porttitor orci id cursus. Nulla';
|
||||
const warningMessage = 'This field exceeds the allowed amount of characters';
|
||||
const warningMessage = 'The string length exceeds the maximum length of ' + maxChars + ' characters, 1 too many.';
|
||||
const dataTypeId = await umbracoApi.dataType.createTextareaDataType(customDataTypeName, maxChars);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, dataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ test.beforeEach(async ({umbracoApi, umbracoUi}) => {
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ test('cannot input the text that exceeds the allowed amount of characters', asyn
|
||||
// Arrange
|
||||
const maxChars = 20;
|
||||
const textExceedMaxChars = 'Lorem ipsum dolor sit';
|
||||
const warningMessage = 'This field exceeds the allowed amount of characters';
|
||||
const warningMessage = 'The string length exceeds the maximum length of ' + maxChars + ' characters, 1 too many.';
|
||||
const dataTypeId = await umbracoApi.dataType.createTextstringDataType(customDataTypeName, maxChars);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, dataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
|
||||
-132
@@ -84,136 +84,4 @@ test('can publish content with RTE Tiptap property editor', async ({umbracoApi,
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
expect(contentData.values[0].value.markup).toEqual('<p>' + inputText + '</p>');
|
||||
});
|
||||
|
||||
test.fixme('can add a media in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Media Picker';
|
||||
const imageName = 'Test Image For Content';
|
||||
await umbracoApi.media.ensureNameNotExists(imageName);
|
||||
await umbracoApi.media.createDefaultMediaWithImage(imageName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
// fix this
|
||||
await umbracoUi.content.selectMediaWithName(imageName);
|
||||
await umbracoUi.content.clickChooseModalButton();
|
||||
await umbracoUi.content.clickMediaCaptionAltTextModalSubmitButton();
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toContain('<img');
|
||||
expect(contentData.values[0].value.markup).toContain(imageName);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.media.ensureNameNotExists(imageName);
|
||||
});
|
||||
|
||||
test('can add a video in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Embed';
|
||||
const videoURL = 'https://www.youtube.com/watch?v=Yu29dE-0OoI';
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.enterEmbeddedURL(videoURL);
|
||||
await umbracoUi.content.clickEmbeddedRetrieveButton();
|
||||
await umbracoUi.content.waitForEmbeddedPreviewVisible();
|
||||
await umbracoUi.content.clickEmbeddedMediaModalConfirmButton();
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toContain('data-embed-url');
|
||||
expect(contentData.values[0].value.markup).toContain(videoURL);
|
||||
});
|
||||
|
||||
test('cannot submit an empty link in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Link';
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.clickManualLinkButton();
|
||||
await umbracoUi.content.enterLink('');
|
||||
await umbracoUi.content.enterAnchorOrQuerystring('');
|
||||
await umbracoUi.content.enterLinkTitle('');
|
||||
await umbracoUi.content.clickAddButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isTextWithMessageVisible(ConstantHelper.validationMessages.emptyLinkPicker);
|
||||
});
|
||||
|
||||
// TODO: Remove skip when the front-end ready. Currently it still accept the empty link with an anchor or querystring
|
||||
// Issue link: https://github.com/umbraco/Umbraco-CMS/issues/17411
|
||||
test.skip('cannot submit an empty URL with an anchor or querystring in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Link';
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.clickManualLinkButton();
|
||||
await umbracoUi.content.enterLink('');
|
||||
await umbracoUi.content.enterAnchorOrQuerystring('#value');
|
||||
await umbracoUi.content.clickAddButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isTextWithMessageVisible(ConstantHelper.validationMessages.emptyLinkPicker);
|
||||
});
|
||||
|
||||
// TODO: Remove skip when the front-end ready. Currently it is impossible to link to unpublished document
|
||||
// Issue link: https://github.com/umbraco/Umbraco-CMS/issues/17974
|
||||
test.skip('can insert a link to an unpublished document in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Link';
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
// Create a document to link
|
||||
const documentTypeForLinkedDocumentName = 'TestDocumentType';
|
||||
const documentTypeForLinkedDocumentId = await umbracoApi.documentType.createDefaultDocumentTypeWithAllowAsRoot(documentTypeForLinkedDocumentName);
|
||||
const linkedDocumentName = 'LinkedDocument';
|
||||
await umbracoApi.document.createDefaultDocument(linkedDocumentName, documentTypeForLinkedDocumentId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.clickDocumentLinkButton();
|
||||
await umbracoUi.content.selectLinkByName(linkedDocumentName);
|
||||
await umbracoUi.content.clickButtonWithName('Choose');
|
||||
await umbracoUi.content.clickAddButton();
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
|
||||
// Clean
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeForLinkedDocumentName);
|
||||
await umbracoApi.document.ensureNameNotExists(linkedDocumentName);
|
||||
});
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
import {AliasHelper, ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
// Content Name
|
||||
const contentName = 'ContentName';
|
||||
|
||||
// Document Type
|
||||
const documentTypeName = 'DocumentTypeName';
|
||||
let documentTypeId = null;
|
||||
const documentTypeGroupName = 'DocumentGroup';
|
||||
|
||||
// Rich Text Editor
|
||||
const richTextDataTypeName = 'RichTextDataType';
|
||||
let richTextDataTypeId = null;
|
||||
|
||||
// Text String
|
||||
const textStringElementTypeName = 'TextStringElementName';
|
||||
let textStringElementTypeId = null;
|
||||
let textStringGroupName = 'TextGroup';
|
||||
const textStringDataTypeName = 'Textstring';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(richTextDataTypeName);
|
||||
});
|
||||
|
||||
test('can publish a rich text editor with a rich text editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const richTextEditorValue = 'Hello First World';
|
||||
const secondRichTextEditorValue = 'Hello Second World';
|
||||
const expectedRichTextEditorOutputValue = '<p>' + richTextEditorValue + '</p>';
|
||||
const secondExpectedRichTextEditorOutputValue = '<p>' + secondRichTextEditorValue + '</p>';
|
||||
const secondRichTextDataTypeName = 'SecondRichTextName';
|
||||
const richTextElementTypeName = 'RichTextElementName';
|
||||
const richTextElementGroupName = 'RichTextElementGroupName';
|
||||
await umbracoApi.dataType.ensureNameNotExists(secondRichTextDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(richTextElementGroupName);
|
||||
|
||||
const secondRichTextEditorDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(secondRichTextDataTypeName);
|
||||
const richTextElementTypeId = await umbracoApi.documentType.createDefaultElementType(richTextElementTypeName, richTextElementGroupName, secondRichTextDataTypeName, secondRichTextEditorDataTypeId);
|
||||
richTextDataTypeId = await umbracoApi.dataType.createRichTextEditorWithABlock(richTextDataTypeName, richTextElementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, richTextDataTypeName, richTextDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.enterRTETipTapEditor(richTextEditorValue);
|
||||
await umbracoUi.content.clickInsertBlockButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(richTextElementTypeName, true);
|
||||
await umbracoUi.content.enterRTETipTapEditorWithName(AliasHelper.toAlias(secondRichTextDataTypeName), secondRichTextEditorValue);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the RTE is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
const documentValues = documentData.values.find(value => value.alias === AliasHelper.toAlias(richTextDataTypeName));
|
||||
// Value in the first RTE
|
||||
expect(documentValues.value.markup).toContain(expectedRichTextEditorOutputValue);
|
||||
// Value in the second RTE
|
||||
const secondRTEInBlock = documentValues.value.blocks.contentData[0].values.find(value => value.alias === AliasHelper.toAlias(secondRichTextDataTypeName));
|
||||
expect(secondRTEInBlock.value.markup).toContain(secondExpectedRichTextEditorOutputValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.documentType.ensureNameNotExists(richTextElementGroupName);
|
||||
});
|
||||
|
||||
test('can publish a rich text editor with a block grid editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const textStringValue = 'Hello World';
|
||||
const blockGridDataTypeName = 'BlockGridDataTypeName';
|
||||
const blockGridElementTypeName = 'BlockGridElementTypeName';
|
||||
const blockGridElementGroupName = 'BlockGridElementGroupName';
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
|
||||
const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName);
|
||||
textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id);
|
||||
const blockGridDataTypeId = await umbracoApi.dataType.createBlockGridWithABlockWithInlineEditingMode(blockGridDataTypeName, textStringElementTypeId, true);
|
||||
const blockGridElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockGridElementTypeName, blockGridElementGroupName, blockGridDataTypeName, blockGridDataTypeId);
|
||||
richTextDataTypeId = await umbracoApi.dataType.createRichTextEditorWithABlock(richTextDataTypeName, blockGridElementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, richTextDataTypeName, richTextDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickInsertBlockButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(blockGridElementTypeName, true);
|
||||
await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName);
|
||||
await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true);
|
||||
await umbracoUi.content.enterTextstring(textStringValue);
|
||||
await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the BlockGrid is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(documentData.values[0].value.blocks.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockGridElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
});
|
||||
|
||||
test('can publish a rich text editor with a block list editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const textStringValue = 'Hello World';
|
||||
const blockListDataTypeName = 'BlockListName';
|
||||
const blockListElementTypeName = 'BlockListElementName';
|
||||
const blockListElementGroupName = 'BlockListGroupName';
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
|
||||
const textStringDataType = await umbracoApi.dataType.getByName(textStringDataTypeName);
|
||||
textStringElementTypeId = await umbracoApi.documentType.createDefaultElementType(textStringElementTypeName, textStringGroupName, textStringDataTypeName, textStringDataType.id);
|
||||
const blockListDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListDataTypeName, textStringElementTypeId);
|
||||
const blockListElementTypeId = await umbracoApi.documentType.createDefaultElementType(blockListElementTypeName, blockListElementGroupName, blockListDataTypeName, blockListDataTypeId);
|
||||
richTextDataTypeId = await umbracoApi.dataType.createRichTextEditorWithABlock(richTextDataTypeName, blockListElementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, richTextDataTypeName, richTextDataTypeId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickInsertBlockButton();
|
||||
await umbracoUi.content.clickBlockCardWithName(blockListElementTypeName, true);
|
||||
await umbracoUi.content.clickAddBlockWithNameButton(textStringElementTypeName);
|
||||
await umbracoUi.content.clickBlockCardWithName(textStringElementTypeName, true);
|
||||
await umbracoUi.content.enterTextstring(textStringValue);
|
||||
await umbracoUi.content.clickCreateForModalWithHeadline('Add ' + textStringElementTypeName);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
// Asserts that the value in the BlockGrid is as expected
|
||||
const documentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(documentData.values[0].value.blocks.contentData[0].values[0].value.contentData[0].values[0].value).toContain(textStringValue);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListDataTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockListElementTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(textStringElementTypeName);
|
||||
});
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
const contentName = 'TestContent';
|
||||
const documentTypeName = 'TestDocumentTypeForContent';
|
||||
const customDataTypeName = 'Test RTE Tiptap Style Select';
|
||||
const inputText = 'This is Tiptap test';
|
||||
|
||||
test.beforeEach(async ({umbracoApi, umbracoUi}) => {
|
||||
const customDataTypeId = await umbracoApi.dataType.createTiptapDataTypeWithStyleSelect(customDataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.enterRTETipTapEditor(inputText);
|
||||
await umbracoUi.content.selectAllRTETipTapEditorText();
|
||||
})
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
test('can apply page header format', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
await umbracoUi.content.clickStyleSelectButton();
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.hoverCascadingMenuItemWithName('Headers');
|
||||
await umbracoUi.content.clickCascadingMenuItemWithName('Page header');
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toEqual('<h2>' + inputText + '</h2><p></p>');
|
||||
});
|
||||
|
||||
test('can apply section header format', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
await umbracoUi.content.clickStyleSelectButton();
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.hoverCascadingMenuItemWithName('Headers');
|
||||
await umbracoUi.content.clickCascadingMenuItemWithName('Section header');
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toEqual('<h3>' + inputText + '</h3><p></p>');
|
||||
});
|
||||
|
||||
test('can apply paragraph header format', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
await umbracoUi.content.clickStyleSelectButton();
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.hoverCascadingMenuItemWithName('Headers');
|
||||
await umbracoUi.content.clickCascadingMenuItemWithName('Paragraph header');
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toEqual('<h4>' + inputText + '</h4><p></p>');
|
||||
});
|
||||
|
||||
test('can apply paragraph blocks format', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
await umbracoUi.content.clickStyleSelectButton();
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.hoverCascadingMenuItemWithName('Blocks');
|
||||
await umbracoUi.content.clickCascadingMenuItemWithName('Paragraph');
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toEqual('<p>' + inputText + '</p>');
|
||||
});
|
||||
|
||||
test('can apply block quote format', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
await umbracoUi.content.clickStyleSelectButton();
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.hoverCascadingMenuItemWithName('Containers');
|
||||
await umbracoUi.content.clickCascadingMenuItemWithName('Block quote');
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toEqual('<blockquote><p>' + inputText + '</p></blockquote><p></p>');
|
||||
});
|
||||
|
||||
test('can apply code block format', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
await umbracoUi.content.clickStyleSelectButton();
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.hoverCascadingMenuItemWithName('Containers');
|
||||
await umbracoUi.content.clickCascadingMenuItemWithName('Code block');
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toEqual('<pre><code>' + inputText + '</code></pre><p></p>');
|
||||
});
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
const contentName = 'TestContent';
|
||||
const documentTypeName = 'TestDocumentTypeForContent';
|
||||
const customDataTypeName = 'Test RTE Tiptap';
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
test('can add a media in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Media Picker';
|
||||
const imageName = 'Test Image For Content';
|
||||
await umbracoApi.media.ensureNameNotExists(imageName);
|
||||
await umbracoApi.media.createDefaultMediaWithImage(imageName);
|
||||
const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.selectMediaWithName(imageName);
|
||||
await umbracoUi.content.clickChooseModalButton();
|
||||
await umbracoUi.content.clickMediaCaptionAltTextModalSubmitButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toContain('<img');
|
||||
expect(contentData.values[0].value.markup).toContain(imageName);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.media.ensureNameNotExists(imageName);
|
||||
});
|
||||
|
||||
test('can embed a video into RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Embed';
|
||||
const videoURL = 'https://www.youtube.com/watch?v=Yu29dE-0OoI';
|
||||
const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.enterEmbeddedURL(videoURL);
|
||||
await umbracoUi.content.clickEmbeddedRetrieveButton();
|
||||
await umbracoUi.content.waitForEmbeddedPreviewVisible();
|
||||
await umbracoUi.content.clickEmbeddedMediaModalConfirmButton();
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].value.markup).toContain('data-embed-url');
|
||||
expect(contentData.values[0].value.markup).toContain(videoURL);
|
||||
});
|
||||
|
||||
test('cannot submit an empty link in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Link';
|
||||
const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.clickManualLinkButton();
|
||||
await umbracoUi.content.enterLink('');
|
||||
await umbracoUi.content.enterAnchorOrQuerystring('');
|
||||
await umbracoUi.content.enterLinkTitle('');
|
||||
await umbracoUi.content.clickAddButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isTextWithMessageVisible(ConstantHelper.validationMessages.emptyLinkPicker);
|
||||
});
|
||||
|
||||
// TODO: Remove skip when the front-end ready. Currently it still accept the empty link with an anchor or querystring
|
||||
// Issue link: https://github.com/umbraco/Umbraco-CMS/issues/17411
|
||||
test.skip('cannot submit an empty URL with an anchor or querystring in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Link';
|
||||
const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.clickManualLinkButton();
|
||||
await umbracoUi.content.enterLink('');
|
||||
await umbracoUi.content.enterAnchorOrQuerystring('#value');
|
||||
await umbracoUi.content.clickAddButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isTextWithMessageVisible(ConstantHelper.validationMessages.emptyLinkPicker);
|
||||
});
|
||||
|
||||
// TODO: Remove skip when the front-end ready. Currently it is impossible to link to unpublished document
|
||||
// Issue link: https://github.com/umbraco/Umbraco-CMS/issues/17974
|
||||
test.skip('can insert a link to an unpublished document in RTE Tiptap property editor', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const iconTitle = 'Link';
|
||||
const customDataTypeId = await umbracoApi.dataType.createDefaultTiptapDataType(customDataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
// Create a document to link
|
||||
const documentTypeForLinkedDocumentName = 'TestDocumentType';
|
||||
const documentTypeForLinkedDocumentId = await umbracoApi.documentType.createDefaultDocumentTypeWithAllowAsRoot(documentTypeForLinkedDocumentName);
|
||||
const linkedDocumentName = 'LinkedDocument';
|
||||
await umbracoApi.document.createDefaultDocument(linkedDocumentName, documentTypeForLinkedDocumentId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickTipTapToolbarIconWithTitle(iconTitle);
|
||||
await umbracoUi.content.clickDocumentLinkButton();
|
||||
await umbracoUi.content.selectLinkByName(linkedDocumentName);
|
||||
await umbracoUi.content.clickButtonWithName('Choose');
|
||||
await umbracoUi.content.clickAddButton();
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
|
||||
// Clean
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeForLinkedDocumentName);
|
||||
await umbracoApi.document.ensureNameNotExists(linkedDocumentName);
|
||||
});
|
||||
|
||||
test('can view word count', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const inputText = 'Test Tiptap <b>here</b>!!!';
|
||||
const expectedWordCount = 3;
|
||||
const customDataTypeId = await umbracoApi.dataType.createTiptapDataTypeWithWordCountStatusbar(customDataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.enterRTETipTapEditor(inputText);
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesTiptapHaveWordCount(expectedWordCount);
|
||||
});
|
||||
|
||||
test('can view element path', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const inputText = 'This is Tiptap test';
|
||||
const expectedElementPath = 'p';
|
||||
const customDataTypeId = await umbracoApi.dataType.createTiptapDataTypeWithElementPathStatusbar(customDataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.enterRTETipTapEditor(inputText);
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesElementPathHaveText(expectedElementPath);
|
||||
});
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import {ConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
let collectionId = '';
|
||||
const contentName = 'TestContent';
|
||||
const documentTypeName = 'TestDocumentTypeForContent';
|
||||
const childDocumentTypeName = 'TestChildDocumentType';
|
||||
const firstChildContentName = 'First Child Content';
|
||||
const secondChildContentName = 'Second Child Content';
|
||||
const collectionDataTypeName = 'List View - Content';
|
||||
const referenceHeadline = ConstantHelper.trashDeleteDialogMessage.bulkReferenceHeadline;
|
||||
const documentPickerName = ['TestPicker', 'DocumentTypeForPicker'];
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
const collectionDataTypeData = await umbracoApi.dataType.getByName(collectionDataTypeName);
|
||||
collectionId = collectionDataTypeData.id;
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(childDocumentTypeName);
|
||||
await umbracoApi.document.emptyRecycleBin();
|
||||
});
|
||||
|
||||
test('can bulk trash content nodes without a relation', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const childDocumentTypeId = await umbracoApi.documentType.createDefaultDocumentType(childDocumentTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithAllowedChildNodeAndCollectionId(documentTypeName, childDocumentTypeId, collectionId);
|
||||
const contentId = await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoApi.document.createDefaultDocumentWithParent(firstChildContentName, childDocumentTypeId, contentId);
|
||||
await umbracoApi.document.createDefaultDocumentWithParent(secondChildContentName, childDocumentTypeId, contentId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.selectContentWithNameInListView(firstChildContentName);
|
||||
await umbracoUi.content.selectContentWithNameInListView(secondChildContentName);
|
||||
await umbracoUi.content.clickTrashSelectedListItems();
|
||||
// Verify the references list not displayed
|
||||
await umbracoUi.content.isReferenceHeadlineVisible(false);
|
||||
await umbracoUi.content.clickConfirmTrashButton();
|
||||
|
||||
// // Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
expect(await umbracoApi.document.doesNameExist(firstChildContentName)).toBeFalsy();
|
||||
expect(await umbracoApi.document.doesNameExist(secondChildContentName)).toBeFalsy();
|
||||
await umbracoUi.content.isItemVisibleInRecycleBin(firstChildContentName);
|
||||
await umbracoUi.content.isItemVisibleInRecycleBin(secondChildContentName);
|
||||
expect(await umbracoApi.document.doesItemExistInRecycleBin(firstChildContentName)).toBeTruthy();
|
||||
expect(await umbracoApi.document.doesItemExistInRecycleBin(secondChildContentName)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('can bulk trash content nodes with a relation', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const childDocumentTypeId = await umbracoApi.documentType.createDefaultDocumentType(childDocumentTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithAllowedChildNodeAndCollectionId(documentTypeName, childDocumentTypeId, collectionId);
|
||||
const contentId = await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoApi.document.publish(contentId);
|
||||
const firstChildContentId = await umbracoApi.document.createDefaultDocumentWithParent(firstChildContentName, childDocumentTypeId, contentId);
|
||||
await umbracoApi.document.publish(firstChildContentId);
|
||||
await umbracoApi.document.createDefaultDocumentWithParent(secondChildContentName, childDocumentTypeId, contentId);
|
||||
// Create a document that has a document picker with firstChildContentName
|
||||
await umbracoApi.document.createDefaultDocumentWithOneDocumentLink(documentPickerName[0], firstChildContentName, firstChildContentId, documentPickerName[1]);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.selectContentWithNameInListView(firstChildContentName);
|
||||
await umbracoUi.content.selectContentWithNameInListView(secondChildContentName);
|
||||
await umbracoUi.content.clickTrashSelectedListItems();
|
||||
// Verify the references list
|
||||
await umbracoUi.content.doesReferenceHeadlineHaveText(referenceHeadline);
|
||||
await umbracoUi.content.doesReferenceItemsHaveCount(1);
|
||||
await umbracoUi.content.isReferenceItemNameVisible(firstChildContentName);
|
||||
await umbracoUi.content.clickConfirmTrashButton();
|
||||
|
||||
// // Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
expect(await umbracoApi.document.doesNameExist(firstChildContentName)).toBeFalsy();
|
||||
expect(await umbracoApi.document.doesNameExist(secondChildContentName)).toBeFalsy();
|
||||
await umbracoUi.content.isItemVisibleInRecycleBin(firstChildContentName);
|
||||
await umbracoUi.content.isItemVisibleInRecycleBin(secondChildContentName);
|
||||
expect(await umbracoApi.document.doesItemExistInRecycleBin(firstChildContentName)).toBeTruthy();
|
||||
expect(await umbracoApi.document.doesItemExistInRecycleBin(secondChildContentName)).toBeTruthy();
|
||||
|
||||
// Clean
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentPickerName[1]);
|
||||
});
|
||||
+1
-1
@@ -6,7 +6,7 @@ const contentName = 'TestContent';
|
||||
const documentTypeName = 'TestDocumentTypeForContent';
|
||||
const dataTypeName = 'Textstring';
|
||||
const contentText = 'This is test content text';
|
||||
const referenceHeadline = 'The following items depend on this';
|
||||
const referenceHeadline = ConstantHelper.trashDeleteDialogMessage.referenceHeadline;
|
||||
const documentPickerName = ['TestPicker', 'DocumentTypeForPicker'];
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
+2
-2
@@ -61,8 +61,8 @@ test('tiptap is the default property editor in rich text editor', async ({umbrac
|
||||
await umbracoUi.dataType.goToDataType(dataTypeName);
|
||||
|
||||
// Assert
|
||||
//await umbracoUi.dataType.doesSettingHaveValue(ConstantHelper.tipTapSettings);
|
||||
//await umbracoUi.dataType.doesSettingItemsHaveCount(ConstantHelper.tipTapSettings);
|
||||
await umbracoUi.dataType.doesSettingHaveValue(ConstantHelper.tipTapSettings);
|
||||
await umbracoUi.dataType.doesSettingItemsHaveCount(ConstantHelper.tipTapSettings);
|
||||
await umbracoUi.dataType.doesPropertyEditorHaveName(tipTapPropertyEditorName);
|
||||
await umbracoUi.dataType.doesPropertyEditorHaveAlias(tipTapAlias);
|
||||
await umbracoUi.dataType.doesPropertyEditorHaveUiAlias(tipTapUiAlias);
|
||||
|
||||
@@ -121,12 +121,15 @@ test('can add an available block', async ({umbracoApi, umbracoUi}) => {
|
||||
await umbracoUi.dataType.goToDataType(tipTapName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.dataType.isExtensionItemChecked('Block', false);
|
||||
await umbracoUi.dataType.addAvailableBlocks(elementTypeName);
|
||||
await umbracoUi.dataType.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.dataType.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
expect(await umbracoApi.dataType.doesRTEContainBlocks(tipTapName, [elementTypeId])).toBeTruthy();
|
||||
// Verify that "Block" extension is enable
|
||||
await umbracoUi.dataType.isExtensionItemChecked('Block');
|
||||
|
||||
// Clean
|
||||
await umbracoApi.documentType.ensureNameNotExists(elementTypeName);
|
||||
@@ -226,3 +229,38 @@ test('can disable extensions item', async ({umbracoApi, umbracoUi}) => {
|
||||
expect(extensionsValue.value.length).toBe(extensionsCount - 1);
|
||||
expect(extensionsValue.value).not.toContain(extensionItemName);
|
||||
});
|
||||
|
||||
test('can add a statusbar', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const statusbarName = 'Word Count';
|
||||
const statusbarApiValue = 'Umb.Tiptap.Statusbar.WordCount';
|
||||
await umbracoApi.dataType.createDefaultTiptapDataType(tipTapName);
|
||||
await umbracoUi.dataType.goToDataType(tipTapName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.dataType.clickStatusbarItemInToolboxWithName(statusbarName);
|
||||
await umbracoUi.dataType.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.dataType.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
const tipTapData = await umbracoApi.dataType.getByName(tipTapName);
|
||||
const statusbarValue = tipTapData.values.find(value => value.alias === 'statusbar');
|
||||
expect(statusbarValue.value).toEqual([[statusbarApiValue]]);
|
||||
});
|
||||
|
||||
test('can remove a statusbar', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const statusbarName = 'Word Count';
|
||||
await umbracoApi.dataType.createTiptapDataTypeWithWordCountStatusbar(tipTapName);
|
||||
await umbracoUi.dataType.goToDataType(tipTapName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.dataType.clickStatusbarItemWithName(statusbarName);
|
||||
await umbracoUi.dataType.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.dataType.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
const tipTapData = await umbracoApi.dataType.getByName(tipTapName);
|
||||
const statusbarValue = tipTapData.values.find(value => value.alias === 'statusbar');
|
||||
expect(statusbarValue).toBeFalsy();
|
||||
});
|
||||
@@ -174,6 +174,8 @@ test('can trash a media item', async ({umbracoApi, umbracoUi}) => {
|
||||
// Act
|
||||
await umbracoUi.media.clickActionsMenuForName(mediaFileName);
|
||||
await umbracoUi.media.clickTrashButton();
|
||||
// Verify the references list not displayed
|
||||
await umbracoUi.content.isReferenceHeadlineVisible(false);
|
||||
await umbracoUi.media.clickConfirmTrashButton();
|
||||
|
||||
// Assert
|
||||
@@ -245,3 +247,79 @@ test('can empty the recycle bin', async ({umbracoApi, umbracoUi}) => {
|
||||
expect(await umbracoApi.media.doesNameExist(mediaFileName)).toBeFalsy();
|
||||
expect(await umbracoApi.media.doesMediaItemExistInRecycleBin(mediaFileName)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('can trash a media node with a relation', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const documentPickerName = ['TestPicker', 'DocumentTypeForPicker'];
|
||||
await umbracoApi.media.emptyRecycleBin();
|
||||
await umbracoApi.media.createDefaultMediaFile(mediaFileName);
|
||||
await umbracoApi.media.doesNameExist(mediaFileName);
|
||||
// Create a document that have media picker is firstMediaFileName
|
||||
await umbracoApi.document.createDefaultDocumentWithOneMediaLink(documentPickerName[0], mediaFileName, documentPickerName[1]);
|
||||
await umbracoUi.media.goToSection(ConstantHelper.sections.media);
|
||||
|
||||
// Act
|
||||
await umbracoUi.media.clickActionsMenuForName(mediaFileName);
|
||||
await umbracoUi.media.clickTrashButton();
|
||||
// Verify the references list
|
||||
await umbracoUi.media.doesReferenceHeadlineHaveText(ConstantHelper.trashDeleteDialogMessage.referenceHeadline);
|
||||
await umbracoUi.media.doesReferenceItemsHaveCount(1);
|
||||
await umbracoUi.media.isReferenceItemNameVisible(documentPickerName[0]);
|
||||
await umbracoUi.media.clickConfirmTrashButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.media.doesSuccessNotificationHaveText(NotificationConstantHelper.success.movedToRecycleBin);
|
||||
await umbracoUi.media.isItemVisibleInRecycleBin(mediaFileName);
|
||||
expect(await umbracoApi.media.doesNameExist(mediaFileName)).toBeFalsy();
|
||||
expect(await umbracoApi.media.doesMediaItemExistInRecycleBin(mediaFileName)).toBeTruthy();
|
||||
|
||||
// Clean
|
||||
await umbracoApi.media.emptyRecycleBin();
|
||||
await umbracoApi.document.ensureNameNotExists(documentPickerName[0]);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentPickerName[1]);
|
||||
});
|
||||
|
||||
test('can bulk trash media nodes with a relation', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const firstMediaFileName = 'FirstMediaFile';
|
||||
const secondMediaFileName = 'SecondMediaFile';
|
||||
const documentPickerName1 = ['TestPicker1', 'DocumentTypeForPicker1'];
|
||||
const documentPickerName2 = ['TestPicker2', 'DocumentTypeForPicker2'];
|
||||
await umbracoApi.media.emptyRecycleBin();
|
||||
await umbracoApi.media.createDefaultMediaFile(firstMediaFileName);
|
||||
await umbracoApi.media.createDefaultMediaFile(secondMediaFileName);
|
||||
// Create a document that has a media picker with firstMediaFileName
|
||||
await umbracoApi.document.createDefaultDocumentWithOneMediaLink(documentPickerName1[0], firstMediaFileName, documentPickerName1[1]);
|
||||
// Create a document that has a media picker with secondMediaFileName
|
||||
await umbracoApi.document.createDefaultDocumentWithOneMediaLink(documentPickerName2[0], secondMediaFileName, documentPickerName2[1]);
|
||||
|
||||
// Act
|
||||
await umbracoUi.media.goToSection(ConstantHelper.sections.media);
|
||||
await umbracoUi.media.selectMediaWithName(firstMediaFileName);
|
||||
await umbracoUi.media.selectMediaWithName(secondMediaFileName);
|
||||
await umbracoUi.media.clickBulkTrashButton();
|
||||
// Verify the references list
|
||||
await umbracoUi.media.doesReferenceHeadlineHaveText(ConstantHelper.trashDeleteDialogMessage.bulkReferenceHeadline);
|
||||
await umbracoUi.media.doesReferenceItemsHaveCount(2);
|
||||
await umbracoUi.media.isReferenceItemNameVisible(firstMediaFileName);
|
||||
await umbracoUi.media.isReferenceItemNameVisible(secondMediaFileName);
|
||||
await umbracoUi.media.clickConfirmTrashButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.media.isSuccessNotificationVisible();
|
||||
expect(await umbracoApi.media.doesNameExist(firstMediaFileName)).toBeFalsy();
|
||||
expect(await umbracoApi.media.doesNameExist(secondMediaFileName)).toBeFalsy();
|
||||
expect(await umbracoApi.media.doesMediaItemExistInRecycleBin(firstMediaFileName)).toBeTruthy();
|
||||
expect(await umbracoApi.media.doesMediaItemExistInRecycleBin(secondMediaFileName)).toBeTruthy();
|
||||
await umbracoUi.media.isItemVisibleInRecycleBin(firstMediaFileName);
|
||||
await umbracoUi.media.isItemVisibleInRecycleBin(secondMediaFileName, true, false);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.media.ensureNameNotExists(firstMediaFileName);
|
||||
await umbracoApi.media.ensureNameNotExists(secondMediaFileName);
|
||||
await umbracoApi.document.ensureNameNotExists(documentPickerName1[0]);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentPickerName1[1]);
|
||||
await umbracoApi.document.ensureNameNotExists(documentPickerName2[0]);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentPickerName2[1]);
|
||||
await umbracoApi.media.emptyRecycleBin();
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -0,0 +1,28 @@
|
||||
// TODO: Consider refactoring the initialization process to improve readability or simplify the builder configuration.
|
||||
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();
|
||||
@@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.deliveryApi.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.getMediaUrlById(mediaId);
|
||||
|
||||
// Assert
|
||||
expect(await umbracoApi.deliveryApi.doesMediaItemWithIdContainValues(mediaId, mediaName, mediaTypeName, mediaUrl)).toBeTruthy();
|
||||
|
||||
// Clean
|
||||
await umbracoApi.media.ensureNameNotExists(mediaName);
|
||||
});
|
||||
+25
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+73
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -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<AzureB2COptions>();
|
||||
|
||||
builder.ConfigureAuthentication(builder.Config);
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -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<BackOfficeExternalLoginProviderOptions>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
+11
@@ -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;
|
||||
}
|
||||
}
|
||||
+58
@@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.login.clickSignInWithAzureADB2CButton();
|
||||
await umbracoUi.login.enterAzureADB2CEmail(azureEmail);
|
||||
await umbracoUi.login.enterAzureADB2CPassword(azurePassword);
|
||||
await umbracoUi.login.clickSignInButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
});
|
||||
+54
@@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -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);
|
||||
});
|
||||
+27
@@ -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);
|
||||
});
|
||||
@@ -1,6 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
|
||||
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0001</DiagnosticId>
|
||||
<Target>T:Umbraco.Cms.Tests.Integration.ManagementApi.Policies.AllCultureControllerTests</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0001</DiagnosticId>
|
||||
<Target>T:Umbraco.Cms.Tests.Integration.ManagementApi.Preview.EndPreviewTests</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0001</DiagnosticId>
|
||||
<Target>T:Umbraco.Cms.Tests.Integration.ManagementApi.Preview.EnterPreviewTests</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0001</DiagnosticId>
|
||||
<Target>T:Umbraco.Cms.Tests.Integration.Umbraco.Core.Services.DocumentUrlServiceTest</Target>
|
||||
@@ -78,6 +99,20 @@
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.ManagementApi.ManagementApiTest`1.Setup</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.Testing.UmbracoIntegrationTestBase.UseTestDatabase(Microsoft.AspNetCore.Builder.IApplicationBuilder)</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.Umbraco.Core.Services.DocumentNavigationServiceTests.Bin_Structure_Can_Rebuild</Target>
|
||||
@@ -141,4 +176,46 @@
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
</Suppressions>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0005</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.ManagementApi.ManagementApiTest`1.set_MethodSelector(System.Linq.Expressions.Expression{System.Func{`0,System.Object}})</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0012</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.ManagementApi.ManagementApiTest`1.get_Url</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0012</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.TestServerTest.UmbracoTestServerTestBase.get_Services</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0012</DiagnosticId>
|
||||
<Target>P:Umbraco.Cms.Tests.Integration.ManagementApi.ManagementApiTest`1.Url</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0012</DiagnosticId>
|
||||
<Target>P:Umbraco.Cms.Tests.Integration.TestServerTest.UmbracoTestServerTestBase.Services</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0012</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.TestServerTest.UmbracoTestServerTestBase.CustomMvcSetup(Microsoft.Extensions.DependencyInjection.IMvcBuilder)</Target>
|
||||
<Left>lib/net9.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net9.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
</Suppressions>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Culture;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.Culture;
|
||||
|
||||
public class AllCultureControllerTests : ManagementApiUserGroupTestBase<AllCultureController>
|
||||
{
|
||||
ILanguageService LanguageService => GetRequiredService<ILanguageService>();
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var langDa = new LanguageBuilder()
|
||||
.WithCultureInfo("da-DK")
|
||||
.Build();
|
||||
await LanguageService.CreateAsync(langDa, Constants.Security.SuperUserKey);
|
||||
}
|
||||
|
||||
protected override Expression<Func<AllCultureController, object>> MethodSelector =>
|
||||
x => x.GetAll(CancellationToken.None, 0, 100);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType;
|
||||
|
||||
public class ByKeyDataTypeControllerTests : ManagementApiUserGroupTestBase<ByKeyDataTypeController>
|
||||
{
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private Guid _dataTypeKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var dataType = new DataTypeBuilder()
|
||||
.WithId(0)
|
||||
.WithName("Custom list view")
|
||||
.WithDatabaseType(ValueStorageType.Nvarchar)
|
||||
.AddEditor()
|
||||
.WithAlias(Constants.PropertyEditors.Aliases.ListView)
|
||||
.Done()
|
||||
.Build();
|
||||
var response = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
||||
|
||||
_dataTypeKey = response.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<ByKeyDataTypeController, object>> MethodSelector =>
|
||||
x => x.ByKey(CancellationToken.None, _dataTypeKey);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType;
|
||||
|
||||
public class CopyDataTypeControllerTests : ManagementApiUserGroupTestBase<CopyDataTypeController>
|
||||
{
|
||||
private IDataTypeContainerService DataTypeContainerService => GetRequiredService<IDataTypeContainerService>();
|
||||
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private Guid _dataTypeKey;
|
||||
private Guid _folderKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
// Folder
|
||||
var responseFolder = await DataTypeContainerService.CreateAsync(Guid.NewGuid(), "TestFolder", Constants.System.RootKey, Constants.Security.SuperUserKey);
|
||||
_folderKey = responseFolder.Result.Key;
|
||||
|
||||
// Datatype
|
||||
var dataType = new DataTypeBuilder()
|
||||
.WithId(0)
|
||||
.WithName("Custom list view")
|
||||
.WithDatabaseType(ValueStorageType.Nvarchar)
|
||||
.AddEditor()
|
||||
.WithAlias(Constants.PropertyEditors.Aliases.ListView)
|
||||
.Done()
|
||||
.Build();
|
||||
var responseDataType = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
||||
|
||||
_dataTypeKey = responseDataType.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<CopyDataTypeController, object>> MethodSelector =>
|
||||
x => x.Copy(CancellationToken.None, _dataTypeKey, null);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Created
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
|
||||
protected override async Task<HttpResponseMessage> ClientRequest()
|
||||
{
|
||||
CopyDataTypeRequestModel copyDataTypeRequestModel = new() { Target = new ReferenceByIdModel(_folderKey) };
|
||||
|
||||
return await Client.PostAsync(Url, JsonContent.Create(copyDataTypeRequestModel));
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType;
|
||||
|
||||
public class CreateDataTypeControllerTests : ManagementApiUserGroupTestBase<CreateDataTypeController>
|
||||
{
|
||||
protected override Expression<Func<CreateDataTypeController, object>> MethodSelector =>
|
||||
x => x.Create(CancellationToken.None, null);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Created
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
|
||||
protected override async Task<HttpResponseMessage> ClientRequest()
|
||||
{
|
||||
CreateDataTypeRequestModel createDataTypeRequestModel = new()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Parent = null,
|
||||
Name = "TestDataType",
|
||||
EditorAlias = "Umbraco.CheckBoxList",
|
||||
EditorUiAlias = "Umb.PropertyEditorUi.CheckBoxList",
|
||||
};
|
||||
|
||||
return await Client.PostAsync(Url, JsonContent.Create(createDataTypeRequestModel));
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType;
|
||||
|
||||
public class DeleteDataTypeControllerTests : ManagementApiUserGroupTestBase<DeleteDataTypeController>
|
||||
{
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private Guid _dataTypeKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var dataType = new DataTypeBuilder()
|
||||
.WithId(0)
|
||||
.WithName("Custom list view")
|
||||
.WithDatabaseType(ValueStorageType.Nvarchar)
|
||||
.AddEditor()
|
||||
.WithAlias(Constants.PropertyEditors.Aliases.ListView)
|
||||
.Done()
|
||||
.Build();
|
||||
var response = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
||||
|
||||
_dataTypeKey = response.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<DeleteDataTypeController, object>> MethodSelector =>
|
||||
x => x.Delete(CancellationToken.None, _dataTypeKey);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
|
||||
protected override async Task<HttpResponseMessage> ClientRequest() => await Client.DeleteAsync(Url);
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType.Folder;
|
||||
|
||||
public class ByKeyDataTypeFolderControllerTests : ManagementApiUserGroupTestBase<ByKeyDataTypeFolderController>
|
||||
{
|
||||
private IDataTypeContainerService DataTypeContainerService => GetRequiredService<IDataTypeContainerService>();
|
||||
|
||||
private Guid _folderKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var response = await DataTypeContainerService.CreateAsync(Guid.NewGuid(), "TestFolder", Constants.System.RootKey, Constants.Security.SuperUserKey);
|
||||
_folderKey = response.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<ByKeyDataTypeFolderController, object>> MethodSelector =>
|
||||
x => x.ByKey(CancellationToken.None, _folderKey);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Folder;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType.Folder;
|
||||
|
||||
public class CreateDataTypeFolderControllerTests : ManagementApiUserGroupTestBase<CreateDataTypeFolderController>
|
||||
{
|
||||
protected override Expression<Func<CreateDataTypeFolderController, object>> MethodSelector =>
|
||||
x => x.Create(CancellationToken.None, null);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Created
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
|
||||
protected override async Task<HttpResponseMessage> ClientRequest()
|
||||
{
|
||||
CreateFolderRequestModel createFolderModel =
|
||||
new() { Id = Guid.NewGuid(), Parent = null, Name = "TestFolderName" };
|
||||
|
||||
return await Client.PostAsync(Url, JsonContent.Create(createFolderModel));
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType.Folder;
|
||||
|
||||
public class DeleteDataTypeFolderControllerTests : ManagementApiUserGroupTestBase<DeleteDataTypeFolderController>
|
||||
{
|
||||
private IDataTypeContainerService DataTypeContainerService => GetRequiredService<IDataTypeContainerService>();
|
||||
|
||||
private Guid _folderKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var response = await DataTypeContainerService.CreateAsync(Guid.NewGuid(), "TestFolder", Constants.System.RootKey, Constants.Security.SuperUserKey);
|
||||
_folderKey = response.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<DeleteDataTypeFolderController, object>> MethodSelector =>
|
||||
x => x.Delete(CancellationToken.None, _folderKey);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
|
||||
protected override async Task<HttpResponseMessage> ClientRequest() => await Client.DeleteAsync(Url);
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Folder;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType.Folder;
|
||||
|
||||
public class UpdateDataTypeFolderControllerTests : ManagementApiUserGroupTestBase<UpdateDataTypeFolderController>
|
||||
{
|
||||
private IDataTypeContainerService DataTypeContainerService => GetRequiredService<IDataTypeContainerService>();
|
||||
|
||||
private Guid _folderKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var response = await DataTypeContainerService.CreateAsync(Guid.NewGuid(), "TestFolder", Constants.System.RootKey, Constants.Security.SuperUserKey);
|
||||
_folderKey = response.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<UpdateDataTypeFolderController, object>> MethodSelector =>
|
||||
x => x.Update(CancellationToken.None, _folderKey, null);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
|
||||
protected override async Task<HttpResponseMessage> ClientRequest()
|
||||
{
|
||||
UpdateFolderResponseModel updateFolderModel = new() { Name = "TestUpdatedName" };
|
||||
|
||||
return await Client.PutAsync(Url, JsonContent.Create(updateFolderModel));
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType;
|
||||
|
||||
public class IsUsedDataTypeControllerTests : ManagementApiUserGroupTestBase<IsUsedDataTypeController>
|
||||
{
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private Guid _dataTypeKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var dataType = new DataTypeBuilder()
|
||||
.WithId(0)
|
||||
.WithName("Custom list view")
|
||||
.WithDatabaseType(ValueStorageType.Nvarchar)
|
||||
.AddEditor()
|
||||
.WithAlias(Constants.PropertyEditors.Aliases.ListView)
|
||||
.Done()
|
||||
.Build();
|
||||
var response = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
||||
|
||||
_dataTypeKey = response.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<IsUsedDataTypeController, object>> MethodSelector =>
|
||||
x => x.IsUsed(CancellationToken.None, _dataTypeKey);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType.Item;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType.Item;
|
||||
|
||||
public class ItemDatatypeItemControllerTests : ManagementApiUserGroupTestBase<ItemDatatypeItemController>
|
||||
{
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private Guid _dataTypeKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var dataType = new DataTypeBuilder()
|
||||
.WithId(0)
|
||||
.WithName("Custom list view")
|
||||
.WithDatabaseType(ValueStorageType.Nvarchar)
|
||||
.AddEditor()
|
||||
.WithAlias(Constants.PropertyEditors.Aliases.ListView)
|
||||
.Done()
|
||||
.Build();
|
||||
var response = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
||||
_dataTypeKey = response.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<ItemDatatypeItemController, object>> MethodSelector =>
|
||||
x => x.Item(CancellationToken.None, new HashSet<Guid> { _dataTypeKey });
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType;
|
||||
|
||||
public class MoveDataTypeControllerTests : ManagementApiUserGroupTestBase<MoveDataTypeController>
|
||||
{
|
||||
private IDataTypeContainerService DataTypeContainerService => GetRequiredService<IDataTypeContainerService>();
|
||||
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private Guid _dataTypeKey;
|
||||
private Guid _folderKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var responseFolder = await DataTypeContainerService.CreateAsync(Guid.NewGuid(), "TestFolder", Constants.System.RootKey, Constants.Security.SuperUserKey);
|
||||
_folderKey = responseFolder.Result.Key;
|
||||
|
||||
var dataType = new DataTypeBuilder()
|
||||
.WithId(0)
|
||||
.WithName("Custom list view")
|
||||
.WithDatabaseType(ValueStorageType.Nvarchar)
|
||||
.AddEditor()
|
||||
.WithAlias(Constants.PropertyEditors.Aliases.ListView)
|
||||
.Done()
|
||||
.Build();
|
||||
var responseDataType = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
||||
|
||||
_dataTypeKey = responseDataType.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<MoveDataTypeController, object>> MethodSelector =>
|
||||
x => x.Move(CancellationToken.None, _dataTypeKey, null);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
|
||||
protected override async Task<HttpResponseMessage> ClientRequest()
|
||||
{
|
||||
MoveDataTypeRequestModel moveDataTypeRequestModel = new() { Target = new ReferenceByIdModel(_folderKey) };
|
||||
|
||||
return await Client.PutAsync(Url, JsonContent.Create(moveDataTypeRequestModel));
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType;
|
||||
|
||||
public class ReferencesDataTypeControllerTests : ManagementApiUserGroupTestBase<ReferencesDataTypeController>
|
||||
{
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private Guid _dataTypeKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var dataType = new DataTypeBuilder()
|
||||
.WithId(0)
|
||||
.WithName("Custom list view")
|
||||
.WithDatabaseType(ValueStorageType.Nvarchar)
|
||||
.AddEditor()
|
||||
.WithAlias(Constants.PropertyEditors.Aliases.ListView)
|
||||
.Done()
|
||||
.Build();
|
||||
var response = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
||||
|
||||
_dataTypeKey = response.Result.Key;
|
||||
}
|
||||
|
||||
protected override Expression<Func<ReferencesDataTypeController, object>> MethodSelector =>
|
||||
x => x.References(CancellationToken.None, _dataTypeKey);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType.Tree;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType.Tree;
|
||||
|
||||
public class ChildrenDataTypeTreeControllerTests : ManagementApiUserGroupTestBase<ChildrenDataTypeTreeController>
|
||||
{
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private IDataTypeContainerService DataTypeContainerService => GetRequiredService<IDataTypeContainerService>();
|
||||
|
||||
private Guid _dataTypeFolderKey;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
// Folder
|
||||
_dataTypeFolderKey = Guid.NewGuid();
|
||||
await DataTypeContainerService.CreateAsync(_dataTypeFolderKey, Guid.NewGuid().ToString(), Constants.System.RootKey, Constants.Security.SuperUserKey);
|
||||
var createdFolder = await DataTypeContainerService.GetAsync(_dataTypeFolderKey);
|
||||
var folderId = createdFolder.Id;
|
||||
|
||||
// Datatype
|
||||
var dataType = new DataTypeBuilder()
|
||||
.WithId(0)
|
||||
.WithName("Custom list view")
|
||||
.WithDatabaseType(ValueStorageType.Nvarchar)
|
||||
.AddEditor()
|
||||
.WithAlias(Constants.PropertyEditors.Aliases.ListView)
|
||||
.Done()
|
||||
.WithParentId(folderId)
|
||||
.Build();
|
||||
await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
||||
}
|
||||
|
||||
protected override Expression<Func<ChildrenDataTypeTreeController, object>> MethodSelector =>
|
||||
x => x.Children(CancellationToken.None, _dataTypeFolderKey, 0, 100, false);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using Umbraco.Cms.Api.Management.Controllers.DataType.Tree;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.DataType.Tree;
|
||||
|
||||
public class RootDataTypeTreeControllerTests : ManagementApiUserGroupTestBase<RootDataTypeTreeController>
|
||||
{
|
||||
protected override Expression<Func<RootDataTypeTreeController, object>> MethodSelector =>
|
||||
x => x.Root(CancellationToken.None, 0, 100, false);
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Forbidden
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user