289 lines
13 KiB
PowerShell
289 lines
13 KiB
PowerShell
$ErrorActionPreference = "Stop"
|
|
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
$initArtifactsScript = Join-Path $root "scripts/wireframe/init-artifacts.ps1"
|
|
$extractScript = Join-Path $root "scripts/wireframe/extract-documents.ps1"
|
|
$validateScript = Join-Path $root "scripts/wireframe/validate-artifacts.ps1"
|
|
$initWorkspaceScript = Join-Path $root "scripts/system/init-workspace.ps1"
|
|
$exportUpdateScript = Join-Path $root "scripts/system/export-update-package.ps1"
|
|
$applyUpdateScript = Join-Path $root "scripts/system/apply-hot-update.ps1"
|
|
$newBugReportScript = Join-Path $root "scripts/system/new-bug-report.ps1"
|
|
$fixtureDocs = Join-Path $root "tests/fixtures/docs"
|
|
$fixtureArtifacts = Join-Path $root "tests/fixtures/artifacts"
|
|
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("wireframe-gen-test-" + [guid]::NewGuid().ToString())
|
|
|
|
function Copy-FixtureArtifacts {
|
|
param([string]$Destination)
|
|
New-Item -ItemType Directory -Force -Path $Destination | Out-Null
|
|
Get-ChildItem -LiteralPath $fixtureArtifacts -Force | ForEach-Object {
|
|
Copy-Item -LiteralPath $_.FullName -Destination $Destination -Recurse -Force
|
|
}
|
|
}
|
|
|
|
function Write-Blueprints {
|
|
param(
|
|
[object]$Blueprints,
|
|
[string]$ArtifactDir
|
|
)
|
|
$path = Join-Path $ArtifactDir "screen_blueprints.json"
|
|
$Blueprints | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $path -Encoding UTF8
|
|
}
|
|
|
|
function Write-NormalizedProject {
|
|
param(
|
|
[object]$Project,
|
|
[string]$ArtifactDir
|
|
)
|
|
$path = Join-Path $ArtifactDir "normalized_project.json"
|
|
$Project | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $path -Encoding UTF8
|
|
}
|
|
|
|
function Expect-ValidationFailure {
|
|
param(
|
|
[string]$Name,
|
|
[scriptblock]$Mutate,
|
|
[string]$Stage = "schema"
|
|
)
|
|
$caseDir = Join-Path $tempDir $Name
|
|
Copy-FixtureArtifacts -Destination $caseDir
|
|
& $Mutate $caseDir
|
|
$oldErrorActionPreference = $ErrorActionPreference
|
|
$ErrorActionPreference = "Continue"
|
|
try {
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $caseDir -Strict -Stage $Stage *> $null
|
|
$exitCode = $LASTEXITCODE
|
|
}
|
|
finally {
|
|
$ErrorActionPreference = $oldErrorActionPreference
|
|
}
|
|
if ($exitCode -eq 0) {
|
|
throw "Expected validation failure for $Name"
|
|
}
|
|
}
|
|
|
|
function Expect-ValidationSuccess {
|
|
param(
|
|
[string]$Name,
|
|
[scriptblock]$Mutate,
|
|
[string]$Stage = "schema"
|
|
)
|
|
$caseDir = Join-Path $tempDir $Name
|
|
Copy-FixtureArtifacts -Destination $caseDir
|
|
& $Mutate $caseDir
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $caseDir -Strict -Stage $Stage | Out-Null
|
|
}
|
|
|
|
function Assert-PathExists {
|
|
param([string]$Path)
|
|
if (-not (Test-Path -LiteralPath $Path)) {
|
|
throw "Expected path to exist: $Path"
|
|
}
|
|
}
|
|
|
|
function Assert-PathMissing {
|
|
param([string]$Path)
|
|
if (Test-Path -LiteralPath $Path) {
|
|
throw "Expected path to be absent: $Path"
|
|
}
|
|
}
|
|
|
|
function Assert-Equal {
|
|
param(
|
|
[object]$Actual,
|
|
[object]$Expected,
|
|
[string]$Message
|
|
)
|
|
if ($Actual -ne $Expected) {
|
|
throw "$Message Expected '$Expected', got '$Actual'"
|
|
}
|
|
}
|
|
|
|
function Copy-DirectoryContents {
|
|
param(
|
|
[string]$Source,
|
|
[string]$Destination
|
|
)
|
|
New-Item -ItemType Directory -Force -Path $Destination | Out-Null
|
|
Get-ChildItem -LiteralPath $Source -Force | ForEach-Object {
|
|
Copy-Item -LiteralPath $_.FullName -Destination $Destination -Recurse -Force
|
|
}
|
|
}
|
|
|
|
function Assert-PackageHasNoPreservedFiles {
|
|
param([object]$PackageManifest)
|
|
foreach ($file in @($PackageManifest.files)) {
|
|
$path = ([string]$file.path) -replace "\\", "/"
|
|
foreach ($blocked in @("workspace", "artifacts", "maintenance", "dist")) {
|
|
if ($path -eq $blocked -or $path.StartsWith("$blocked/")) {
|
|
throw "Package should not include preserved path: $path"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
$defaultCase = Join-Path $tempDir "default-paths"
|
|
New-Item -ItemType Directory -Force -Path $defaultCase | Out-Null
|
|
Push-Location $defaultCase
|
|
try {
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $initArtifactsScript | Out-Null
|
|
Assert-PathExists (Join-Path $defaultCase "workspace/artifacts/wireframe-gen/source_inventory.json")
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -Strict | Out-Null
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
|
|
$legacyArtifactDir = Join-Path $tempDir "legacy-artifact-dir"
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $initArtifactsScript -ArtifactDir $legacyArtifactDir | Out-Null
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $legacyArtifactDir -Strict | Out-Null
|
|
|
|
$legacySource = Join-Path $tempDir "legacy-source/artifacts/wireframe-gen"
|
|
Copy-FixtureArtifacts -Destination $legacySource
|
|
$workspaceRoot = Join-Path $tempDir "client-workspace"
|
|
$workspaceArtifacts = Join-Path $workspaceRoot "artifacts/wireframe-gen"
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $initWorkspaceScript -WorkspaceDir $workspaceRoot -ArtifactDir $workspaceArtifacts -LegacyArtifactDir $legacySource | Out-Null
|
|
Assert-PathExists (Join-Path $workspaceArtifacts "source_inventory.json")
|
|
Assert-PathExists (Join-Path $workspaceRoot "system-feedback/bug-reports")
|
|
Assert-PathExists (Join-Path $workspaceRoot ".hot-update/backups")
|
|
Assert-PathExists (Join-Path $legacySource "source_inventory.json")
|
|
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $extractScript -InputPath $fixtureDocs -OutputDir $tempDir | Out-Null
|
|
$inventoryPath = Join-Path $tempDir "source_inventory.json"
|
|
if (-not (Test-Path -LiteralPath $inventoryPath)) {
|
|
throw "source_inventory.json was not created"
|
|
}
|
|
|
|
$inventory = Get-Content -LiteralPath $inventoryPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
if (@($inventory.sources).Count -lt 2) {
|
|
throw "Expected at least 2 extracted fixture sources"
|
|
}
|
|
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $fixtureArtifacts -Strict | Out-Null
|
|
|
|
Expect-ValidationFailure "legacy-open-question-blocks-pre-figma" {
|
|
param($caseDir)
|
|
} -Stage "pre-figma"
|
|
|
|
Expect-ValidationSuccess "resolved-open-question-passes-pre-ux" {
|
|
param($caseDir)
|
|
} -Stage "pre-ux"
|
|
|
|
Expect-ValidationFailure "unresolved-open-question-blocks-pre-ux" {
|
|
param($caseDir)
|
|
$project = Get-Content -LiteralPath (Join-Path $caseDir "normalized_project.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$project.open_questions += [pscustomobject]@{
|
|
id = "Q-003"
|
|
question = "Which navigation model should be used?"
|
|
status = "unresolved"
|
|
blocks = @("ux-construction")
|
|
default_assumption = ""
|
|
answer = ""
|
|
answered_at = ""
|
|
source_refs = @()
|
|
}
|
|
Write-NormalizedProject -Project $project -ArtifactDir $caseDir
|
|
} -Stage "pre-ux"
|
|
|
|
Expect-ValidationFailure "bad-screen-width" {
|
|
param($caseDir)
|
|
$blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$blueprints[0].viewport.width = 1436
|
|
Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir
|
|
}
|
|
|
|
Expect-ValidationFailure "bad-screen-height" {
|
|
param($caseDir)
|
|
$blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$blueprints[0].viewport.height = 796
|
|
Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir
|
|
}
|
|
|
|
Expect-ValidationFailure "missing-element-parent" {
|
|
param($caseDir)
|
|
$blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$blueprints[1].PSObject.Properties.Remove("parent_screen_id")
|
|
Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir
|
|
}
|
|
|
|
Expect-ValidationFailure "fractional-spacing" {
|
|
param($caseDir)
|
|
$blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$blueprints[0].sections[0].padding = 32.5
|
|
Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir
|
|
}
|
|
|
|
Expect-ValidationFailure "bad-element-size" {
|
|
param($caseDir)
|
|
$blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$blueprints[1].bounds.width = 13
|
|
Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir
|
|
}
|
|
|
|
$packageOutput = Join-Path $tempDir "packages"
|
|
$exportOutput = powershell -NoProfile -ExecutionPolicy Bypass -File $exportUpdateScript -OutputDir $packageOutput
|
|
$packagePath = @($exportOutput)[-1]
|
|
Assert-PathExists $packagePath
|
|
$packageManifestPath = Join-Path $packagePath "package-manifest.json"
|
|
Assert-PathExists $packageManifestPath
|
|
$packageManifest = Get-Content -LiteralPath $packageManifestPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
Assert-PackageHasNoPreservedFiles -PackageManifest $packageManifest
|
|
|
|
$clientRoot = Join-Path $tempDir "client-copy"
|
|
Copy-DirectoryContents -Source $packagePath -Destination $clientRoot
|
|
$clientAgents = Join-Path $clientRoot "AGENTS.md"
|
|
Set-Content -LiteralPath $clientAgents -Value "old system instructions" -Encoding UTF8 -NoNewline
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $clientRoot "workspace/artifacts/wireframe-gen") | Out-Null
|
|
Set-Content -LiteralPath (Join-Path $clientRoot "workspace/artifacts/wireframe-gen/client-data.txt") -Value "do not touch" -Encoding UTF8 -NoNewline
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $clientRoot "maintenance") | Out-Null
|
|
Set-Content -LiteralPath (Join-Path $clientRoot "maintenance/origin-only.txt") -Value "do not touch" -Encoding UTF8 -NoNewline
|
|
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $applyUpdateScript -PackagePath $packagePath -TargetRoot $clientRoot -DryRun | Out-Null
|
|
Assert-Equal (Get-Content -LiteralPath $clientAgents -Raw -Encoding UTF8) "old system instructions" "Dry-run must not mutate system files."
|
|
Assert-PathMissing (Join-Path $clientRoot "workspace/.hot-update/last-apply.json")
|
|
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $applyUpdateScript -PackagePath $packagePath -TargetRoot $clientRoot | Out-Null
|
|
$sourceAgents = Get-Content -LiteralPath (Join-Path $packagePath "AGENTS.md") -Raw -Encoding UTF8
|
|
$updatedAgents = Get-Content -LiteralPath $clientAgents -Raw -Encoding UTF8
|
|
Assert-Equal $updatedAgents $sourceAgents "Hot Update must replace allowlisted system files."
|
|
Assert-Equal (Get-Content -LiteralPath (Join-Path $clientRoot "workspace/artifacts/wireframe-gen/client-data.txt") -Raw -Encoding UTF8) "do not touch" "Hot Update must preserve workspace files."
|
|
Assert-Equal (Get-Content -LiteralPath (Join-Path $clientRoot "maintenance/origin-only.txt") -Raw -Encoding UTF8) "do not touch" "Hot Update must preserve maintenance files."
|
|
Assert-PathExists (Join-Path $clientRoot "workspace/.hot-update/last-apply.json")
|
|
$backupAgents = Get-ChildItem -LiteralPath (Join-Path $clientRoot "workspace/.hot-update/backups") -Recurse -File -Filter "AGENTS.md" | Select-Object -First 1
|
|
if ($null -eq $backupAgents) {
|
|
throw "Expected Hot Update to create a backup for overwritten AGENTS.md"
|
|
}
|
|
Assert-Equal (Get-Content -LiteralPath $backupAgents.FullName -Raw -Encoding UTF8) "old system instructions" "Backup should contain the previous file content."
|
|
|
|
$bugWorkspace = Join-Path $tempDir "bug-workspace"
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File $newBugReportScript `
|
|
-WorkspaceDir $bugWorkspace `
|
|
-Title "Validation does not block bad screen" `
|
|
-Area "validation" `
|
|
-Expected "Validation fails when a screen is underspecified." `
|
|
-Actual "Validation passed." `
|
|
-TriggerContext "During sanitized fixture checks." `
|
|
-ReproSteps "Run validation; observe success" `
|
|
-Impact "Incorrect downstream wireframes" | Out-Null
|
|
|
|
$reportJsonPath = Get-ChildItem -LiteralPath (Join-Path $bugWorkspace "system-feedback/bug-reports") -Recurse -File -Filter "report.json" | Select-Object -First 1
|
|
if ($null -eq $reportJsonPath) {
|
|
throw "Expected bug report JSON to be created."
|
|
}
|
|
$report = Get-Content -LiteralPath $reportJsonPath.FullName -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
foreach ($field in @("id", "created_at", "system_version", "area", "severity", "trigger_context", "actual_behavior", "expected_behavior", "repro_steps", "impact", "workaround", "depersonalization_notes", "privacy_confirmed")) {
|
|
if (-not ($report.PSObject.Properties.Name -contains $field)) {
|
|
throw "Bug report missing required field: $field"
|
|
}
|
|
}
|
|
Assert-PathExists (Join-Path $reportJsonPath.Directory.FullName "report.md")
|
|
Assert-PathExists (Join-Path $reportJsonPath.Directory.FullName "sanitized-snippets")
|
|
|
|
Write-Output "Wireframe checks passed"
|
|
}
|
|
finally {
|
|
if (Test-Path -LiteralPath $tempDir) {
|
|
Remove-Item -LiteralPath $tempDir -Recurse -Force
|
|
}
|
|
}
|