Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions scripts/validate-control-example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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}.`)
}
Expand All @@ -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)) {
Expand Down Expand Up @@ -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) {
Expand Down
16 changes: 9 additions & 7 deletions tests/control-lite.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `<img src=x onerror="alert('test')">`
await page.locator('#goal').fill(hostile)
const unsafeLookingText = `<img src=x onerror="alert('test')">`
await page.locator('#goal').fill(unsafeLookingText)

let dialogOpened = false
page.on('dialog', async (dialog) => {
Expand All @@ -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)
})
Loading