This commit is contained in:
Ân Đoàn 2026-06-03 07:44:52 -07:00 • committed by GitHub
commit ace0269f1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 290 additions and 0 deletions

View file

@ -0,0 +1,82 @@
# Mantine Migration QA Checklist
## Visual Coherence
- [ ] All components maintain consistent styling with existing design system
- [ ] Typography hierarchy is preserved across all pages
- [ ] Color palette matches brand guidelines
- [ ] Spacing and alignment conform to design specifications
- [ ] Responsive behavior works correctly on all screen sizes
- [ ] Dark mode/light mode themes are properly implemented
- [ ] Component states (hover, focus, active) display correctly
- [ ] No visual regressions compared to previous implementation
## Accessibility
- [ ] All interactive elements have proper focus indicators
- [ ] Keyboard navigation works seamlessly
- [ ] ARIA labels and roles are correctly applied
- [ ] Color contrast meets WCAG 2.1 AA standards
- [ ] Screen reader compatibility verified
- [ ] Form elements have proper labeling
- [ ] Alt text provided for all relevant images
- [ ] No accessibility violations in automated testing tools
## Performance
- [ ] Page load times are within acceptable thresholds
- [ ] Bundle size does not exceed previous implementation
- [ ] No new console errors or warnings
- [ ] Memory usage remains stable during extended use
- [ ] Component rendering performance is optimized
- [ ] Lazy loading implemented where appropriate
- [ ] Caching strategies are effective
- [ ] Third-party dependencies do not impact performance negatively
## Cleanup Verification
- [ ] Deprecated code and components are removed
- [ ] Unused CSS classes and styles are eliminated
- [ ] Old component libraries are fully uninstalled
- [ ] Configuration files updated to reflect new setup
- [ ] Documentation updated to match current implementation
- [ ] Test suites pass with new components
- [ ] No breaking changes introduced to public APIs
- [ ] Migration scripts execute successfully
## Cross-Browser Compatibility
- [ ] Chrome - latest version
- [ ] Firefox - latest version
- [ ] Safari - latest version
- [ ] Edge - latest version
- [ ] Mobile browsers (iOS Safari, Android Chrome)
## Integration Testing
- [ ] Authentication flows work correctly
- [ ] Form submissions process without errors
- [ ] API integrations maintain functionality
- [ ] State management behaves as expected
- [ ] Third-party service integrations remain stable
## User Acceptance Testing
- [ ] Core user workflows function as expected
- [ ] Edge cases handled appropriately
- [ ] Error states display helpful messages
- [ ] User feedback incorporated where applicable
## Documentation
- [ ] Component usage documentation updated
- [ ] Migration guide completed
- [ ] Code examples reflect current implementation
- [ ] Troubleshooting section includes common issues
## Final Verification
- [ ] Stakeholder review completed
- [ ] All checklist items addressed
- [ ] Sign-off obtained from product team
- [ ] Release notes prepared

View file

@ -0,0 +1,87 @@
import fs from 'fs/promises';
import path from 'path';
import { glob } from 'glob';
interface AnalysisResult {
unusedDependencies: string[];
unusedCssVariables: string[];
deadStyles: string[];
}
async function analyzeDependencies(): Promise<AnalysisResult> {
const result: AnalysisResult = {
unusedDependencies: [],
unusedCssVariables: [],
deadStyles: []
};
try {
// Read package.json
const packageJson = JSON.parse(await fs.readFile('package.json', 'utf-8'));
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
// Check for unused shadcn/Radix dependencies
const shadcnDeps = Object.keys(dependencies).filter(dep =>
dep.includes('shadcn') || dep.includes('@radix-ui')
);
if (shadcnDeps.length > 0) {
result.unusedDependencies = shadcnDeps;
}
// Analyze CSS files for unused variables
const cssFiles = await glob('src/**/*.css');
for (const file of cssFiles) {
const content = await fs.readFile(file, 'utf-8');
// Find CSS variables
const varMatches = content.match(/--[^:)]+:/g);
if (varMatches) {
result.unusedCssVariables.push(...varMatches.map(m => m.slice(0, -1)));
}
}
// Analyze for dead styles
const tsFiles = await glob('src/**/*.{ts,tsx}');
for (const file of tsFiles) {
const content = await fs.readFile(file, 'utf-8');
// Look for classNames that might be dead styles
const classMatches = content.match(/className="[^"]*"/g);
if (classMatches) {
result.deadStyles.push(...classMatches);
}
}
return result;
} catch (error) {
console.error('Analysis failed:', error);
return result;
}
}
async function main() {
console.log('Starting cleanup analysis...');
const results = await analyzeDependencies();
console.log('\n=== Cleanup Analysis Results ===');
console.log('\nUnused Dependencies:');
results.unusedDependencies.forEach(dep => console.log(` - ${dep}`));
console.log('\nPotential Unused CSS Variables:');
results.unusedCssVariables.forEach(variable => console.log(` - ${variable}`));
console.log('\nPotential Dead Styles:');
results.deadStyles.forEach(style => console.log(` - ${style}`));
console.log('\nAnalysis complete.');
}
if (require.main === module) {
main();
}
export { analyzeDependencies, AnalysisResult };

