From d654d3ee71256005443d9c82dee42b716c702ddb Mon Sep 17 00:00:00 2001 From: Developer-Atlas Date: Wed, 22 Jul 2026 15:15:43 +0200 Subject: [PATCH] Fix Control Lite CI assertions --- scripts/validate-control-example.mjs | 15 +++++++++++---- tests/control-lite.spec.mjs | 16 +++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/scripts/validate-control-example.mjs b/scripts/validate-control-example.mjs index 8967205..0959562 100644 --- a/scripts/validate-control-example.mjs +++ b/scripts/validate-control-example.mjs @@ -28,7 +28,7 @@ for (const file of allowed) { if (!changed.includes(file)) failures.push(`Allowed file is absent from patch: ${file}`) } -const checkedDecisions = [...decision.matchAll(/^- \[x\] (Accept|Revise|Reject)$/gmi)].map((m) => m[1]) +const checkedDecisions = [...decision.matchAll(/^- \[x\] (Accept|Revise|Reject)$/gmi)].map((match) => match[1]) if (checkedDecisions.length !== 1) { failures.push(`Decision record must select exactly one outcome; found ${checkedDecisions.length}.`) } @@ -47,6 +47,8 @@ if (selected === 'revise' || selected === 'reject') { } const parked = section(instruction, 'Parked ideas') +if (!parked.trim()) failures.push('Instruction must declare a non-empty Parked ideas section.') + const parkedTerms = ['color', 'polling', 'api endpoint', 'dashboard', 'deployment integration'] for (const term of parkedTerms) { if (parked.toLowerCase().includes(term) && addedLines.toLowerCase().includes(term)) { @@ -76,9 +78,14 @@ function read(filename) { } function section(markdown, heading) { - const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - const match = markdown.match(new RegExp(`^##\\s+${escaped}\\s*$([\\s\\S]*?)(?=^##\\s+|\\Z)`, 'mi')) - return match?.[1] ?? '' + const lines = markdown.split(/\r?\n/) + const expectedHeading = `## ${heading}`.toLowerCase() + const start = lines.findIndex((line) => line.trim().toLowerCase() === expectedHeading) + if (start === -1) return '' + + const relativeEnd = lines.slice(start + 1).findIndex((line) => /^##\s+/.test(line.trim())) + const end = relativeEnd === -1 ? lines.length : start + 1 + relativeEnd + return lines.slice(start + 1, end).join('\n') } function extractBacktickList(markdown) { diff --git a/tests/control-lite.spec.mjs b/tests/control-lite.spec.mjs index ecd40c1..f290b9d 100644 --- a/tests/control-lite.spec.mjs +++ b/tests/control-lite.spec.mjs @@ -6,16 +6,16 @@ test('generates four reviewable Atlas artifacts locally', async ({ page }) => { const artifact = page.locator('.artifact') await expect(artifact).toHaveCount(4) - await expect(artifact.nth(0).locator('textarea')).toContainText('# Atlas Change Contract') - await expect(artifact.nth(1).locator('textarea')).toContainText('Report every check as Passed, Failed, or Not run') - await expect(artifact.nth(2).locator('textarea')).toContainText('**Not run**') - await expect(artifact.nth(3).locator('textarea')).toContainText('- [ ] Accept') + await expect(artifact.nth(0).locator('textarea')).toHaveValue(/# Atlas Change Contract/) + await expect(artifact.nth(1).locator('textarea')).toHaveValue(/Report every check as Passed, Failed, or Not run/) + await expect(artifact.nth(2).locator('textarea')).toHaveValue(/\*\*Not run\*\*/) + await expect(artifact.nth(3).locator('textarea')).toHaveValue(/- \[ \] Accept/) }) test('renders user-entered content as text inside generated markdown', async ({ page }) => { await page.goto('/control-lite/') - const hostile = `` - await page.locator('#goal').fill(hostile) + const unsafeLookingText = `` + await page.locator('#goal').fill(unsafeLookingText) let dialogOpened = false page.on('dialog', async (dialog) => { @@ -24,7 +24,9 @@ test('renders user-entered content as text inside generated markdown', async ({ }) await page.getByRole('button', { name: 'Generate Atlas artifacts' }).click() - await expect(page.locator('.artifact').first().locator('textarea')).toContainText(hostile) + + const generatedValue = await page.locator('.artifact').first().locator('textarea').inputValue() + expect(generatedValue).toContain(unsafeLookingText) await expect(page.locator('.artifact img')).toHaveCount(0) expect(dialogOpened).toBe(false) })