Skip to content
221 changes: 134 additions & 87 deletions AppHandling/Run-AlPipeline.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@
Override function parameter for Get-BcContainerEventLog
.Parameter InstallMissingDependencies
Override function parameter for Installing missing dependencies
.Parameter RunPageScriptingTests
Override function parameter for Running Page Scripting Tests
.Example
Please visit https://www.freddysblog.com for descriptions
.Example
Expand Down Expand Up @@ -416,6 +418,7 @@ Param(
[scriptblock] $GetBestGenericImageName,
[scriptblock] $GetBcContainerEventLog,
[scriptblock] $InstallMissingDependencies,
[scriptblock] $RunPageScriptingTests,
[scriptblock] $PipelineFinalize
)

Expand Down Expand Up @@ -528,6 +531,110 @@ function GetInstalledApps {
Write-GroupEnd
}

function RunPageScriptingTests {
param(
[string] $containerName,
[PSCredential] $credential,
[array] $pageScriptingTests,
[array] $restoreDatabases,
[string] $pageScriptingTestResultsFile,
[string] $pageScriptingTestResultsFolder,
[string] $startAddress,
[scriptblock] $RestoreDatabasesInBcContainer,
[switch] $returnTrueIfAllPassed
)

# Install npm package for page scripting tests
pwsh -command { npm i @microsoft/[email protected] --save --silent }

${env:containerUsername} = $credential.UserName
${env:containerPassword} = $credential.Password | Get-PlainText

$allPassed = $true
$usedNames = @()

$pageScriptingTests | ForEach-Object {
$thisFailed = $false
if ($restoreDatabases -contains 'BeforeEachPageScriptingTest') {
Write-GroupStart -Message "Restoring databases before each page scripting test"
Invoke-Command -ScriptBlock $RestoreDatabasesInBcContainer -ArgumentList @{"containerName" = $containerName }
Write-GroupEnd
}
$testSpec = $_
$name = $testSpec -replace '[\\/]', '-' -replace ':', '' -replace '\*', 'all' -replace '\?', 'x' -replace '\.yml$', ''
if ($usedNames -contains $name) {
throw "PageScriptingTests contains two similar test specs (resulting in identical results folders), please rename your test specs ($testSpec)."
}
$usedNames += $name
$path = $testSpec
if (-not [System.IO.Path]::IsPathRooted($path)) { $path = Join-Path $baseFolder $path }
if (-not (Test-Path $path)) { throw "No page scripting tests found matching $testSpec" }
Write-Host "Running Page Scripting Tests for $testSpec (test name: $name)"
$resultsFolder = Join-Path $pageScriptingTestResultsFolder $name
New-Item -Path $resultsFolder -ItemType Directory | Out-Null
try {
pwsh -command {
param(
[string]$TestPath,
[string]$ResultsFolder,
[string]$StartAddress
)
Write-Host "Running: npx replay $TestPath -ResultDir $ResultsFolder -StartAddress $StartAddress -Authentication 'UserPassword' -usernameKey 'containerUsername' -passwordkey 'containerPassword'"
npx replay $TestPath -ResultDir $ResultsFolder -StartAddress $StartAddress -Authentication 'UserPassword' -usernameKey 'containerUsername' -passwordkey 'containerPassword'
} -args $path, $resultsFolder, $startAddress
if ($? -ne "True") {
Write-Host "Page Scripting Tests failed for $testSpec"
$thisFailed = $true
}
}
catch {
$thisFailed = $true
Write-Host -ForegroundColor Red "Page Scripting Tests failed for $testSpec : $($_.Exception.Message)"
}

if ($thisFailed) {
Write-Host "Page Scripting Tests failed for $testSpec"
$allPassed = $false
}
$testResultsFile = Join-Path $resultsFolder "results.xml"
$playwrightReportFolder = Join-Path $resultsFolder 'playwright-report'
if ((Test-Path $testResultsFile -PathType Leaf) -and (Test-Path $playwrightReportFolder -PathType Container)) {
$thisXml = [xml](Get-Content $testResultsFile -Encoding UTF8)
$thisXml.testsuites.testsuite.Name = $name
$resultsXml = $thisXml
if (Test-Path $pageScriptingTestResultsFile) {
# Merge results and aggregate counts
$resultsXml = [xml](Get-Content $pageScriptingTestResultsFile -Encoding UTF8)
$resultsXml.testsuites.AppendChild($resultsXml.ImportNode($thisXml.testsuites.testsuite, $true))
}
foreach ($property in 'tests', 'failures', 'skipped', 'errors', 'time') {
$resultsXml.testsuites."$property" = "$(([double[]]$resultsXml.testsuites.testsuite."$property" | Measure-Object -Sum).Sum)"
}
$resultsXml.Save($pageScriptingTestResultsFile)
Remove-Item $testResultsFile -Force
if ($thisFailed) {
Write-Host "Moving Playwright report folder"
Move-Item -Path "$playwrightReportFolder/*" -Destination $resultsFolder -Force
Write-Host "Removing Playwright report folder"
Remove-Item -Path $playwrightReportFolder -Force
}
else {
if (Test-Path $resultsFolder) {
Write-Host "Removing results folder"
Remove-Item -Path $resultsFolder -Recurse -Force -ErrorAction SilentlyContinue
}
else {
Write-Host "Results folder $resultsFolder not found"
}
}
}
}

if ($returnTrueIfAllPassed) {
return $allPassed
}
}