View file

@ -0,0 +1,77 @@
import { test, expect } from '@playwright/test';
import { AxeBuilder } from '@axe-core/playwright';
test.describe('Accessibility Quality Control', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('should have no automatically detectable accessibility issues', async ({ page }) => {
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
test('should have proper keyboard navigation', async ({ page }) => {
// Check focus management on interactive elements
await page.keyboard.press('Tab');
const firstFocusable = await page.locator('*:focus');
await expect(firstFocusable).toBeVisible();
// Check that focus moves through interactive elements
await page.keyboard.press('Tab');
const secondFocusable = await page.locator('*:focus');
await expect(secondFocusable).toBeVisible();
});
test('should have sufficient color contrast', async ({ page }) => {
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.include(['.text-primary', '.bg-primary', 'button', 'a'])
.analyze();
const contrastViolations = accessibilityScanResults.violations.filter(
violation => violation.id === 'color-contrast'
);
expect(contrastViolations).toEqual([]);
});
test('should have proper focus indicators', async ({ page }) => {
await page.keyboard.press('Tab');
const focusedElement = await page.locator('*:focus');
// Check that focused element has visible focus indicator
const outlineWidth = await focusedElement.evaluate(el => {
return window.getComputedStyle(el).outlineWidth;
});
expect(outlineWidth).not.toBe('0px');
});
test('should have accessible labels for form elements', async ({ page }) => {
const accessibilityScanResults = await new AxeBuilder({ page })
.withRules(['label'])
.analyze();
const labelViolations = accessibilityScanResults.violations.filter(
violation => violation.id === 'label'
);
expect(labelViolations).toEqual([]);
});
test('should have proper heading structure', async ({ page }) => {
const accessibilityScanResults = await new AxeBuilder({ page })
.withRules(['heading-order'])
.analyze();
const headingViolations = accessibilityScanResults.violations.filter(
violation => violation.id === 'heading-order'
);
expect(headingViolations).toEqual([]);
});
});

View file

@ -0,0 +1,44 @@
import { test, expect } from '@playwright/test';
test.describe('Visual Quality Control Tests', () => {
[
{ name: 'Board View', path: '/' },
{ name: 'Task Detail', path: '/task/1' },
{ name: 'Create Task', path: '/create-task' },
].forEach(({ name, path }) => {
['light', 'dark'].forEach((theme) => {
['desktop', 'tablet', 'mobile'].forEach((viewport) => {
test(`${name} - ${theme} mode - ${viewport}`, async ({ page }) => {
// Set viewport
switch (viewport) {
case 'mobile':
await page.setViewportSize({ width: 375, height: 667 });
break;
case 'tablet':
await page.setViewportSize({ width: 768, height: 1024 });
break;
default: // desktop
await page.setViewportSize({ width: 1280, height: 800 });
}
// Set theme
await page.addInitScript((theme) => {
window.localStorage.setItem('theme', theme);
}, theme);
// Navigate to page
await page.goto(path);
// Wait for page to load
await page.waitForLoadState('networkidle');
// Take screenshot
await expect(page).toHaveScreenshot({
fullPage: true,
maxDiffPixelRatio: 0.01
});
});
});
});
});
});