diff --git a/README.md b/README.md index 99db6e65..f0e52938 100644 --- a/README.md +++ b/README.md @@ -287,3 +287,16 @@ We welcome contributions! Please read our [Contributing Guide](./CONTRIBUTING.md Made with โค๏ธ by the PropChain Team + +## ๐Ÿ› ๏ธ Local Development Guardrails (Git Hooks) + +To maximize code reliability and streamline PR review cycles, this project uses **Husky** to enforce local quality validation checks prior to remote integration. + +### Active Git Hook Safeguards +* **Pre-Commit Hook:** Triggered automatically upon running `git commit`. Performs light syntax linting on modified files. +* **Pre-Push Hook:** Triggered automatically when executing `git push`. This gate forces an application-wide compile verification check (`tsc --noEmit`) and runs all matching unit tests. If compilation faults are surfaced or unit assertions fail, the push is safely aborted locally, keeping broken code off the remote origin branch. + +### Bypassing in Emergencies +If you must explicitly push an intermediate draft up to a private backup branch without running validations, you can bypass Husky checks by appending the `--no-verify` flag: +```bash +git push origin feature/my-branch --no-verify \ No newline at end of file diff --git a/src/.husky/pre-push b/src/.husky/pre-push new file mode 100644 index 00000000..19d15a3f --- /dev/null +++ b/src/.husky/pre-push @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +. "$(dirname "$0")/_/husky.sh" + +echo "==========================================================" +echo "๐Ÿš€ Husky Gatekeeper: Running pre-push typecheck & tests..." +echo "==========================================================" + +# 1. Execute strict compile-time type verification across the frontend workspace +echo "๐Ÿ“ฆ Validating frontend type-safety compliance..." +cd frontend && npm run typecheck +if [ $? -ne 0 ]; then + echo "โŒ Error: TypeScript compilation compilation targets failed. Push blocked." + exit 1 +fi +cd .. + +# 2. Execute local unit test suites to guard against regression breaks +echo "๐Ÿงช Running localized unit tests..." +npm run test -- --watchAll=false --passWithNoTests +if [ $? -ne 0 ]; then + echo "โŒ Error: Unit testing suite encountered failures. Push blocked." + exit 1 +fi + +echo "โœ… Success: All gates cleared. Safe to push upstream." +exit 0 \ No newline at end of file diff --git a/src/bundlewatch.config.json b/src/bundlewatch.config.json new file mode 100644 index 00000000..89eadf29 --- /dev/null +++ b/src/bundlewatch.config.json @@ -0,0 +1,18 @@ +{ + "files": [ + { + "path": "frontend/dist/assets/*.js", + "maxSize": "250 kB", + "compression": "gzip" + }, + { + "path": "frontend/dist/assets/*.css", + "maxSize": "50 kB", + "compression": "gzip" + } + ], + "ci": { + "trackBranches": ["main"], + "githubAccessor": "comment" + } +} \ No newline at end of file diff --git a/src/lighthouserc.js b/src/lighthouserc.js new file mode 100644 index 00000000..1b78d64f --- /dev/null +++ b/src/lighthouserc.js @@ -0,0 +1,19 @@ +module.exports = { + ci: { + collect: { + numberOfRuns: 3, + staticDistDir: './frontend/dist', // Points directly to the built bundle output + }, + assert: { + assertions: { + 'categories:performance': ['error', { minScore: 0.90 }], + // Task Requirement: Enforce strict performance budgets for LCP and CLS + 'largest-contentful-paint': ['error', { maxNumericValue: 2500 }], // LCP <= 2.5s (Good threshold) + 'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }], // CLS <= 0.1 (Good threshold) + }, + }, + upload: { + target: 'temporary-public-storage', // Uploads auditable HTML reports for triage + }, + }, +}; \ No newline at end of file diff --git a/tests/accessibility.spec.ts b/tests/accessibility.spec.ts new file mode 100644 index 00000000..79b68cca --- /dev/null +++ b/tests/accessibility.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; +import AxeBuilder from '@axe-core/playwright'; + +test.describe('Accessibility Quality Gate Matrix', () => { + test('should pass standard automated a11y checking algorithms without strict violations', async ({ page }) => { + // Navigate to a critical landing view route + await page.goto('/'); + + // Scan page elements using WCAG 2.1 AA benchmarks + const accessibilityScanResults = await new AxeBuilder({ page }) + .withTags(['wcag2a', 'wcag2aa']) + .analyze(); + + // Task Requirement: Verify zero violations to avoid regression failures + expect(accessibilityScanResults.violations).toEqual([]); + }); +}); \ No newline at end of file