app/tests/e2e/conflict-resolution-parity.spec.ts

330 lines
No EOL
13 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { testFormData, ConflictResolutionFormHelper, MarkdownValidator } from './conflict-resolution-utils'
test.describe('Conflict Resolution Framework - Form to Markdown Parity', () => {
test('Complete form fill and validate 100% parity with markdown output', async ({ page }) => {
const formHelper = new ConflictResolutionFormHelper(page)
// Navigate to the form
await formHelper.goto()
await expect(page.locator('h1:has-text("CONFLICT RESOLUTION FRAMEWORK")').first()).toBeVisible()
// Fill all form sections systematically
await test.step('Fill basic organization information', async () => {
await formHelper.fillBasicInfo(testFormData)
})
await test.step('Fill core values section', async () => {
await formHelper.fillCoreValues(testFormData)
})
await test.step('Fill conflict types section', async () => {
await formHelper.fillConflictTypes(testFormData)
})
await test.step('Fill resolution approach section', async () => {
await formHelper.fillApproach(testFormData)
})
await test.step('Fill report receivers section', async () => {
await formHelper.fillReportReceivers(testFormData)
})
await test.step('Fill mediator structure section', async () => {
await formHelper.fillMediatorStructure(testFormData)
})
await test.step('Fill process steps section', async () => {
await formHelper.fillProcessSteps(testFormData)
})
await test.step('Fill timeline section', async () => {
await formHelper.fillTimeline(testFormData)
})
await test.step('Fill available actions section', async () => {
await formHelper.fillAvailableActions(testFormData)
})
await test.step('Fill documentation section', async () => {
await formHelper.fillDocumentation(testFormData)
})
await test.step('Fill implementation section', async () => {
await formHelper.fillImplementation(testFormData)
})
await test.step('Fill enhanced sections', async () => {
await formHelper.fillEnhancedSections(testFormData)
})
// Wait for auto-save to complete
await page.waitForTimeout(1000)
// Validate form completion
await test.step('Validate form completion', async () => {
await page.locator('button:has-text("CHECK")').click()
// Should see success message
await expect(page.locator('text=Form is complete')).toBeVisible({ timeout: 5000 })
})
// Generate and download markdown
const markdownContent = await test.step('Download markdown', async () => {
return await formHelper.downloadMarkdown()
})
// Validate markdown content against form data
await test.step('Validate markdown parity', async () => {
const validator = new MarkdownValidator(markdownContent)
const errors = validator.validateAll(testFormData)
if (errors.length > 0) {
console.log('Markdown content:', markdownContent.substring(0, 1000) + '...')
console.log('Validation errors:', errors)
}
expect(errors).toEqual([])
})
// Additional specific validations
await test.step('Validate document structure', async () => {
expect(markdownContent).toContain('# Test Cooperative Solutions Conflict Resolution Policy')
expect(markdownContent).toContain('## Purpose')
expect(markdownContent).toContain('## Who does this policy apply to?')
expect(markdownContent).toContain('## What policy should be used?')
expect(markdownContent).toContain('## Guiding principles')
expect(markdownContent).toContain('## Definitions')
expect(markdownContent).toContain('## Responsibility for implementation')
expect(markdownContent).toContain('## Procedures')
expect(markdownContent).toContain('### Reflection')
expect(markdownContent).toContain('## Direct Resolution')
expect(markdownContent).toContain('## Assisted Resolution')
expect(markdownContent).toContain('### Formal Complaints')
expect(markdownContent).toContain('## Other Redress')
expect(markdownContent).toContain('## Acknowledgments')
})
await test.step('Validate data integrity', async () => {
// Check that no placeholder text remains
expect(markdownContent).not.toContain('[Organization Name]')
expect(markdownContent).not.toContain('[Date]')
expect(markdownContent).not.toContain('[Not specified]')
expect(markdownContent).not.toContain('[None selected]')
// Check proper possessive forms
expect(markdownContent).toContain("Test Cooperative Solutions'")
expect(markdownContent).not.toContain("Test Cooperative Solutions's")
// Check no redundant text
expect(markdownContent).not.toContain('within within')
expect(markdownContent).not.toContain('members members')
})
})
test('Validate organization type variations', async ({ page }) => {
const formHelper = new ConflictResolutionFormHelper(page)
for (const orgType of ['Worker Cooperative', 'Consumer Cooperative', 'Nonprofit Organization', 'Social Enterprise']) {
await test.step(`Test organization type: ${orgType}`, async () => {
await formHelper.goto()
// Fill minimal data with specific org type
await page.fill('input[placeholder*="organization name"]', `Test ${orgType}`)
const orgTypeButton = page.locator('button:has-text("Select organization type"), [role="combobox"]:has-text("Select organization type")').first()
await orgTypeButton.click()
await page.locator(`text="${orgType}"`).click()
await page.fill('input[type="number"]', '5')
// Select one conflict type to make form valid
await page.locator('label:has-text("Financial disagreements") input[type="checkbox"]').check()
// Check validation
await page.locator('button:has-text("CHECK")').click()
await expect(page.locator('text=Form is complete')).toBeVisible({ timeout: 5000 })
// Download and validate
const markdown = await formHelper.downloadMarkdown()
// Validate org type specific content
if (orgType.includes('Cooperative')) {
expect(markdown).toContain('members')
expect(markdown).toContain('Directors, staff, members')
} else {
expect(markdown).toContain('community members')
expect(markdown).toContain('Directors, staff, community members')
}
// Check possessive handling
expect(markdown).toContain(`Test ${orgType}`)
expect(markdown).not.toContain(`Test ${orgType}'s's`)
})
}
})
test('Validate checkbox selections integrity', async ({ page }) => {
const formHelper = new ConflictResolutionFormHelper(page)
await formHelper.goto()
// Fill basic info
await page.fill('input[placeholder*="organization name"]', 'Checkbox Test Org')
const orgTypeButton = page.locator('button:has-text("Select organization type"), [role="combobox"]:has-text("Select organization type")').first()
await orgTypeButton.click()
await page.locator('text="Worker Cooperative"').click()
await page.fill('input[type="number"]', '8')
// Test specific checkbox combinations
const checkboxTests = [
{
section: 'Core Values',
items: ['Mutual Care', 'Anti-Oppression', 'Collective Liberation'],
shouldFind: ['Mutual Care', 'Anti-Oppression', 'Collective Liberation']
},
{
section: 'Conflict Types',
items: ['Code of Conduct violations', 'Harassment or discrimination', 'Conflicts of interest'],
shouldFind: ['Code of Conduct violations', 'Harassment or discrimination', 'Conflicts of interest']
},
{
section: 'Available Actions',
items: ['Verbal warning', 'Mediation facilitation', 'Removal from organization'],
shouldFind: ['Verbal warning', 'Mediation facilitation', 'Removal from organization']
}
]
for (const test of checkboxTests) {
await test.step(`Test ${test.section} checkboxes`, async () => {
// Clear any existing selections first
for (const item of test.items) {
const checkbox = page.locator(`label:has-text("${item}") input[type="checkbox"]`)
if (await checkbox.isChecked()) {
await checkbox.uncheck()
}
}
// Select specific items
for (const item of test.items) {
await page.locator(`label:has-text("${item}") input[type="checkbox"]`).check()
}
// Download markdown
const markdown = await formHelper.downloadMarkdown()
// Validate each selected item appears
for (const expectedItem of test.shouldFind) {
expect(markdown).toContain(expectedItem)
}
})
}
})
test('Validate toggle sections functionality', async ({ page }) => {
const formHelper = new ConflictResolutionFormHelper(page)
await formHelper.goto()
// Fill basic required fields
await page.fill('input[placeholder*="organization name"]', 'Toggle Test Org')
const orgTypeButton = page.locator('button:has-text("Select organization type"), [role="combobox"]:has-text("Select organization type")').first()
await orgTypeButton.click()
await page.locator('text="Nonprofit Organization"').click()
await page.fill('input[type="number"]', '6')
await page.locator('label:has-text("Financial disagreements") input[type="checkbox"]').check()
// Test toggleable sections
const toggleSections = [
{ name: 'Reflection', content: 'reflection process' },
{ name: 'Direct Resolution', content: 'escalate the bandwidth' },
{ name: 'External Resources', content: 'Human Rights Commission' }
]
for (const section of toggleSections) {
await test.step(`Test ${section.name} section toggle`, async () => {
// Find and enable toggle
const toggle = page.locator(`.toggle:near(:text("${section.name}"))`).first()
if (await toggle.isVisible()) {
await toggle.click()
await page.waitForTimeout(500) // Wait for UI update
}
// Download markdown
const markdown = await formHelper.downloadMarkdown()
// Section should be included when toggled on
expect(markdown.toLowerCase()).toContain(section.content.toLowerCase())
// Toggle off and test again
if (await toggle.isVisible()) {
await toggle.click()
await page.waitForTimeout(500)
}
const markdownOff = await formHelper.downloadMarkdown()
// For some sections, content might still appear in different contexts
// So we check for section-specific markers
if (section.name === 'Reflection') {
expect(markdownOff).not.toContain('### Reflection')
}
})
}
})
test('Validate form validation prevents incomplete exports', async ({ page }) => {
const formHelper = new ConflictResolutionFormHelper(page)
await formHelper.goto()
// Test with minimal/incomplete data
await page.fill('input[placeholder*="organization name"]', 'Incomplete Org')
// Deliberately don't fill other required fields
// Try validation
await page.locator('button:has-text("CHECK")').click()
// Should see error message
await expect(page.locator(':has-text("complete")', { timeout: 5000 })).toBeVisible()
await expect(page.locator(':has-text("required")')).toBeVisible()
// Now complete required fields
const orgTypeButton = page.locator('button:has-text("Select organization type"), [role="combobox"]:has-text("Select organization type")').first()
await orgTypeButton.click()
await page.locator('text="Social Enterprise"').click()
await page.fill('input[type="number"]', '3')
await page.locator('label:has-text("Financial disagreements") input[type="checkbox"]').check()
// Try validation again
await page.locator('button:has-text("CHECK")').click()
await expect(page.locator('text=Form is complete')).toBeVisible({ timeout: 5000 })
})
test('Validate date handling and formatting', async ({ page }) => {
const formHelper = new ConflictResolutionFormHelper(page)
await formHelper.goto()
// Fill basic info
await page.fill('input[placeholder*="organization name"]', 'Date Test Org')
const orgTypeButton = page.locator('button:has-text("Select organization type"), [role="combobox"]:has-text("Select organization type")').first()
await orgTypeButton.click()
await page.locator('text="Worker Cooperative"').click()
await page.fill('input[type="number"]', '4')
await page.locator('label:has-text("Financial disagreements") input[type="checkbox"]').check()
// Fill specific dates
const testDates = {
created: '2024-01-15',
review: '2025-01-15'
}
await page.fill('input[type="date"]:first-of-type', testDates.created)
await page.fill('input[type="date"]:last-of-type', testDates.review)
// Download and validate
const markdown = await formHelper.downloadMarkdown()
expect(markdown).toContain(testDates.created)
expect(markdown).toContain(testDates.review)
// Check proper date formatting in context
expect(markdown).toContain(`*This policy was created on ${testDates.created}`)
expect(markdown).toContain(`*Next review date: ${testDates.review}*`)
})
})