330 lines
No EOL
13 KiB
TypeScript
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}*`)
|
|
})
|
|
}) |