$script:existingContainerName = ''
$script:existingCompilerFolder = ''

Expand Down Expand Up @@ -838,7 +945,10 @@ if ($bcptTestFolders) {

$artifactUrl = ""
$filesOnly = $false
if ($bcAuthContext) {
$IsBcSaaSInfrastructure = $bcAuthContext -and $bcAuthContext -is [Hashtable] -and $bcAuthContext.ContainsKey('scopes') -and $bcAuthContext.scopes -like "https://projectmadeira.com/*"

if ($IsBcSaaSInfrastructure) {
Write-Host "Using BC SaaS Infrastructure. Test for feature compatibility."
if ("$environment" -eq "") {
throw "When specifying bcAuthContext, you also have to specify the name of the pre-setup online environment to use."
}
Expand Down Expand Up @@ -1263,6 +1373,11 @@ else {
if ($InstallMissingDependencies) {
Write-Host -ForegroundColor Yellow "InstallMissingDependencies override"; Write-Host $InstallMissingDependencies.ToString()
}
if ($RunPageScriptingTests) {
Write-Host -ForegroundColor Yellow "RunPageScriptingTests override"; Write-Host $RunPageScriptingTests.ToString()
} else {
$RunPageScriptingTests = { Param([Hashtable]$parameters) RunPageScriptingTests @parameters }
}
Write-GroupEnd

$signApps = ($codeSignCertPfxFile -ne "")
Expand Down Expand Up @@ -2878,7 +2993,7 @@ if ($buildArtifactFolder -and (Test-Path $bcptResultsFile)) {
Write-GroupEnd
}

if ($createContainer -and !$doNotRunPageScriptingTests -and $pageScriptingTests -and $pageScriptingTestResultsFolder -and $pageScriptingTestResultsFile) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you not check that a container is created when restoring the database in a container?
Else, I assume this could fail with CompilerFolder running

Copy link
Contributor Author

@ChrisBlankDe ChrisBlankDe Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because you use CompilerFolder doesn't necessarily mean you don't have a container.
That's only the case if you set doNotPublishApps at the same time. But if you set this, doNotRunPageScriptingTests is automatically set to true. That would make it OK again, right?

if (!$doNotRunPageScriptingTests -and $pageScriptingTests -and $pageScriptingTestResultsFolder -and $pageScriptingTestResultsFile) {
if ($restoreDatabases -contains 'BeforePageScriptingTests' -and $restoreDatabases -notcontains 'BeforeEachPageScriptingTest') {
Write-GroupStart -Message "Restoring databases before page scripting tests"
Invoke-Command -ScriptBlock $RestoreDatabasesInBcContainer -ArgumentList @{"containerName" = (GetBuildContainer)}
Expand All @@ -2899,92 +3014,24 @@ Measure-Command {
if ($testCountry) {
Write-Host -ForegroundColor Yellow "Running Page Scripting Tests for additional country $testCountry"
}

$containerName = (GetBuildContainer)

# Install npm package for page scripting tests
pwsh -command { npm i @microsoft/[email protected] --save --silent }

${env:containerUsername} = $credential.UserName
${env:containerPassword} = $credential.Password | Get-PlainText
$startAddress = "http://$containerName/BC?tenant=$tenant"

$usedNames = @()

$pageScriptingTests | ForEach-Object {
$thisFailed = $false
if ($restoreDatabases -contains 'BeforeEachPageScriptingTest') {
Write-GroupStart -Message "Restoring databases before each page scripting test"
Invoke-Command -ScriptBlock $RestoreDatabasesInBcContainer -ArgumentList @{"containerName" = $containerName}
Write-GroupEnd
}
$testSpec = $_
$name = $testSpec -replace '[\\/]', '-' -replace ':', '' -replace '\*', 'all' -replace '\?', 'x' -replace '\.yml$', ''
if ($usedNames -contains $name) {
throw "PageScriptingTests contains two similar test specs (resulting in identical results folders), please rename your test specs ($testSpec)."
}
$usedNames += $name
$path = $testSpec
if (-not [System.IO.Path]::IsPathRooted($path)) { $path = Join-Path $baseFolder $path }
if (-not (Test-Path $path)) { throw "No page scripting tests found matching $testSpec" }
Write-Host "Running Page Scripting Tests for $testSpec (test name: $name)"
$resultsFolder = Join-Path $pageScriptingTestResultsFolder $name
New-Item -Path $resultsFolder -ItemType Directory | Out-Null
try {
pwsh -command {
param(
[string]$TestPath,
[string]$ResultsFolder,
[string]$StartAddress
)
Write-Host "Running: npx replay $TestPath -ResultDir $ResultsFolder -StartAddress $StartAddress -Authentication 'UserPassword' -usernameKey 'containerUsername' -passwordkey 'containerPassword'"
npx replay $TestPath -ResultDir $ResultsFolder -StartAddress $StartAddress -Authentication 'UserPassword' -usernameKey 'containerUsername' -passwordkey 'containerPassword'
} -args $path, $resultsFolder, $startAddress
if ($? -ne "True") {
Write-Host "Page Scripting Tests failed for $testSpec"
$thisFailed = $true
}
} catch {
$thisFailed = $true
Write-Host -ForegroundColor Red "Page Scripting Tests failed for $testSpec : $($_.Exception.Message)"
}

if ($thisFailed) {
Write-Host "Page Scripting Tests failed for $testSpec"
$allPassed = $false
}
$testResultsFile = Join-Path $resultsFolder "results.xml"
$playwrightReportFolder = Join-Path $resultsFolder 'playwright-report'
if ((Test-Path $testResultsFile -PathType Leaf) -and (Test-Path $playwrightReportFolder -PathType Container)) {
$thisXml = [xml](Get-Content $testResultsFile -encoding UTF8)
$thisXml.testsuites.testsuite.Name = $name
$resultsXml = $thisXml
if (Test-Path $pageScriptingTestResultsFile) {
# Merge results and aggregate counts
$resultsXml = [xml](Get-Content $pageScriptingTestResultsFile -encoding UTF8)
$resultsXml.testsuites.AppendChild($resultsXml.ImportNode($thisXml.testsuites.testsuite, $true))
}
foreach($property in 'tests','failures','skipped','errors','time') {
$resultsXml.testsuites."$property" = "$(([double[]]$resultsXml.testsuites.testsuite."$property" | Measure-Object -Sum).Sum)"
}
$resultsXml.Save($pageScriptingTestResultsFile)
Remove-Item $testResultsFile -Force
if ($thisFailed) {
Write-Host "Moving Playwright report folder"
Move-Item -Path "$playwrightReportFolder/*" -Destination $resultsFolder -Force
Write-Host "Removing Playwright report folder"
Remove-Item -Path $playwrightReportFolder -Force
}
else {
if (Test-Path $resultsFolder) {
Write-Host "Removing results folder"
Remove-Item -Path $resultsFolder -Recurse -Force -ErrorAction SilentlyContinue
} else {
Write-Host "Results folder $resultsFolder not found"
}
}
}
$Parameters = @{
"containerName" = (GetBuildContainer)
"credential" = $credential
"pageScriptingTests" = $pageScriptingTests
"restoreDatabases" = $restoreDatabases
"pageScriptingTestResultsFile" = $pageScriptingTestResultsFile
"pageScriptingTestResultsFolder" = $pageScriptingTestResultsFolder
"startAddress" = "http://$containerName/BC?tenant=$tenant"
"RestoreDatabasesInBcContainer" = $RestoreDatabasesInBcContainer
"returnTrueIfAllPassed" = $true
}
$result = Invoke-Command -ScriptBlock $RunPageScriptingTests -ArgumentList $Parameters
if ($result -is [array]) {
$allPassed = $allPassed -and $result[$result.Count-1]
}
else {
$allPassed = $allPassed -and $result
}
} | ForEach-Object { Write-Host -ForegroundColor Yellow "`nRunning Page Scripting Tests took $([int]$_.TotalSeconds) seconds" }
Write-GroupEnd
}
Expand Down
1 change: 1 addition & 0 deletions ReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
6.1.11
Temporarily avoid using dotnet 10 for assemblyProbingPaths
Improve SaaS Environment recognition and allow overwriting Page Scripting Test execution in Run-AlPipeline
Issue 3986 Endless loop when errors occur during the execution of AL tests with external environments
Issue 3986 Execution of AL tests fails for test codeunits without test functions

Expand Down