Skip to content

Commit 81b460d

Browse files
authored
Chore/a11y linting - Added a11y linting to eslint *.html files. (#307)
* chore(a11y-linter): installed an a11y linter * chore(eslint): fixed all the eslint errors * chore(update readme): Added an eslint doc file * chore(testing): started the testing docs
1 parent 944d851 commit 81b460d

File tree

12 files changed

+404
-21
lines changed

12 files changed

+404
-21
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ take up to 60 seconds once the docker build finishes.
1717
### First steps
1818

1919
- Install git commit template: [Commit Template](docs/commit.template.md).
20-
- Volta: [Volta](#volta)
20+
- [Volta](#volta)
2121

2222
### Recommended
2323

24-
- Compodoc: [Compodoc Conventions](docs/compodoc.md).
25-
- Docker Commands: [Docker Commands](docs/docker.md).
26-
- Git Conventions: [Git Conventions](docs/git-convention.md).
27-
- NGXS: [NGXS Conventions](docs/ngxs.md).
24+
- [Compodoc Conventions](docs/compodoc.md).
25+
- [Docker Commands](docs/docker.md).
26+
- [ESLint Strategy](docs/eslint.md).
27+
- [Git Conventions](docs/git-convention.md).
28+
- [NGXS Conventions](docs/ngxs.md).
29+
- [Testing Strategy](docs/testing.md).
2830

2931
### Optional
3032

docs/eslint.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Linting Strategy – OSF Angular
2+
3+
---
4+
5+
## Overview
6+
7+
This project uses a **unified, modern ESLint flat config** approach to enforce consistent coding styles, accessibility standards, and TypeScript best practices. Linting runs on:
8+
9+
- TypeScript (`*.ts`)
10+
- HTML templates (`*.html`)
11+
- Specs (`*.spec.ts`)
12+
13+
It also integrates into the **Git workflow** via `pre-commit` hooks to ensure clean, compliant code before every commit.
14+
15+
---
16+
17+
## Linting Commands
18+
19+
```bash
20+
# Run full project lint
21+
npm run lint
22+
```
23+
24+
---
25+
26+
## ESLint Config Structure
27+
28+
### 1. TypeScript Files (`**/*.ts`)
29+
30+
**Extends**:
31+
32+
- `@eslint/js` → base ESLint config
33+
- `typescript-eslint` recommended & stylistic configs
34+
- `angular-eslint` TypeScript rules
35+
- `eslint-plugin-prettier/recommended`
36+
37+
**Plugins**:
38+
39+
- `eslint-plugin-import`
40+
- `eslint-plugin-simple-import-sort`
41+
- `eslint-plugin-unused-imports`
42+
43+
**Key Rules**:
44+
45+
- Enforces Angular selector styles:
46+
- Directives → `osfFoo` (camelCase)
47+
- Components → `<osf-bar>` (kebab-case)
48+
- Enforces import sorting and duplicate prevention
49+
- Removes unused imports automatically
50+
- Allows unused variables named `_` to support convention (e.g., destructuring)
51+
52+
---
53+
54+
### 2. HTML Templates (`**/*.html`)
55+
56+
**Extends**:
57+
58+
- `angular-eslint/templateRecommended`
59+
- `angular-eslint/templateAccessibility`
60+
61+
**Parser**:
62+
63+
- `@angular-eslint/template-parser`
64+
65+
**Key A11y Rules** (All Set to `"error"`):
66+
67+
- `alt-text`
68+
- `valid-aria`
69+
- `click-events-have-key-events`
70+
- `role-has-required-aria`
71+
- `interactive-supports-focus`
72+
- `no-distracting-elements`
73+
- `no-autofocus`
74+
- `label-has-associated-control`
75+
76+
> This enforces **WCAG accessibility compliance** directly in Angular templates.
77+
78+
---
79+
80+
### 3. Test Files (`**/*.spec.ts`)
81+
82+
Loosened restrictions for developer convenience:
83+
84+
```ts
85+
'@typescript-eslint/no-explicit-any': 'off',
86+
'@typescript-eslint/no-empty-function': 'off',
87+
```
88+
89+
---
90+
91+
## Pre-Commit Hook
92+
93+
The `pre-commit` file includes:
94+
95+
- `npx lint-staged` → Lint only **staged files** before every commit
96+
- Ensures **TypeScript and template linting** run automatically
97+
98+
## Summary
99+
100+
| File Type | Purpose | Key Rules |
101+
| ------------ | ------------------------------------- | --------------------------------------- |
102+
| `*.ts` | Lint Angular + TS logic | Unused imports, selector rules, sorting |
103+
| `*.html` | Enforce a11y & Angular best practices | All template a11y rules enforced |
104+
| `*.spec.ts` | Relaxed for test convenience | Some TS rules turned off |
105+
| `pre-commit` | Git hook to lint before committing | Ensures consistent PR quality |

docs/testing.md

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# OSF Angular Testing Strategy
2+
3+
## Overview
4+
5+
The OSF Angular project uses a modular and mock-driven testing strategy. A shared `testing/` folder provides reusable mocks, mock data, and testing module configuration to support consistent and maintainable unit tests across the codebase.
6+
7+
---
8+
9+
## Index
10+
11+
- [Best Practices](#best-practices)
12+
- [Summary Table](#summary-table)
13+
- [Test Coverage Enforcement (100%)](#test-coverage-enforcement-100)
14+
- [Key Structure](#key-structure)
15+
- [Testing Angular Services (with HTTP)](#testing-angular-services-with-http)
16+
- [Testing Angular Components](#testing-angular-components)
17+
- [Testing Angular Pipes](#testing-angular-pipes)
18+
- [Testing Angular Directives](#testing-angular-directives)
19+
- [Testing Angular NGXS](#testing-ngxs)
20+
21+
--
22+
23+
## Best Practices
24+
25+
- Always import `OsfTestingModule` or `OsfTestingStoreModule` to minimize boilerplate and get consistent mock behavior.
26+
- Use mocks and mock-data from `testing/` to avoid repeating test setup.
27+
- Avoid real HTTP, translation, or store dependencies in unit tests by default.
28+
- Co-locate unit tests with components using `*.spec.ts`.
29+
30+
---
31+
32+
## Summary Table
33+
34+
| Location | Purpose |
35+
| ----------------------- | -------------------------------------- |
36+
| `osf.testing.module.ts` | Unified test module for shared imports |
37+
| `mocks/*.mock.ts` | Mock services and tokens |
38+
| `data/*.data.ts` | Static mock data for test cases |
39+
40+
---
41+
42+
## Test Coverage Enforcement (100%)
43+
44+
This project **strictly enforces 100% test coverage** through the following mechanisms:
45+
46+
### Husky Pre-Push Hook
47+
48+
Before pushing any code, Husky runs a **pre-push hook** that executes:
49+
50+
```bash
51+
npm run test:coverage
52+
```
53+
54+
This command:
55+
56+
- Runs the full test suite with `--coverage`.
57+
- Fails the push if **coverage drops below 100%**.
58+
- Ensures developers never bypass test coverage enforcement locally.
59+
60+
> Pro Tip: Use `npm run test:watch` during development to maintain coverage incrementally.
61+
62+
---
63+
64+
### GitHub Actions CI
65+
66+
Every pull request and push runs GitHub Actions that:
67+
68+
- Run `npm run test:coverage`.
69+
- Verify test suite passes with **100% code coverage**.
70+
- Fail the build if even **1 uncovered branch/line/function** exists.
71+
72+
This guarantees **test integrity in CI** and **prevents regressions**.
73+
74+
---
75+
76+
### Coverage Expectations
77+
78+
| File Type | Coverage Requirement |
79+
| ----------- | ------------------------------------------ |
80+
| `*.ts` | 100% line & branch |
81+
| `*.spec.ts` | Required per file |
82+
| Services | Must mock HTTP via `HttpTestingController` |
83+
| Components | DOM + Input + Output event coverage |
84+
| Pipes/Utils | All edge cases tested |
85+
86+
---
87+
88+
### Summary
89+
90+
- **Zero exceptions** for test coverage.
91+
- **Push blocked** without passing 100% tests.
92+
- GitHub CI double-checks every PR.
93+
94+
## Key Structure
95+
96+
### `testing/osf.testing.module.ts`
97+
98+
This module centralizes commonly used providers, declarations, and test utilities. It's intended to be imported into any `*.spec.ts` test file to avoid repetitive boilerplate.
99+
100+
Example usage:
101+
102+
```ts
103+
import { TestBed } from '@angular/core/testing';
104+
import { OsfTestingModule } from 'testing/osf.testing.module';
105+
106+
beforeEach(async () => {
107+
await TestBed.configureTestingModule({
108+
imports: [OsfTestingModule],
109+
}).compileComponents();
110+
});
111+
```
112+
113+
### OSFTestingModule
114+
115+
**Imports:**
116+
117+
- `NoopAnimationsModule` – disables Angular animations for clean, predictable unit tests.
118+
- `BrowserModule` – required for bootstrapping Angular features.
119+
- `CommonModule` – provides core Angular directives (e.g., `ngIf`, `ngFor`).
120+
- `TranslateModule.forRoot()` – sets up the translation layer for template-based testing with `@ngx-translate`.
121+
122+
**Providers:**
123+
124+
- `provideNoopAnimations()` – disables animation via the new standalone provider API.
125+
- `provideRouter([])` – injects an empty router config for component-level testing.
126+
- `provideHttpClient(withInterceptorsFromDi())` – ensures DI-compatible HTTP interceptors are respected in tests.
127+
- `provideHttpClientTesting()` – injects `HttpTestingController` for mocking HTTP requests in unit tests.
128+
- `TranslationServiceMock` – mocks i18n service methods.
129+
- `EnvironmentTokenMock` – mocks environment config values.
130+
131+
---
132+
133+
### OSFTestingStoreModule
134+
135+
**Imports:**
136+
137+
- `OSFTestingModule` – reuses core mocks and modules.
138+
139+
**Providers:**
140+
141+
- `StoreMock` – mocks NgRx Store for selector and dispatch testing.
142+
- `ToastServiceMock` – injects a mock version of the UI toast service.
143+
144+
### `testing/mocks/`
145+
146+
Provides common service and token mocks to isolate unit tests from real implementations.
147+
148+
**examples**
149+
150+
- `environment.token.mock.ts` – Mocks environment tokens like base API URLs.
151+
- `store.mock.ts` – NGXS or other store-related mocks.
152+
- `translation.service.mock.ts` – Prevents needing actual i18n setup during testing.
153+
- `toast.service.mock.ts` – Mocks user feedback services to track invocations without UI.
154+
155+
---
156+
157+
### `testing/data/`
158+
159+
Includes fake/mock data used by tests to simulate external API responses or internal state.
160+
161+
Only use data from the `testing/data` data mocks to ensure that all data is the centralized.
162+
163+
**examples**
164+
165+
- `addons.authorized-storage.data.ts`
166+
- `addons.external-storage.data.ts`
167+
- `addons.configured.data.ts`
168+
- `addons.operation-invocation.data.ts`
169+
170+
---
171+
172+
---
173+
174+
## Testing Angular Services (with HTTP)
175+
176+
All OSF Angular services that make HTTP requests must be tested using `HttpClientTestingModule` and `HttpTestingController`.
177+
178+
### Setup
179+
180+
```ts
181+
import { HttpTestingController } from '@angular/common/http/testing';
182+
import { OSFTestingModule } from '@testing/osf.testing.module';
183+
184+
let service: YourService;
185+
186+
beforeEach(() => {
187+
TestBed.configureTestingModule({
188+
imports: [OSFTestingModule],
189+
providers: [YourService],
190+
});
191+
192+
service = TestBed.inject(YourService);
193+
});
194+
```
195+
196+
### Example Test
197+
198+
```ts
199+
it('should call correct endpoint and return expected data', inject(
200+
[HttpTestingController],
201+
(httpMock: HttpTestingController) => {
202+
service.getSomething().subscribe((data) => {
203+
expect(data).toEqual(mockData);
204+
});
205+
206+
const req = httpMock.expectOne('/api/endpoint');
207+
expect(req.request.method).toBe('GET');
208+
req.flush(getMockDataFromTestingData());
209+
210+
httpMock.verify(); // Verify no outstanding HTTP calls
211+
}
212+
));
213+
```
214+
215+
### Key Rules
216+
217+
- Use `OSFTestingModule` to isolate the service
218+
- Inject and use `HttpTestingController`
219+
- Always call `httpMock.expectOne()` to verify the URL and method
220+
- Always call `req.flush()` to simulate the backend response
221+
- Add `httpMock.verify()` in each `it` to catch unflushed requests
222+
223+
---
224+
225+
## Testing Angular Components
226+
227+
- coming soon
228+
229+
---
230+
231+
## Testing Angular Pipes
232+
233+
- coming soon
234+
235+
---
236+
237+
## Testing Angular Directives
238+
239+
- coming soon
240+
241+
---
242+
243+
## Testing NGXS
244+
245+
- coming soon
246+
247+
---

0 commit comments

Comments
 (0)