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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ See [Disaster Recovery Runbooks](./docs/runbooks/README.md) for detailed procedu
- **Q3**: Expand security review, run load and failure-mode validation, and prepare launch readiness.
- **Q4**: Complete mainnet launch checklist, monitor production stability, and gather retrospective improvements.

```bash
# Validate branch naming, PR description format, and contribution standards
npm run validate:contribution-standards

# Validate the testing strategy document for unit, integration, and E2E coverage guidance
npm run validate:testing-strategy

# Validate sprint label formats and issue triage taxonomy
npm run validate:sprint-and-triage

# Validate release notes template and cliff config
npm run validate:release-notes

# Validate NFR baselines (SLO, RTO, RPO)
npm run validate:nfr-baselines
```
## 🤝 Contributing

Fork the repository and clone it to your local machine
Expand Down
4 changes: 4 additions & 0 deletions docs/TESTING_STRATEGY.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ Use E2E tests only for user journeys that must prove the app works in a real bro
- Browser-only flows have at least one Playwright test.
- New feature work adds coverage in the layer that owns the behavior, not just in the widest suite.

## Repository Enforcement

This strategy is enforced with the repository validator at `npm run validate:testing-strategy`. The command checks that the strategy document still covers the required testing layers, layer-specific guidance, recommended commands, and Playwright-based E2E coverage expectations.

## Core Playwright User Flows

Canonical browser journeys live under `frontend/e2e/` and run with `cd frontend && npm run test:e2e` (CI: `.github/workflows/e2e.yml`).
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"validate:frontend-env": "tsx scripts/validate-frontend-env.ts",
"test:validate-frontend-env": "vitest run --config scripts/vitest.config.ts",
"validate:contribution-standards": "tsx scripts/validate-contribution-standards.ts",
"validate:testing-strategy": "tsx scripts/validate-testing-strategy.ts",
"validate:sprint-and-triage": "tsx scripts/validate-sprint-and-triage-conventions.ts",
"validate:release-notes": "tsx scripts/validate-release-notes.ts",
"validate:nfr-baselines": "tsx scripts/validate-nfr-baselines.ts"
Expand Down
84 changes: 84 additions & 0 deletions scripts/validate-testing-strategy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, expect, it } from 'vitest';
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { runFullTestingStrategyValidation, validateTestingStrategyDoc } from './validate-testing-strategy';

describe('testing strategy validator', () => {
it('accepts a testing strategy document with required sections', () => {
const markdown = `
# YieldVault-RWA Testing Strategy

## Principles
- Keep tests close to the code they validate.

## Test Layers
| Layer | Purpose | Owned by | Typical locations | Primary commands |
| --- | --- | --- | --- | --- |
| Unit | Local logic | Owner | src/**/*.test.ts | npm test |

## Ownership Rules
- Frontend tests are owned by the UI team.

## Fixture Strategy
- Keep fixtures local to the suite.

## Coverage Expectations By Feature Type
| Feature type | Required coverage |
| --- | --- |
| Utility | Unit tests |

## What Belongs In Each Layer
### Unit
Use unit tests for deterministic logic.

### Integration
Use integration tests for cross-module behavior.

### E2E
Use E2E tests for browser journeys.

## Recommended Commands
- npm run test
- npm run test:e2e

## Review Checklist
- The test scope matches the behavior under change.

## Core Playwright User Flows
| Flow | Spec | What it proves |
| --- | --- | --- |
| Dashboard | dashboard-load.spec.ts | App loads correctly |
`;

const result = validateTestingStrategyDoc(markdown);
expect(result.valid).toBe(true);
expect(result.errors).toEqual([]);
});

it('rejects a document that is missing required sections', () => {
const result = validateTestingStrategyDoc('# YieldVault-RWA Testing Strategy\n\n## Principles\n');

expect(result.valid).toBe(false);
expect(result.errors.some((error) => error.includes('Test Layers'))).toBe(true);
expect(result.errors.some((error) => error.includes('Recommended Commands'))).toBe(true);
});

it('accepts the repository testing strategy document', () => {
const docPath = resolve(__dirname, '../docs/TESTING_STRATEGY.md');
expect(existsSync(docPath)).toBe(true);

const markdown = readFileSync(docPath, 'utf8');
const result = validateTestingStrategyDoc(markdown);

expect(result.valid).toBe(true);
expect(result.errors).toEqual([]);
});

it('passes the full repository validation for the testing strategy doc', () => {
const rootDir = resolve(__dirname, '..');
const result = runFullTestingStrategyValidation(rootDir);

expect(result.valid).toBe(true);
expect(result.errors).toEqual([]);
});
});
82 changes: 82 additions & 0 deletions scripts/validate-testing-strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';

export interface ValidationResult {
valid: boolean;
errors: string[];
warnings: string[];
}

const REQUIRED_SECTIONS = [
'## Principles',
'## Test Layers',
'## Ownership Rules',
'## Fixture Strategy',
'## Coverage Expectations By Feature Type',
'## What Belongs In Each Layer',
'## Recommended Commands',
'## Review Checklist',
'## Core Playwright User Flows',
];

const REQUIRED_LAYER_HEADINGS = ['### Unit', '### Integration', '### E2E'];

export function validateTestingStrategyDoc(markdownContent: string): ValidationResult {
const errors: string[] = [];
const warnings: string[] = [];

if (!markdownContent || markdownContent.trim() === '') {
errors.push('Testing strategy document cannot be empty.');
return { valid: false, errors, warnings };
}

for (const section of REQUIRED_SECTIONS) {
if (!markdownContent.includes(section)) {
errors.push(`Testing strategy doc is missing required section: "${section}"`);
}
}

for (const heading of REQUIRED_LAYER_HEADINGS) {
if (!markdownContent.includes(heading)) {
errors.push(`Testing strategy doc is missing layer heading: "${heading}"`);
}
}

if (!markdownContent.includes('Playwright')) {
errors.push('Testing strategy doc must reference Playwright for browser E2E coverage.');
}

if (!markdownContent.includes('npm run test:e2e')) {
errors.push('Testing strategy doc must include the E2E command for browser journeys.');
}

return { valid: errors.length === 0, errors, warnings };
}

export function runFullTestingStrategyValidation(rootDir: string = process.cwd()): ValidationResult {
const errors: string[] = [];
const warnings: string[] = [];

const docPath = resolve(rootDir, 'docs/TESTING_STRATEGY.md');
if (!existsSync(docPath)) {
errors.push('docs/TESTING_STRATEGY.md file does not exist.');
return { valid: false, errors, warnings };
}

const result = validateTestingStrategyDoc(readFileSync(docPath, 'utf8'));
errors.push(...result.errors);
warnings.push(...result.warnings);

return { valid: errors.length === 0, errors, warnings };
}

if (require.main === module) {
const result = runFullTestingStrategyValidation();
if (!result.valid) {
console.error('❌ Testing strategy validation failed:');
result.errors.forEach((err) => console.error(` - ${err}`));
process.exit(1);
} else {
console.log('✅ Testing strategy validation passed successfully!');
}
}
Loading