diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index 3c12bf4fe..b06877914 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -3,9 +3,7 @@ name: Require at least one approval on: pull_request: - types: [opened, reopened, ready_for_review, synchronize] - pull_request_review: - types: [submitted, edited, dismissed] + types: [opened, reopened, ready_for_review, synchronize, edited] jobs: check-approval: diff --git a/README.md b/README.md index 14508c7ab..b2c6bb3e9 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ take up to 60 seconds once the docker build finishes. - [Docker Commands](docs/docker.md). - [ESLint Strategy](docs/eslint.md). - [Git Conventions](docs/git-convention.md). +- [i18n](docs/i18n.md). - [NGXS Conventions](docs/ngxs.md). - [Testing Strategy](docs/testing.md). diff --git a/commitlint.config.cjs b/commitlint.config.cjs index 33a11c3e9..2e552773e 100644 --- a/commitlint.config.cjs +++ b/commitlint.config.cjs @@ -5,15 +5,16 @@ module.exports = { 2, 'always', [ + 'chore', // Build process, CI/CD, dependencies + 'docs', // Documentation update 'feat', // New feature 'fix', // Bug fix - 'docs', // Documentation update - 'style', // Code style (formatting, missing semicolons, etc.) - 'refactor', // Code refactoring (no feature changes) + 'lang', // All updates related to i18n changes 'perf', // Performance improvements - 'test', // Adding tests - 'chore', // Build process, CI/CD, dependencies + 'refactor', // Code refactoring (no feature changes) 'revert', // Reverting changes + 'style', // Code style (formatting, missing semicolons, etc.) + 'test', // Adding tests ], ], 'scope-empty': [2, 'never'], // Scope must always be present diff --git a/docs/admin.knowledge-base.md b/docs/admin.knowledge-base.md index 15d8adf5a..d33082099 100644 --- a/docs/admin.knowledge-base.md +++ b/docs/admin.knowledge-base.md @@ -1,7 +1,18 @@ # Admin Knowledge Base +## Index + +- [Overview](#overview) +- [All things GitHub](#all-things-github) + +--- + +## Overview + Information on updates that require admin permissions +--- + ## All things GitHub ### GitHub pipeline @@ -16,6 +27,8 @@ The `.github` folder contains the following: .github/workflows 4. The GitHub PR templates .github/pull_request_template.md +5. The backup json for the settings/rules + .github/rules ### Local pipeline diff --git a/docs/assets/osf-ngxs-diagram.png b/docs/assets/osf-ngxs-diagram.png index e19e06910..95460f3ae 100644 Binary files a/docs/assets/osf-ngxs-diagram.png and b/docs/assets/osf-ngxs-diagram.png differ diff --git a/docs/commit.template.md b/docs/commit.template.md index f5a591b92..5bcfdf575 100644 --- a/docs/commit.template.md +++ b/docs/commit.template.md @@ -1,5 +1,7 @@ ## 📝 Enabling the Shared Commit Template +## Overview + This project includes a Git commit message template stored at: ``` diff --git a/docs/compodoc.md b/docs/compodoc.md index 30ff0b86b..0f61cc364 100644 --- a/docs/compodoc.md +++ b/docs/compodoc.md @@ -1,5 +1,20 @@ # Angular Documentation with Compodoc +## Index + +- [Overview](#overview) +- [How to Generate Documentation](#how-to-generate-documentation) +- [Documentation Coverage Requirements](#documentation-coverage-requirements) +- [Pre-commit Enforcement via Husky](#pre-commit-enforcement-via-husky) +- [CI/CD Enforcement](#cicd-enforcement) +- [Tips for Passing Coverage](#tips-for-passing-coverage) +- [Output Directory](#output-directory) +- [Need Help?](#need-help) + +--- + +## Overview + This project uses [Compodoc](https://compodoc.app/) to generate and enforce documentation for all Angular code. Documentation is mandatory and must meet a **100% coverage threshold** to ensure consistent API clarity across the codebase. --- diff --git a/docs/docker.md b/docs/docker.md index 6cb529323..0404bdaa4 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -1,5 +1,15 @@ # Docker +## Index + +- [Overview](#overview) +- [Docker Commands](#docker-commands) +- [Troubleshooting](#troubleshooting) + +--- + +## Overview + The OSF angular project uses a docker image to simplify the developer process. ### Volumes @@ -33,6 +43,8 @@ If you don’t see the site, ensure the start script includes: "start": "ng serve --host 0.0.0.0 --port 4200 --poll 2000" ``` +--- + ## Docker Commands ### build + run in background (build is only required for the initial install or npm updates) @@ -105,6 +117,8 @@ docker rmi : docker rmi -f ``` +--- + ## Troubleshooting If the application does not open in your browser at [http://localhost:4200](http://localhost:4200), follow these steps in order: diff --git a/docs/eslint.md b/docs/eslint.md index 5a4933835..255d0cc3c 100644 --- a/docs/eslint.md +++ b/docs/eslint.md @@ -1,5 +1,13 @@ # Linting Strategy – OSF Angular +## Index + +- [Overview](#overview) +- [Linting Commands](#linting-commands) +- [ESLint Config Structure](#eslint-config-structure) +- [Pre-Commit Hook](#pre-commit-hook) +- [Summary](#summary) + --- ## Overview @@ -12,6 +20,9 @@ This project uses a **unified, modern ESLint flat config** approach to enforce c It also integrates into the **Git workflow** via `pre-commit` hooks to ensure clean, compliant code before every commit. +**IMPORTANT** +The OSF application must meet full accessibility (a11y) compliance to ensure equitable access for users of all abilities, in alignment with our commitment to inclusivity and funding obligations. + --- ## Linting Commands diff --git a/docs/git-convention.md b/docs/git-convention.md index d0c99b2b4..f231f792f 100644 --- a/docs/git-convention.md +++ b/docs/git-convention.md @@ -1,11 +1,25 @@ # CommitLint and Git Branch Naming Convention (Aligned with Angular Guideline) +## Index + +- [Overview](#overview) +- [Local pipeline](#local-pipeline) +- [Contributing Workflow](#contributing-workflow) +- [Commitlint](#commitlint) +- [Branch Naming Format](#branch-naming-format) + +--- + +## Overview + To maintain a clean, structured commit history and optimize team collaboration, we adhere to the Angular Conventional Commits standard for both commit messages and Git branch naming. This ensures every change type is immediately recognizable and supports automation for changelog generation, semantic versioning, and streamlined release processes. In addition, we enforce these standards using CommitLint, ensuring that all commit messages conform to the defined rules before they are accepted into the repository. This project employs both GitHub Actions and a local pre-commit pipeline to validate commit messages, enforce branch naming conventions, and maintain repository integrity throughout the development workflow. +--- + ## Local pipeline The local pipeline is managed via husky @@ -16,6 +30,8 @@ The local pipeline is managed via husky - All tests pass - Test coverage is met +--- + ## Contributing Workflow To contribute to this repository, follow these steps: @@ -69,6 +85,8 @@ This workflow ensures that: For a step-by-step guide on forking and creating pull requests, see [GitHub’s documentation on forks](https://docs.github.com/en/get-started/quickstart/fork-a-repo) and [about pull requests](https://docs.github.com/en/pull-requests). +--- + ## Commitlint OSF uses [Commitlint](https://www.npmjs.com/package/commitlint) to **enforce a consistent commit message format**. @@ -103,35 +121,37 @@ Commit messages must be structured as: | Type | Description | | ------------ | ------------------------------------------------------------------------------------- | +| **chore** | Changes to the build process, CI/CD pipeline, or dependencies. | +| **docs** | Documentation-only changes (e.g., README, comments). | | **feat** | New feature added to the codebase. | | **fix** | Bug fix for an existing issue. | -| **docs** | Documentation-only changes (e.g., README, comments). | -| **style** | Changes that do not affect code meaning (formatting, whitespace, missing semicolons). | -| **refactor** | Code restructuring without changing external behavior. | +| **lang** | Any updates to the i18n files in src/asssets/i18n/en.json. | | **perf** | Code changes that improve performance. | -| **test** | Adding or updating tests. | -| **chore** | Changes to the build process, CI/CD pipeline, or dependencies. | +| **refactor** | Code restructuring without changing external behavior. | | **revert** | Reverts a previous commit. | +| **style** | Changes that do not affect code meaning (formatting, whitespace, missing semicolons). | +| **test** | Adding or updating tests. | --- ### **Examples** -✅ **Good Examples** +**Good Examples** ``` +chore(deps): update Angular to v19 +docs(readme): add setup instructions for Windows feat(auth): add OAuth2 login support fix(user-profile): resolve avatar upload failure on Safari -docs(readme): add setup instructions for Windows -style(header): reformat nav menu CSS -refactor(api): simplify data fetching logic +lang(eng-4898): added new strings for preprint page perf(search): reduce API response time by caching results -test(auth): add tests for password reset flow -chore(deps): update Angular to v19 +refactor(api): simplify data fetching logic revert: revert “feat(auth): add OAuth2 login support” +style(header): reformat nav menu CSS +test(auth): add tests for password reset flow ``` -❌ **Bad Examples** +**Bad Examples** ``` fixed bug in login @@ -156,10 +176,10 @@ update stuff Refs #456 ``` ---- - Commitlint will run automatically and reject non-compliant messages. +--- + ## Branch Naming Format ### The branch name should follow the format: @@ -175,10 +195,14 @@ short-description – a brief description of the change. ``` +--- + ## Available Types (type) See the [Allowed Commit Types](#allowed-commit-types) section for details. +--- + ## Branch Naming Examples ### Here are some examples of branch names: @@ -192,7 +216,7 @@ See the [Allowed Commit Types](#allowed-commit-types) section for details. ``` -### 🛠 Example of Creating a Branch: +### Example of Creating a Branch: To create a new branch, use the following command: @@ -201,15 +225,15 @@ git checkout -b feat/1234-add-user-authentication ``` -### 🏆 Best Practices +### Best Practices -- ✅ Use short and clear descriptions in branch names. -- ✅ Follow a consistent style across all branches for better project structure. -- ✅ Avoid redundant words, e.g., fix/1234-fix-bug (the word "fix" is redundant). -- ✅ Use kebab-case (- instead of \_ or CamelCase). -- ✅ If there is no issue ID, omit it, e.g., docs/update-contributing-guide. +- Use short and clear descriptions in branch names. +- Follow a consistent style across all branches for better project structure. +- Avoid redundant words, e.g., fix/1234-fix-bug (the word "fix" is redundant). +- Use kebab-case (- instead of \_ or CamelCase). +- If there is no issue ID, omit it, e.g., docs/update-contributing-guide. -### 🔗 Additional Resources +### Additional Resources **Conventional Commits**: https://www.conventionalcommits.org @@ -219,7 +243,7 @@ git checkout -b feat/1234-add-user-authentication ### This branch naming strategy ensures better traceability and improves commit history readability. -### 🔗 Additional Resources +### Additional Resources Conventional Commits: https://www.conventionalcommits.org @@ -227,4 +251,4 @@ Angular Commit Guidelines: https://github.com/angular/angular/blob/main/CONTRIBU Git Flow: https://nvie.com/posts/a-successful-git-branching-model/ -This branch naming and commit message strategy ensures better traceability and improves commit history readability. 🚀 +This branch naming and commit message strategy ensures better traceability and improves commit history readability. diff --git a/docs/i18n.md b/docs/i18n.md new file mode 100644 index 000000000..45e7e2a39 --- /dev/null +++ b/docs/i18n.md @@ -0,0 +1,147 @@ +# OSF Angular – Internationalization (i18n) Strategy + +## Index + +- [Overview](#overview) +- [Integration: `@ngx-translate/core`](#integration-ngx-translatecore) +- [Usage Guidelines](#usage-guidelines) +- [Format: `en.json`](#format-enjson) +- [Source of Truth: `en.json` Only](#source-of-truth-enjson-only) +- [Language Branch Workflow](#language-branch-workflow) +- [Summary](#summary) + +--- + +## Overview + +The OSF Angular project uses [`@ngx-translate/core`](https://github.com/ngx-translate/core) to manage internationalization (i18n) across the entire application. This allows for consistent, dynamic translation of all user-visible text, making it easier to support multiple languages. + +All strings rendered to users—whether in HTML templates or dynamically via Angular components—must be sourced from the centralized i18n JSON files located in: + +``` +src/app/i18n/ +``` + +**IMPORTANT** +The OSF application must maintain 100% translation coverage, as it is a requirement of grant funding that supports a globally distributed user base. + +## Integration: `@ngx-translate/core` + +To support multilingual content, the following module is included globally: + +```ts +import { TranslatePipe } from '@ngx-translate/core'; +``` + +The translation service (`TranslateService`) is injected where necessary and used to load and access translations at runtime. + +## Usage Guidelines + +### 1. HTML Templates + +In templates, use the `translate` pipe: + +```html +

{{ 'home.title' | translate }}

+``` + +### 2. Dynamic Component Strings + +For component logic, use the `TranslateService`: + +```ts +private readonly translateService = inject(TranslateService); +public title!: string; + +ngOnInit(): void { + this.title = this.translate.instant('home.title'); +} +``` + +Avoid hardcoded strings in both the template and logic. All user-facing text must be represented by a translation key from the JSON files in `src/app/i18n`. + +## Format: `en.json` + +The primary English translation file is located at: + +``` +src/app/i18n/en.json +``` + +The structure should be **namespaced by feature or component** for maintainability: + +```json +{ + "home": { + "title": "Welcome to OSF", + "subTitle": "Your research. Your control." + }, + "login": { + "email": "Email Address", + "password": "Password" + } +} +``` + +Avoid deeply nested keys, and always use consistent camel-casing and key naming for reuse and clarity. + +**Note** + +The `common section` in the en.json file stores frequently used phrases to prevent one-off duplicate translations. + +## Source of Truth: `en.json` Only + +All translation key additions and updates must be made **only** in the `src/app/i18n/en.json` file. + +> Other language files (e.g., `fr.json`, `es.json`) must **not** be modified by non-OSF engineers. These translations will be handle internally by the OSF Angular team. + +This ensures consistency across translations and prevents desynchronization between supported languages. + +### Summary + +| Language File | Editable? | Notes | +| -------------------- | --------- | -------------------------------------------------- | +| `en.json` | Yes | Canonical source of all keys and values | +| `fr.json`, `es.json` | No | Never manually modified during feature development | + +Always validate that new translation keys appear in `en.json` only. + +## Language Branch Workflow + +All updates to the i18n translation files (e.g., `en.json`) must follow a strict workflow: + +### Branch Naming + +Create a branch prefixed with: + +``` +language/ +``` + +Example: + +``` +language/ENG-145-update-login-copy +``` + +### Commit Message Format + +The commit header must include the `lang()` label: + +``` +lang(ENG-145): update login page strings +``` + +This ensures that all translation updates are tracked, reviewed, and associated with the appropriate task or ticket. + +## Summary + +| Requirement | Description | +| ---------------------- | -------------------------------------------------------------- | +| Translation Library | [`@ngx-translate/core`](https://github.com/ngx-translate/core) | +| Source of All Strings | `src/app/i18n/*.json` | +| Required in HTML | Must use the `translate` pipe | +| Required in Components | Must use `TranslateService` | +| English File Format | Namespaced keys in `en.json` | +| Branch Naming | `language/` | +| Commit Convention | `lang(): message` | diff --git a/docs/ngxs.md b/docs/ngxs.md index 114db45fa..3f93391c0 100644 --- a/docs/ngxs.md +++ b/docs/ngxs.md @@ -1,4 +1,18 @@ -# NGXS State Management Overview +# NGXS State Management + +## Index + +- [Purpose](#purpose) +- [Core Concepts](#core-concepts) +- [Directory Structure](#directory-structure) +- [State Models](#state-models) +- [Tooling and Extensions](#tooling-and-extensions) +- [Testing](#testing) +- [Documentation](#documentation) + +--- + +## Overview The OSF Angular project uses [NGXS](https://www.ngxs.io/) as the state management library for Angular applications. NGXS provides a simple, powerful, and TypeScript-friendly framework for managing state across components and services. @@ -45,6 +59,33 @@ src/app/shared/services/ --- +## State Models + +The OSF Angular project follows a consistent NGXS state model structure to ensure clarity, predictability, and alignment across all features. The recommended shape for each domain-specific state is as follows: + +1. Domain state pattern: + +```ts +domain: { + data: [], // Array of typed model data (e.g., Project[], User[]) + isLoading: false, // Indicates if data retrieval (GET) is in progress + isSubmitting: false, // Indicates if data submission (POST/PUT/DELETE) is in progress + error: null, // Captures error messages from failed HTTP requests +} +``` + +2. `data` holds the strongly typed collection of entities defined by the feature's interface or model class. + +3. `isLoading` is a signal used to inform the component and template layer that a read or fetch operation is currently pending. + +4. `isSubmitting` signals that a write operation (form submission, update, delete, etc.) is currently in progress. + +5. `error` stores error state information (commonly strings or structured error objects) that result from failed service interactions. This can be displayed in UI or logged for debugging. + +Each domain state should be minimal, normalized, and scoped to its specific feature, mirroring the structure and shape of the corresponding OSF backend API response. + +--- + ## Tooling and Extensions - [Redux DevTools](https://github.com/zalmoxisus/redux-devtools-extension) is supported. Enable it in development via `NgxsReduxDevtoolsPluginModule`. @@ -55,7 +96,8 @@ src/app/shared/services/ ## Testing -- Mock `Store` using `jest.fn()` or test-specific modules for unit testing components and services. +- [Testing Strategy](docs/testing.md) +- [NGXS State Testing Strategy](docs/testing.md#ngxs-state-testing-strategy) --- diff --git a/docs/testing.md b/docs/testing.md index 17448bdc9..3de20e198 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -1,13 +1,9 @@ # OSF Angular Testing Strategy -## Overview - -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. - ---- - ## Index +- [Overview](#overview) + - [Pro-tips](#pro-tips) - [Best Practices](#best-practices) - [Summary Table](#summary-table) - [Test Coverage Enforcement (100%)](#test-coverage-enforcement-100) @@ -18,7 +14,36 @@ The OSF Angular project uses a modular and mock-driven testing strategy. A share - [Testing Angular Directives](#testing-angular-directives) - [Testing Angular NGXS](#testing-ngxs) --- +--- + +## Overview + +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. + +--- + +### Pro-tips + +**What to test** + +The OSF Angular testing strategy enforces 100% coverage while also serving as a guardrail for future engineers. Each test should highlight the most critical aspect of your code — what you’d want the next developer to understand before making changes. If a test fails during a refactor, it should clearly signal that a core feature was impacted, prompting them to investigate why and preserve the intended behavior. + +--- + +**Test Data** + +The OSF Angular Test Data module provides a centralized and consistent source of data across all unit tests. It is intended solely for use within unit tests. By standardizing test data, any changes to underlying data models will produce cascading failures, which help expose the full scope of a refactor. This is preferable to isolated or hardcoded test values, which can lead to false positives and missed regressions. + +The strategy for structuring test data follows two principles: + +1. Include enough data to cover all relevant permutations required by the test suite. +2. Ensure the data reflects all possible states (stati) of the model. + +**Test Scope** + +The OSF Angular project defines a `@testing` scope that can be used for importing all testing-related modules. + +--- ## Best Practices @@ -34,8 +59,8 @@ The OSF Angular project uses a modular and mock-driven testing strategy. A share | Location | Purpose | | ----------------------- | -------------------------------------- | | `osf.testing.module.ts` | Unified test module for shared imports | -| `mocks/*.mock.ts` | Mock services and tokens | -| `data/*.data.ts` | Static mock data for test cases | +| `src/mocks/*.mock.ts` | Mock services and tokens | +| `src/data/*.data.ts` | Static mock data for test cases | --- @@ -91,9 +116,11 @@ This guarantees **test integrity in CI** and **prevents regressions**. - **Push blocked** without passing 100% tests. - GitHub CI double-checks every PR. +--- + ## Key Structure -### `testing/osf.testing.module.ts` +### `src/testing/osf.testing.module.ts` 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. @@ -101,7 +128,7 @@ Example usage: ```ts import { TestBed } from '@angular/core/testing'; -import { OsfTestingModule } from 'testing/osf.testing.module'; +import { OsfTestingModule } from '@testing/osf.testing.module'; beforeEach(async () => { await TestBed.configureTestingModule({ @@ -141,9 +168,9 @@ beforeEach(async () => { - `StoreMock` – mocks NgRx Store for selector and dispatch testing. - `ToastServiceMock` – injects a mock version of the UI toast service. -### `testing/mocks/` +### Testing Mocks -Provides common service and token mocks to isolate unit tests from real implementations. +The `src/testing/mocks/` directory provides common service and token mocks to isolate unit tests from real implementations. **examples** @@ -154,11 +181,16 @@ Provides common service and token mocks to isolate unit tests from real implemen --- -### `testing/data/` +### Test Data -Includes fake/mock data used by tests to simulate external API responses or internal state. +The `src/testing/data/` directory includes fake/mock data used by tests to simulate external API responses or internal state. -Only use data from the `testing/data` data mocks to ensure that all data is the centralized. +The OSF Angular Test Data module provides a centralized and consistent source of data across all unit tests. It is intended solely for use within unit tests. By standardizing test data, any changes to underlying data models will produce cascading failures, which help expose the full scope of a refactor. This is preferable to isolated or hardcoded test values, which can lead to false positives and missed regressions. + +The strategy for structuring test data follows two principles: + +1. Include enough data to cover all relevant permutations required by the test suite. +2. Ensure the data reflects all possible states (stati) of the model. **examples** @@ -169,11 +201,13 @@ Only use data from the `testing/data` data mocks to ensure that all data is the --- ---- - ## Testing Angular Services (with HTTP) -All OSF Angular services that make HTTP requests must be tested using `HttpClientTestingModule` and `HttpTestingController`. +All OSF Angular services that make HTTP requests must be tested using `HttpClientTestingModule` and `HttpTestingController`. This testing style verifies both the API call itself and the logic that maps the response into application data. + +When using HttpTestingController to flush HTTP requests in tests, only use data from the @testing/data mocks to ensure consistency and full test coverage. + +Any error handling will also need to be tested. ### Setup @@ -240,8 +274,86 @@ it('should call correct endpoint and return expected data', inject( --- -## Testing NGXS +## NGXS State Testing Strategy -- coming soon +The OSF Angular strategy for NGXS state testing is to create **small integration test scenarios**. This is a deliberate departure from traditional **black box isolated** testing. The rationale is: + +1. **NGXS actions** tested in isolation are difficult to mock and result in garbage-in/garbage-out tests. +2. **NGXS selectors** tested in isolation are easy to mock but also lead to garbage-in/garbage-out outcomes. +3. **NGXS states** tested in isolation are easy to invoke but provide no meaningful validation. +4. **Mocking service calls** during state testing introduces false positives, since the mocked service responses may not reflect actual backend behavior. + +This approach favors realism and accuracy over artificial test isolation. + +### Test Outline Strategy + +1. **Dispatch the primary action** – Kick off the state logic under test. +2. **Dispatch any dependent actions** – Include any secondary actions that rely on the primary action's outcome. +3. **Verify the loading selector is `true`** – Ensure the loading state is activated during the async flow. +4. **Verify the service call using `HttpTestingController` and `@testing/data` mocks** – Confirm that the correct HTTP request is made and flushed with known mock data. +5. **Verify the loading selector is `false`** – Ensure the loading state deactivates after the response is handled. +6. **Verify the primary data selector** – Check that the core selector related to the dispatched action returns the expected state. +7. **Verify any additional selectors** – Assert the output of other derived selectors relevant to the action. +8. **Validate the test with `httpMock.verify()`** – Confirm that all HTTP requests were flushed and none remain unhandled: + +```ts +expect(httpMock.verify).toBeTruthy(); +``` + +### Example + +This is an example of an NGXS action test that involves both a **primary action** and a **dependent action**. The dependency must be dispatched first to ensure the test environment mimics the actual runtime behavior. This pattern helps validate not only the action effects but also the full selector state after updates. All HTTP requests are flushed using the centralized `@testing/data` mocks. + +```ts +it('should test action, state and selectors', inject([HttpTestingController], (httpMock: HttpTestingController) => { + let result: any[] = []; + // Dependency Action + store.dispatch(new GetAuthorizedStorageAddons('reference-id')).subscribe(); + + // Primary Action + store.dispatch(new GetAuthorizedStorageOauthToken('account-id')).subscribe(() => { + result = store.selectSnapshot(AddonsSelectors.getAuthorizedStorageAddons); + }); + + // Loading selector is true + const loading = store.selectSignal(AddonsSelectors.getAuthorizedStorageAddonsLoading); + expect(loading()).toBeTruthy(); + + // Http request for service for dependency action + let request = httpMock.expectOne('api/path/dependency/action'); + expect(request.request.method).toBe('GET'); + // @testing/data response mock + request.flush(getAddonsAuthorizedStorageData()); + + // Http request for service for primary action + let request = httpMock.expectOne('api/path/primary/action'); + expect(request.request.method).toBe('PATCH'); + // @testing/data response mock with updates + const addonWithToken = getAddonsAuthorizedStorageData(1); + addonWithToken.data.attributes.oauth_token = 'ya2.34234324534'; + request.flush(addonWithToken); + + // Full testing of the dependency selector + expect(result[1]).toEqual( + Object({ + accountOwnerId: '0b441148-83e5-4f7f-b302-b07b528b160b', + }) + ); + + // Full testing of the primary selector + let oauthToken = store.selectSnapshot(AddonsSelectors.getAuthorizedStorageAddonOauthToken(result[0].id)); + expect(oauthToken).toBe('ya29.A0AS3H6NzDCKgrUx'); + + // Verify only the requested `account-id` was updated + oauthToken = store.selectSnapshot(AddonsSelectors.getAuthorizedStorageAddonOauthToken(result[1].id)); + expect(oauthToken).toBe(result[1].oauthToken); + + // Loading selector is false + expect(loading()).toBeFalsy(); + + // httpMock.verify to ensure no other api calls are called. + expect(httpMock.verify).toBeTruthy(); +})); +``` ---