Skip to content

Commit bcc31e4

Browse files
Merge pull request #81 from EDOHWARES/chore/pr-quality-gates-68
add-pr-quality-gates
2 parents de20e6b + e6884cb commit bcc31e4

File tree

10 files changed

+268
-37
lines changed

10 files changed

+268
-37
lines changed

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
blank_issues_enabled: true
2+
contact_links:
3+
- name: TeachLink Community
4+
url: https://t.me/teachlinkOD
5+
about: Ask questions and discuss changes with maintainers.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: "PR Quality Gates / Governance"
2+
description: "Work related to CI, branch protection, PR process, governance"
3+
title: "[Governance] <short description>"
4+
labels: ["frontend", "devops", "ci", "governance", "priority-high"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Use this template for branch protection, CI, and PR process improvements.
10+
11+
- type: input
12+
id: background
13+
attributes:
14+
label: Background
15+
description: Why is this needed?
16+
validations:
17+
required: true
18+
19+
- type: textarea
20+
id: acceptance
21+
attributes:
22+
label: Acceptance Criteria
23+
description: List the conditions that must be true to close this issue.
24+
value: |
25+
- [ ] PR cannot merge without passing CI
26+
- [ ] PR cannot merge without approval
27+
- [ ] Direct push to main/develop is blocked
28+
- [ ] All PRs must reference an issue
29+
- [ ] All conversations must be resolved
30+
validations:
31+
required: true
32+
33+
- type: textarea
34+
id: implementation
35+
attributes:
36+
label: Implementation notes
37+
description: Optional implementation plan / links.
38+
validations:
39+
required: false

.github/branch-protection.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Branch protection (repository settings)
2+
3+
These are **GitHub repository settings** (not enforced by code). Configure them under:
4+
**Settings → Branches → Branch protection rules**.
5+
6+
## Protected branches
7+
Create rules for:
8+
- `main`
9+
- `develop`
10+
11+
## Required settings
12+
Enable the following options:
13+
14+
### блок direct pushes
15+
-**Restrict who can push to matching branches** (recommended)
16+
-**Do not allow force pushes**
17+
-**Do not allow deletions**
18+
19+
### Require PRs
20+
-**Require a pull request before merging**
21+
-**Require approvals**: at least **1** (or **2** if desired)
22+
-**Dismiss stale approvals when new commits are pushed**
23+
-**Require conversation resolution before merging**
24+
25+
### Require checks
26+
-**Require status checks to pass before merging**
27+
- ✅ Select required checks from `Frontend CI`:
28+
- `type-check`
29+
- `lint`
30+
- `build`
31+
- `test`
32+
33+
### Keep branch up to date
34+
-**Require branches to be up to date before merging**
35+
36+
## PR must reference an issue
37+
GitHub does not have a single built-in "require issue link" toggle for all repos.
38+
Recommended options:
39+
40+
1. **Process enforcement (lightweight)**
41+
- Use `.github/pull_request_template.md` and require `Closes #<issue>` in the PR.
42+
43+
2. **Stronger enforcement (recommended)**
44+
- Add a dedicated GitHub Action that fails if the PR body does not contain `Closes #<number>`.
45+
- If you want this, we can add a small workflow using `actions/github-script`.
46+
47+
## Notes
48+
- Repository admins can optionally be included or excluded from these requirements.
49+
- Configure the same rule set for both `main` and `develop` to avoid bypass paths.

.github/pull_request_template.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Summary
2+
<!-- What does this PR change and why? -->
3+
4+
## Related Issue
5+
<!-- REQUIRED: link the issue this PR addresses -->
6+
Closes #
7+
8+
## Type of change
9+
- [ ] Feature
10+
- [ ] Bug fix
11+
- [ ] Chore / Refactor
12+
- [ ] Docs
13+
14+
## Screenshots / Recording (if UI)
15+
<!-- Optional -->
16+
17+
## Testing
18+
<!-- REQUIRED: describe how you tested this change -->
19+
- [ ] `npm run type-check`
20+
- [ ] `npm run lint`
21+
- [ ] `npm run test`
22+
- [ ] `npm run build`
23+
24+
## Quality gate checklist
25+
- [ ] CI checks pass (Frontend CI)
26+
- [ ] At least 1–2 approvals (per branch protection rules)
27+
- [ ] Branch is up-to-date with the base branch
28+
- [ ] All conversations resolved
29+
- [ ] PR description includes `Closes #<issue-number>`
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: PR Quality Gates
2+
3+
on:
4+
pull_request:
5+
types: [opened, edited, synchronize, reopened]
6+
branches: [main, develop]
7+
8+
permissions:
9+
pull-requests: read
10+
11+
jobs:
12+
require-linked-issue:
13+
name: Require linked issue in PR body
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Validate PR body contains issue closing keyword
17+
uses: actions/github-script@v7
18+
with:
19+
script: |
20+
const body = context.payload.pull_request?.body || '';
21+
// Accept common keywords: close/closes/closed/fix/fixes/fixed/resolve/resolves/resolved
22+
// Require a github issue reference like: "Closes #123"
23+
const re = /(close[sd]?|fix(e[sd])?|resolve[sd]?)\s+#\d+/i;
24+
if (!re.test(body)) {
25+
core.setFailed('PR description must reference an issue using e.g. "Closes #123".');
26+
}

CONTRIBUTING.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Contributing to TeachLink Frontend
2+
3+
Thanks for contributing to TeachLink.
4+
5+
## Branching & workflow
6+
- **Do not push directly** to protected branches (`main`, `develop`).
7+
- Create a feature branch from `develop` (preferred) or `main`:
8+
- `feat/<short-description>`
9+
- `fix/<short-description>`
10+
- `chore/<short-description>`
11+
12+
## Assignment required
13+
Before opening a PR, ensure the issue is assigned to you.
14+
15+
## Pull request requirements (quality gates)
16+
Your PR will be blocked from merging unless it meets the following:
17+
18+
1. **CI must pass**
19+
- Required checks: `type-check`, `lint`, `build`, `test` (GitHub Actions: **Frontend CI**)
20+
21+
2. **Approvals required**
22+
- Minimum **1–2 approvals** (as configured in branch protection rules).
23+
24+
3. **Branch must be up to date**
25+
- Update your branch with the target branch before merge (no stale merge).
26+
27+
4. **Conversations resolved**
28+
- All review conversations must be resolved before merge.
29+
30+
5. **Issue must be referenced**
31+
- PR description must reference a GitHub issue and include one of:
32+
- `Close #<issue-number>` / `Closes #<issue-number>` / `Fixes #<issue-number>`
33+
34+
## Local checks (run before pushing)
35+
- `npm run type-check`
36+
- `npm run lint`
37+
- `npm run test`
38+
- `npm run build`
39+
40+
## PR description format
41+
Use the PR template (auto-applied). Ensure it includes:
42+
- Summary of changes
43+
- Testing notes
44+
- `Close #<issue-number>`
45+
46+
## Code standards
47+
- Keep changes small and focused.
48+
- No console errors.
49+
- Use `lucide-react` icons for UI.
50+
- Keep components accessible and responsive.
51+
52+
## Security
53+
Do not commit secrets. Use `.env.local` for local environment variables.

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,16 @@ npm run dev
109109

110110
For detailed tasks, see GitHub Issues
111111

112-
🤝 Contributing
112+
## 🤝 Contributing
113113
We welcome community contributions!
114114

115-
Guidelines:
116-
Fork the repo and make your changes in a feature branch
115+
- Read **`CONTRIBUTING.md`** before opening a PR.
116+
- All PRs must include an issue reference in the description (e.g. `Closes #68`).
117+
- Merges to protected branches require passing CI + approvals.
117118

118-
Before submitting a PR, read the CONTRIBUTING.md file
119+
Guidelines:
120+
- Fork the repo and make your changes in a feature branch
121+
- Before submitting a PR, read the **`CONTRIBUTING.md`** file
119122

120123
## 📬 Join the Community
121124

@@ -144,5 +147,3 @@ let make our code clean, maintainable and scallable. Keep to Standard
144147

145148
📜 License
146149
MIT © 2025 TeachLink DAO
147-
148-
```

src/form-management/state/form-state-manager.test.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Unit tests for Form State Manager
33
*/
44

5-
import { describe, it, expect, beforeEach } from 'vitest';
5+
import { describe, it, expect, beforeEach, vi } from 'vitest';
66
import { FormStateManager } from './form-state-manager';
77
import { ValidationResult, StateChangeEvent } from '../types/core';
88

@@ -214,7 +214,7 @@ describe('FormStateManager', () => {
214214

215215
describe('state change subscriptions', () => {
216216
it('should notify subscribers of field changes', () => {
217-
const callback = jest.fn();
217+
const callback = vi.fn();
218218
const subscription = stateManager.subscribeToChanges(callback);
219219

220220
stateManager.updateField('email', 'test@example.com');
@@ -232,7 +232,7 @@ describe('FormStateManager', () => {
232232
});
233233

234234
it('should notify subscribers of validation changes', () => {
235-
const callback = jest.fn();
235+
const callback = vi.fn();
236236
stateManager.subscribeToChanges(callback);
237237

238238
const validationResult: ValidationResult = {
@@ -252,10 +252,10 @@ describe('FormStateManager', () => {
252252
});
253253

254254
it('should handle subscription errors gracefully', () => {
255-
const errorCallback = jest.fn(() => {
256-
throw new Error('Callback error');
255+
const errorCallback = vi.fn(() => {
256+
throw new Error('subscription error');
257257
});
258-
const normalCallback = jest.fn();
258+
const normalCallback = vi.fn();
259259

260260
stateManager.subscribeToChanges(errorCallback);
261261
stateManager.subscribeToChanges(normalCallback);
@@ -269,7 +269,7 @@ describe('FormStateManager', () => {
269269
});
270270

271271
it('should unsubscribe correctly', () => {
272-
const callback = jest.fn();
272+
const callback = vi.fn();
273273
const subscription = stateManager.subscribeToChanges(callback);
274274

275275
stateManager.updateField('email', 'test1@example.com');
@@ -333,7 +333,7 @@ describe('FormStateManager', () => {
333333
describe('programmatic state control methods', () => {
334334
describe('silent field updates', () => {
335335
it('should set field value without triggering change events', () => {
336-
const callback = jest.fn();
336+
const callback = vi.fn();
337337
stateManager.subscribeToChanges(callback);
338338

339339
stateManager.setFieldValueSilently('email', 'test@example.com');
@@ -345,7 +345,7 @@ describe('FormStateManager', () => {
345345

346346
describe('batch operations', () => {
347347
it('should set multiple validation states at once', () => {
348-
const callback = jest.fn();
348+
const callback = vi.fn();
349349
stateManager.subscribeToChanges(callback);
350350

351351
const validationStates = {
@@ -364,7 +364,7 @@ describe('FormStateManager', () => {
364364
});
365365

366366
it('should set multiple field values in batch', () => {
367-
const callback = jest.fn();
367+
const callback = vi.fn();
368368
stateManager.subscribeToChanges(callback);
369369

370370
const values = {
@@ -471,15 +471,15 @@ describe('FormStateManager', () => {
471471

472472
describe('submission control', () => {
473473
it('should start submission with callback', () => {
474-
const callback = jest.fn();
474+
const callback = vi.fn();
475475
stateManager.startSubmission(callback);
476476

477477
expect(stateManager.getState().isSubmitting).toBe(true);
478478
expect(callback).toHaveBeenCalled();
479479
});
480480

481481
it('should complete submission successfully', () => {
482-
const callback = jest.fn();
482+
const callback = vi.fn();
483483

484484
// Set up dirty state
485485
stateManager.updateField('email', 'test@example.com');
@@ -493,7 +493,7 @@ describe('FormStateManager', () => {
493493
});
494494

495495
it('should complete submission with failure', () => {
496-
const callback = jest.fn();
496+
const callback = vi.fn();
497497

498498
// Set up dirty state
499499
stateManager.updateField('email', 'test@example.com');
@@ -686,7 +686,7 @@ describe('FormStateManager', () => {
686686
});
687687

688688
it('should trigger cascading updates manually', () => {
689-
const callback = jest.fn();
689+
const callback = vi.fn();
690690
stateManager.subscribeToChanges(callback);
691691

692692
stateManager.updateField('trigger', 'show');

0 commit comments

Comments
 (0)