Skip to content
Draft
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 5 additions & 6 deletions .github/scripts/check-coverage-thresholds.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const fs = require('fs');
const coverage = require('../../coverage/coverage-summary.json');
const jestConfig = require('../../jest.config.js');
const jestConfig = require('../../jest.config.ts');

const summary = coverage.total;
const thresholds = jestConfig.coverageThreshold.global;
Expand Down Expand Up @@ -29,7 +28,7 @@ for (const key of ['branches', 'functions', 'lines', 'statements']) {
);
errors.push(
formatErrorsWithAlignedStars(
`Please update the coverageThreshold.global.${key} in the jest.config.js to ---> ${current} <---`,
`Please update the coverageThreshold.global.${key} in the jest.config.ts to ---> ${current} <---`,
true
)
);
Expand All @@ -41,9 +40,9 @@ for (const key of ['branches', 'functions', 'lines', 'statements']) {
if (failed) {
const stars = '*'.repeat(warnMessage.length + 8);
console.log('\n\nCongratulations! You have successfully run the coverage check and added tests.');
console.log('\n\nThe jest.config.js file is not insync with your new test additions.');
console.log('Please update the coverage thresholds in jest.config.js.');
console.log('You will need to commit again once you have updated the jst.config.js file.');
console.log('\n\nThe jest.config.ts file is not insync with your new test additions.');
console.log('Please update the coverage thresholds in jest.config.ts.');
console.log('You will need to commit again once you have updated the jst.config.ts file.');
console.log('This is only necessary until we hit 100% coverage.');
console.log(`\n\n${stars}`);
errors.forEach((err) => {
Expand Down
14 changes: 3 additions & 11 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
```
src/testing/
├── osf.testing.provider.ts ← provideOSFCore(), provideOSFHttp()
├── osf.testing.module.ts ← OSFTestingModule (legacy — prefer providers)
├── providers/ ← Builder-pattern mocks for services
│ ├── store-provider.mock.ts
│ ├── route-provider.mock.ts
Expand All @@ -68,21 +67,14 @@ src/testing/

### `provideOSFCore()` — mandatory base provider

Every component test must include `provideOSFCore()`. It configures animations, translations, and environment tokens.
Every component test must include `provideOSFCore()`. It configures translations and environment tokens.

```typescript
export function provideOSFCore() {
return [
provideNoopAnimations(),
importProvidersFrom(TranslateModule.forRoot()),
TranslationServiceMock,
EnvironmentTokenMock,
];
return [provideTranslation, TranslateServiceMock, EnvironmentTokenMock];
}
```

> **Never** import `OSFTestingModule` directly in new tests. It is retained for legacy compatibility only. Use `provideOSFCore()` instead.
---

## 3. Test File Structure
Expand Down Expand Up @@ -854,7 +846,7 @@ This project strictly enforces 90%+ test coverage through GitHub Actions CI.

## 18. Best Practices

1. **Always use `provideOSFCore()`** — never import `OSFTestingModule` directly in new tests.
1. **Always use `provideOSFCore()`**.
2. **Always use `provideMockStore()`** — never mock `component.actions` via `Object.defineProperty`.
3. **Always pass explicit mocks to `MockProvider`** when you need `jest.fn()` assertions. Bare `MockProvider(Service)` creates ng-mocks stubs.
4. **Check `@testing/` before creating inline mocks** — builders and factories almost certainly exist.
Expand Down
37 changes: 17 additions & 20 deletions jest.config.js → jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
module.exports = {
import type { Config } from 'jest';

const config: Config = {
preset: 'jest-preset-angular',
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
globalSetup: '<rootDir>/jest.global-setup.ts',
collectCoverage: false,
testEnvironment: 'jsdom',
clearMocks: true,
restoreMocks: true,
coverageReporters: ['json-summary', 'lcov', 'clover'],
collectCoverage: false,
coverageDirectory: 'coverage',
coverageReporters: ['json-summary', 'lcov', 'clover', 'text-summary'],
moduleFileExtensions: ['ts', 'js', 'html', 'json', 'mjs'],
extensionsToTreatAsEsm: ['.ts'],
testMatch: ['<rootDir>/src/**/*.spec.ts'],
moduleNameMapper: {
'^@osf/(.*)$': '<rootDir>/src/app/$1',
'^@core/(.*)$': '<rootDir>/src/app/core/$1',
Expand All @@ -27,11 +34,8 @@ module.exports = {
],
},
transformIgnorePatterns: [
'node_modules/(?!.*\\.mjs$|@ngxs|@angular|@ngrx|parse5|entities|chart.js|@mdit|@citation-js|@traptitech|@sentry|@primeng|@newrelic)',
'node_modules/(?!(@angular|@ngxs|@ngx-translate|angular-google-tag-manager|ngx-cookie-service|ngx-markdown-editor|angularx-qrcode|ngx-captcha|@sentry|@newrelic|@centerforopenscience|@mdit|@traptitech|@citation-js|primeng|@primeuix|markdown-it|markdown-it-anchor|markdown-it-toc-done-right|markdown-it-video|chart\\.js)/)',
],
testEnvironment: 'jsdom',
moduleFileExtensions: ['ts', 'js', 'html', 'json', 'mjs'],
coverageDirectory: 'coverage',
collectCoverageFrom: [
'src/app/**/*.{ts,js}',
'!src/app/core/theme/**',
Expand All @@ -40,24 +44,21 @@ module.exports = {
'!src/app/**/*.routes.{ts,js}',
'!src/app/**/*.route.{ts,js}',
'!src/app/**/mappers/**',
'!src/app/shared/mappers/**',
'!src/app/**/*.model.{ts,js}',
'!src/app/**/models/*.{ts,js}',
'!src/app/shared/models/**',
'!src/app/**/*.enum.{ts,js}',
'!src/app/**/*.type.{ts,js}',
'!src/app/**/*.spec.{ts,js}',
'!src/app/**/*.module.ts',
'!src/app/**/index.ts',
'!src/app/**/public-api.ts',
],
extensionsToTreatAsEsm: ['.ts'],
coverageThreshold: {
global: {
branches: 43.3,
functions: 42.7,
lines: 69.3,
statements: 69.8,
functions: 43.8,
lines: 70.18,
statements: 70.6,
},
},
watchPathIgnorePatterns: [
Expand All @@ -68,11 +69,7 @@ module.exports = {
'<rootDir>/src/environments/',
'<rootDir>/src/@types/',
],
testPathIgnorePatterns: [
'<rootDir>/src/environments',
'<rootDir>/src/app/features/files/pages/file-detail',
'<rootDir>/src/app/features/project/addons/',
'<rootDir>/src/app/features/settings/addons/',
'<rootDir>/src/app/features/settings/tokens/store/',
],
testPathIgnorePatterns: ['<rootDir>/src/environments'],
};

export default config;
Loading
Loading