Skip to content

feat(docs): add the angular testing documentation #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

arvi18
Copy link

@arvi18 arvi18 commented Apr 16, 2025

This PR is to improve the testing doc of Angular package.
Let's discuss what is missing and you would like to be added.
BR

Summary by CodeRabbit

  • New Features

    • Added a comprehensive guide and examples for testing Angular applications, including a dedicated "Testing" section in the documentation.
    • Introduced a complete Angular unit testing example project with setup for Jest and Testing Library, including configuration files, mock API interceptor, and sample service with optimistic updates.
    • Provided a standalone Angular component and service demonstrating integration with TanStack Query and unit testing practices.
  • Documentation

    • Expanded Angular documentation with new guides and example links focused on testing and unit testing with Jest.
  • Tests

    • Added a detailed test suite for the Angular tasks service, covering optimistic updates, error handling, and cache management.

@gandalf-repo
Copy link

@coderabbitai full review

Copy link

coderabbitai bot commented Apr 16, 2025

✅ Actions performed

Full review triggered.

Copy link

coderabbitai bot commented Apr 16, 2025

Walkthrough

This update introduces a comprehensive Angular unit testing example using TanStack Query, Jest, and Angular Testing Library. It adds new documentation and guides on testing Angular applications, including practical examples and setup instructions. The new example project includes all necessary configuration files, a mock API interceptor for simulating backend responses, a service for managing tasks with optimistic updates, and a complete test suite verifying query and mutation behaviors. Additional minor changes improve type safety, control flow, and error handling in related Angular examples. Documentation and configuration are updated to reflect these new testing resources.

Changes

File(s) / Path(s) Change Summary
docs/config.json Updated documentation configuration to add Angular testing guides and examples.
docs/framework/angular/guides/testing.md Added a comprehensive guide on testing Angular apps with TanStack Query, including sample code, setup, and mocking strategies.
examples/angular/auto-refetching/src/app/services/tasks.service.ts Modified mutation onSuccess handlers to return the result of invalidateQueries.
examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts Removed the clearMutation property from the component.
examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts Changed error handling for a specific URL to emit an observable error using throwError.
examples/angular/optimistic-updates/src/app/services/tasks.service.ts Updated mutation callbacks: typed onMutate parameter, made onSuccess a no-op, returned promise in onSettled, and renamed onError parameters.
examples/angular/unit-testing/.devcontainer/devcontainer.json Added a dev container configuration for Node.js development.
examples/angular/unit-testing/.eslintrc.cjs Added an (empty) ESLint configuration file with TypeScript checking.
examples/angular/unit-testing/README.md Added a README with instructions for running and testing the example.
examples/angular/unit-testing/angular.json Added Angular workspace configuration for the unit testing example, including build, serve, and test settings.
examples/angular/unit-testing/jest.config.ts Added Jest configuration for the Angular unit testing example.
examples/angular/unit-testing/package.json Added package definition with dependencies for Angular, TanStack Query, Jest, and Testing Library.
examples/angular/unit-testing/src/app/app.component.ts Added a standalone root Angular component rendering the unit testing component.
examples/angular/unit-testing/src/app/app.config.ts Added application configuration with HTTP and query client providers, including mock API interceptor.
examples/angular/unit-testing/src/app/components/unit-testing.component.ts Added a new Angular component demonstrating query and mutation usage with optimistic updates and error simulation.
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts Added a mock HTTP interceptor simulating task API endpoints and error cases.
examples/angular/unit-testing/src/app/services/tasks.service.spec.ts Added a test suite for the TasksService, covering query/mutation lifecycle and optimistic updates.
examples/angular/unit-testing/src/app/services/tasks.service.ts Added a service managing tasks with TanStack Query, implementing optimistic updates and cache management.
examples/angular/unit-testing/src/index.html Added the main HTML entry point for the Angular unit testing example.
examples/angular/unit-testing/src/main.ts Added the main bootstrap file for the Angular application.
examples/angular/unit-testing/tsconfig.app.json, tsconfig.json, tsconfig.spec.json Added TypeScript configuration files for application and test compilation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UnitTestingComponent
    participant TasksService
    participant MockAPIInterceptor
    participant QueryClient

    User->>UnitTestingComponent: Enters a new task and clicks "Add"
    UnitTestingComponent->>TasksService: Calls addTask mutation
    TasksService->>QueryClient: Cancel ongoing 'tasks' queries
    TasksService->>QueryClient: Snapshot current tasks cache
    TasksService->>QueryClient: Optimistically update cache with new task
    TasksService->>MockAPIInterceptor: POST /api/tasks (simulate network)
    MockAPIInterceptor-->>TasksService: Responds with success or error after delay
    alt Success
        TasksService->>QueryClient: Invalidate 'tasks' query cache
        QueryClient->>TasksService: Refetch tasks
        TasksService->>MockAPIInterceptor: GET /api/tasks
        MockAPIInterceptor-->>TasksService: Responds with updated tasks
        TasksService->>UnitTestingComponent: Updates with latest tasks
    else Error
        TasksService->>QueryClient: Rollback cache to previous state
        TasksService->>UnitTestingComponent: Updates with rolled-back tasks
    end
Loading

Poem

In the garden of Angular, tests now bloom,
With queries and mocks, there's plenty of room.
Tasks dance in the cache, both fast and slow,
Optimistic bunnies watch data grow.
A jestful hop, a mutation's delight,
Our code is now tested—oh, what a sight!
🐇✨

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (8)
examples/angular/unit-testing/README.md (1)

1-7: Clear instructions, but consider adding more context

The README provides concise instructions for running the example and tests, which is good. However, it would be helpful to add more context about what this example demonstrates and what technologies it uses (TanStack Query, Jest, Angular Testing Library).

Consider expanding the README with:

 # TanStack Query Angular unit-testing example

+This example demonstrates how to test Angular applications that use TanStack Query for data fetching. It includes:
+
+- Jest configuration for Angular testing
+- Angular Testing Library for component testing
+- Mock HTTP interceptors for API simulation
+- Testing queries and mutations with optimistic updates
+
 To run this example:

 - `npm install` or `yarn` or `pnpm i` or `bun i`
 - `npm run start` or `yarn start` or `pnpm start` or `bun start`
 - `npm run test` to run the tests
🧰 Tools
🪛 LanguageTool

[duplication] ~5-~5: Possible typo: you repeated a word.
Context: ... - npm install or yarn or pnpm i or bun i - npm run start or yarn start or pnpm start or `bun st...

(ENGLISH_WORD_REPEAT_RULE)

examples/angular/unit-testing/jest.config.ts (3)

1-1: Fix the type import syntax.

The import for Jest's Config type should use explicit type import syntax.

-const config: import('jest').Config = {
+import type { Config } from 'jest';
+
+const config: Config = {
🧰 Tools
🪛 ESLint

[error] 1-1: import() type annotations are forbidden.

(@typescript-eslint/consistent-type-imports)


1-5: Fix inconsistent indentation.

The file has unnecessary indentation that should be removed for consistency.

-const config: import('jest').Config = {
-    roots: ['<rootDir>/src/'],
-    setupFilesAfterEnv: []
-  };
-  export default config;
+const config: import('jest').Config = {
+  roots: ['<rootDir>/src/'],
+  setupFilesAfterEnv: []
+};
+export default config;
🧰 Tools
🪛 ESLint

[error] 1-1: import() type annotations are forbidden.

(@typescript-eslint/consistent-type-imports)


3-3: Consider adding common Jest setup files.

The setupFilesAfterEnv array is empty. For Angular testing, you might want to include setup files for common testing utilities.

For example, you could add:

-  setupFilesAfterEnv: []
+  setupFilesAfterEnv: ['<rootDir>/setup-jest.ts']

And create a setup-jest.ts file with common test utilities and global setup.

examples/angular/unit-testing/src/app/app.config.ts (2)

21-21: Use a named constant for time values.

Using magic numbers for time calculations can make code less maintainable. Consider using a named constant for clarity.

+// Constants for better readability
+const DAY_IN_MS = 1000 * 60 * 60 * 24;
+
export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch(), withInterceptors([mockInterceptor])),
    provideTanStackQuery(
      new QueryClient({
        defaultOptions: {
          queries: {
-            gcTime: 1000 * 60 * 60 * 24, // 24 hours
+            gcTime: DAY_IN_MS, // 24 hours
          },
        },
      }),

25-25: Consider conditionally enabling devtools based on environment.

For production builds, you might want to disable devtools. Consider using environment variables or Angular's isDevMode() to conditionally include devtools.

import {
  provideHttpClient,
  withFetch,
  withInterceptors,
} from '@angular/common/http'
import {
  QueryClient,
  provideTanStackQuery,
  withDevtools,
} from '@tanstack/angular-query-experimental'
+import { isDevMode } from '@angular/core'
import { mockInterceptor } from './interceptor/mock-api.interceptor'
import type { ApplicationConfig } from '@angular/core'

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch(), withInterceptors([mockInterceptor])),
    provideTanStackQuery(
      new QueryClient({
        defaultOptions: {
          queries: {
            gcTime: 1000 * 60 * 60 * 24, // 24 hours
          },
        },
      }),
-      withDevtools(),
+      isDevMode() ? withDevtools() : undefined,
    ),
  ],
}

Note: This is needed only if the withDevtools() function doesn't already handle this internally. From the code snippets, it looks like it might already check isDevMode() internally.

examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (1)

10-15: Improve type annotation for mutation result.

The addTask variable uses any type which could be more strictly typed for better type safety.

-let addTask: any;
+import { CreateMutationResult } from "@tanstack/angular-query-experimental";
+let addTask: CreateMutationResult<any, Error, {task: string, failMutation: boolean}, unknown>;
examples/angular/unit-testing/src/app/services/tasks.service.ts (1)

30-33: Minor typo in documentation comment.

There's a small grammatical error in the JSDoc comment.

/**
 * Creates a mutation for adding a task.
- * On success, invalidates and refetch the "tasks" query cache to update the task list.
+ * On success, invalidates and refetches the "tasks" query cache to update the task list.
 */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a5990e and cb6cea3.

⛔ Files ignored due to path filters (2)
  • examples/angular/unit-testing/src/favicon.ico is excluded by !**/*.ico
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • docs/config.json (2 hunks)
  • docs/framework/angular/guides/testing.md (1 hunks)
  • examples/angular/auto-refetching/src/app/services/tasks.service.ts (2 hunks)
  • examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts (0 hunks)
  • examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts (2 hunks)
  • examples/angular/optimistic-updates/src/app/services/tasks.service.ts (2 hunks)
  • examples/angular/unit-testing/.devcontainer/devcontainer.json (1 hunks)
  • examples/angular/unit-testing/.eslintrc.cjs (1 hunks)
  • examples/angular/unit-testing/README.md (1 hunks)
  • examples/angular/unit-testing/angular.json (1 hunks)
  • examples/angular/unit-testing/jest.config.ts (1 hunks)
  • examples/angular/unit-testing/package.json (1 hunks)
  • examples/angular/unit-testing/src/app/app.component.ts (1 hunks)
  • examples/angular/unit-testing/src/app/app.config.ts (1 hunks)
  • examples/angular/unit-testing/src/app/components/unit-testing.component.ts (1 hunks)
  • examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (1 hunks)
  • examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (1 hunks)
  • examples/angular/unit-testing/src/app/services/tasks.service.ts (1 hunks)
  • examples/angular/unit-testing/src/index.html (1 hunks)
  • examples/angular/unit-testing/src/main.ts (1 hunks)
  • examples/angular/unit-testing/tsconfig.app.json (1 hunks)
  • examples/angular/unit-testing/tsconfig.json (1 hunks)
  • examples/angular/unit-testing/tsconfig.spec.json (1 hunks)
💤 Files with no reviewable changes (1)
  • examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts
🧰 Additional context used
🧬 Code Graph Analysis (4)
examples/angular/unit-testing/src/app/app.config.ts (2)
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (1)
  • mockInterceptor (20-64)
packages/angular-query-experimental/src/providers.ts (2)
  • provideTanStackQuery (93-111)
  • withDevtools (242-335)
examples/angular/unit-testing/src/main.ts (1)
examples/angular/unit-testing/src/app/app.config.ts (1)
  • appConfig (14-28)
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (1)
examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts (1)
  • mockInterceptor (18-53)
examples/angular/unit-testing/src/app/services/tasks.service.ts (1)
examples/angular/optimistic-updates/src/app/services/tasks.service.ts (1)
  • Injectable (11-82)
🪛 ESLint
examples/angular/unit-testing/jest.config.ts

[error] 1-1: import() type annotations are forbidden.

(@typescript-eslint/consistent-type-imports)

🪛 LanguageTool
examples/angular/unit-testing/README.md

[duplication] ~5-~5: Possible typo: you repeated a word.
Context: ... - npm install or yarn or pnpm i or bun i - npm run start or yarn start or pnpm start or `bun st...

(ENGLISH_WORD_REPEAT_RULE)

docs/framework/angular/guides/testing.md

[uncategorized] ~6-~6: A comma might be missing here.
Context: ...y to await a signal to reach a specific value we will use polling to wait in our test...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~6-~6: The preposition “into” seems more likely in this position than the preposition “in”.
Context: ...st (instead of transforming our signals in observable and use RxJS features to fil...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION_IN_INTO)


[uncategorized] ~6-~6: This verb may not be in the correct form. Consider using a different form for this context.
Context: ...nsforming our signals in observable and use RxJS features to filter the values). If...

(AI_EN_LECTOR_REPLACEMENT_VERB_FORM)


[uncategorized] ~6-~6: A comma might be missing here.
Context: ...ues). If you want to do like us for the polling you can use the angular testing library...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~13-~13: A comma may be missing after the conjunctive/linking adverb ‘Otherwise’.
Context: ...sh ng add @testing-library/angular ``` Otherwise we recommend to use the toObservable fe...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


[grammar] ~14-~14: The verb ‘recommend’ is used with the gerund form.
Context: ...sting-library/angular ``` Otherwise we recommend to use the toObservable feature from Angular. ...

(ADMIT_ENJOY_VB)


[uncategorized] ~18-~18: A comma might be missing here.
Context: ... that provide the Query options through function this is what we are going to do. ## A ...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~166-~166: Possible missing comma found.
Context: ...provideTanStackQuery during the TestBed setup as shown in the above example. ## Test...

(AI_HYDRA_LEO_MISSING_COMMA)


[grammar] ~170-~170: Please check whether an article is missing.
Context: ...you should mock the requests. There are multiple way of handling the mocking, we recomme...

(THE_BEST_WAY)


[grammar] ~170-~170: The verb ‘recommend’ is used with the gerund form.
Context: ...ultiple way of handling the mocking, we recommend to use the Interceptor from Angular, see [here...

(ADMIT_ENJOY_VB)


[duplication] ~171-~171: Possible typo: you repeated a word.
Context: ...rceptors) for more details. You can see the the Interceptor setup in the "Unit testing ...

(ENGLISH_WORD_REPEAT_RULE)

🪛 Biome (1.9.4)
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts

[error] 45-47: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (39)
examples/angular/unit-testing/.eslintrc.cjs (1)

1-6: Simple ESLint configuration looks good!

This is a minimal but correctly configured ESLint setup for the Angular unit testing example. The TypeScript checking is enabled and the config is properly typed.

examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts (2)

9-9: LGTM - Import for throwError added correctly.

The throwError import is correctly added to the imports from 'rxjs', which will be used for error handling.


49-49: Better error handling implementation!

Good improvement! You've changed from returning an HTTP response with error status to using RxJS's throwError operator, which better simulates actual network failures and allows proper error handling testing.

examples/angular/unit-testing/tsconfig.json (1)

1-32: Well-configured TypeScript settings for Angular testing

This is a comprehensive TypeScript configuration that enables strict type checking and modern ES2022 features. The Angular compiler options are also well configured with strict template checking which will help catch errors early in the development process.

examples/angular/auto-refetching/src/app/services/tasks.service.ts (2)

40-42: Improved promise handling in onSuccess callbacks

The addition of return statements for invalidateQueries ensures the promise chain is maintained, allowing consumers to properly await the completion of cache invalidation after mutations succeed.


54-56: Improved promise handling in onSuccess callbacks

Same improvement as in the addTask method - returning the invalidation promise maintains the promise chain, ensuring proper control flow in async operations.

examples/angular/unit-testing/.devcontainer/devcontainer.json (1)

1-4: Good development container configuration

This configuration provides a standardized Node.js 22 development environment for the Angular unit testing example, ensuring consistent development experience across different machines.

examples/angular/unit-testing/tsconfig.app.json (1)

1-9: Proper TypeScript application configuration

The configuration correctly extends the base tsconfig.json while specifying app-specific settings. It properly sets the output directory and includes only the necessary files for the application build, excluding test files.

examples/angular/unit-testing/src/main.ts (1)

1-5: Standard Angular application bootstrapping

This main entry file properly bootstraps the Angular application using the standalone approach with appropriate error handling. It correctly imports and uses the appConfig which provides HTTP client with mock interceptors and TanStack Query configuration.

examples/angular/unit-testing/tsconfig.spec.json (1)

1-8: Code looks good.

The TypeScript configuration for test files is properly set up with the correct extends, output directory, and type definitions for Jest.

docs/config.json (2)

710-713: Great addition of Angular testing documentation.

The addition of the Testing guide for Angular is well-placed in the documentation structure. This helps users find testing resources for Angular applications.


1291-1294: Good example addition for Angular unit testing.

Adding the "Unit Testing / Jest" example to the Angular examples section provides a valuable resource for users looking to implement testing in their Angular applications.

examples/angular/unit-testing/src/app/app.config.ts (2)

1-12: Well-organized imports.

The imports are properly organized by package, with type imports correctly using the type keyword.


14-28: Good configuration setup for the demo application.

The appConfig sets up the necessary providers for HTTP client and TanStack Query client with appropriate configuration. The mock interceptor integration allows for realistic testing scenarios without a real backend.

examples/angular/unit-testing/src/index.html (1)

1-13: Well-structured HTML document

This is a clean, standard HTML5 document that follows best practices for Angular applications. It properly sets up the viewport, character encoding, and base href. The app-root selector matches the component defined in app.component.ts.

examples/angular/unit-testing/src/app/app.component.ts (1)

1-11: Good implementation with modern Angular practices

This component follows current Angular best practices:

  • Uses OnPush change detection for better performance
  • Implemented as a standalone component
  • Clean minimal template
  • Properly imports the UnitTestingComponent

The structure is appropriate for a root component in a testing example.

examples/angular/optimistic-updates/src/app/services/tasks.service.ts (4)

51-51: Improved type safety

Adding explicit type annotation for the task parameter improves type safety and code readability.


71-71: Better parameter naming convention

Using underscore prefix for unused parameters follows best practices by explicitly indicating these parameters aren't used in the function body.


78-78: Improved promise handling

Returning the invalidateQueries promise is better than the previous implementation as it allows proper promise chaining when calling code needs to wait for the invalidation to complete.


50-50:

❓ Verification inconclusive

Verify the empty onSuccess handler intention

The onSuccess handler has been changed to an empty function. Since this previously may have contained the query invalidation logic (now moved to onSettled), please confirm this change is intentional and doesn't affect the optimistic update flow.


🏁 Script executed:

#!/bin/bash
# Check if this was intentional by looking at related component usage and tests
rg -A 3 -B 3 "onSuccess" --glob "examples/angular/**/*.ts" | grep -v "node_modules"

Length of output: 3608


Confirm Intentional Removal of Query Invalidation from onSuccess

It appears that in examples/angular/optimistic-updates/src/app/services/tasks.service.ts the onSuccess handler has been changed to an empty function—consistent with the unit-testing variant, while the auto-refetching variant still performs query invalidation in onSuccess. Since the invalidation logic seems to have been migrated to an onSettled callback in this flow, please confirm that this design change is deliberate and does not adversely affect the optimistic update mechanism.

examples/angular/unit-testing/package.json (2)

1-37: Well-configured package.json for Angular testing

The package.json is properly configured with:

  • All necessary Angular dependencies at consistent versions
  • Appropriate testing libraries (Jest and Testing Library)
  • Standard npm scripts for Angular development

The combination of Jest and Testing Library is a good choice for Angular unit testing and matches the purpose of this example.


18-18:

✅ Verification successful

Note about experimental package

The package uses @tanstack/angular-query-experimental which is marked as experimental. This is appropriate for a testing example, but you might want to add a note in the documentation about the experimental status of this package.


🌐 Web query:

What is the current status of @tanstack/angular-query-experimental?

💡 Result:

Current Status of @tanstack/angular-query-experimental

@tanstack/angular-query-experimental is currently in an experimental phase and not considered stable for production use. This status is clearly communicated across official documentation and package information:

  • Experimental Stage: The library is subject to breaking changes even in minor and patch releases. Users are strongly advised to upgrade carefully and, if used in production, to lock to a specific patch version to avoid unexpected breaking changes[1][5][9].
  • Angular Compatibility: The adapter primarily targets Angular 16 and higher, utilizing recent Angular features like signals for reactivity[1][10].
  • Active Development: The project is actively maintained, with new features and changes appearing regularly in recent release notes (for example, v5.74.0 released in April 2025 included Angular-specific updates)[6]. However, no official timeline for a stable release has been announced yet[2][3].
  • Usage Guidance: For stable, long-term projects or production environments, developers are encouraged to consider other, stable alternatives—such as @tanstack/query-angular for Angular v18 and above[7].

Key Points for Developers

  • Usage Risks: Because breaking changes can occur at any time (including minor and patch releases), production use is discouraged unless the package version is locked tightly[1][5][9].
  • Features: The library leverages Angular signals and provides a declarative API for data fetching, caching, and synchronization, similar to TanStack Query’s APIs for other frameworks[8][10].
  • Active Tracking: There is an open discussion and issue on GitHub for tracking progress toward a stable release, but as of April 2025, it remains in experimental status[2][3].

Summary Table

Status Angular Version Support Stable? Recent Updates Key Recommendation
Experimental 16+ No April 2025 Lock version if used; not for production

Recommendation

Use @tanstack/angular-query-experimental for experimentation, learning, or prototyping in supported Angular versions. For production or mission-critical projects, wait for the stable release or consider using a stable alternative such as @tanstack/query-angular[7].

Citations:


Documentation Update: Clarify Experimental Status of Angular Query Package

The use of @tanstack/angular-query-experimental is confirmed to be experimental, meaning it may introduce breaking changes even in minor updates. While its inclusion is acceptable for a testing example, please update the associated documentation (e.g., in the README or a dedicated docs section) to clearly state that this package is experimental and not recommended for production without version locking. This note will help future users understand the risks involved with using an experimental package.

examples/angular/unit-testing/src/app/components/unit-testing.component.ts (5)

1-9: Good use of experimental query hooks.

The component properly imports necessary dependencies including the experimental TanStack Query hooks. This approach demonstrates cutting-edge usage of the Angular Query features.


10-14: Strong component configuration with OnPush change detection.

The component uses OnPush change detection strategy which is optimal for performance. The selector and imports are properly defined with good component hygiene.


15-47: Well-structured template with modern Angular control flow syntax.

The template uses Angular's modern control flow syntax (@if, @for) and properly handles different states:

  • Loading state
  • Background fetching indicator
  • Form controls with two-way binding
  • Mutation failure toggle

This provides a complete example of how to build interfaces that respond to query states.


49-56: Good service integration and state management.

The component correctly injects the TasksService and uses TanStack Query hooks to manage data fetching and mutations. The private service field with the # prefix follows best practices.


58-66: Proper input validation in addItem method.

The method correctly validates that the input is not empty before triggering a mutation. It also resets the input field after submission, which is a good practice for user experience.

examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (1)

1-7: Good documentation with JSDoc comments.

The JSDoc header clearly explains the purpose of the interceptor and the endpoints it mocks, making it easier for developers to understand its role in the testing environment.

examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (4)

1-9: Good testing setup with proper imports.

The test file correctly imports all necessary dependencies for testing Angular services with TanStack Query.


17-44: Well-structured test setup with disabled retries.

The setup disables query retries which is a best practice for testing. The TestBed configuration is thorough and includes all necessary providers.


51-85: Comprehensive query testing with waitFor pattern.

The test correctly uses Angular Testing Library's waitFor to handle asynchronous state changes and validates both the initial data fetching and optimistic updates.


86-111: Good error handling test coverage.

The test properly verifies the error case scenario, including checking optimistic updates, error rejection, and cache rollback behavior.

examples/angular/unit-testing/src/app/services/tasks.service.ts (6)

1-10: Good import organization.

The imports are properly organized with Angular core imports first, followed by library imports, with a blank line separating import groups.


11-17: Well-structured service setup.

The service is properly configured with providedIn: 'root' and uses the recommended private field notation with the # prefix.


18-28: Clear query implementation with proper typing.

The allTasks method uses correct typing with the TanStack Query options pattern. The use of lastValueFrom to convert the Observable to a Promise is appropriate.


34-50: Well-implemented mutation function.

The addTask method properly implements a mutation with typed parameters and correctly handles the conditional URL for simulating errors.


51-70: Robust optimistic update implementation.

The onMutate handler correctly implements all the steps for optimistic updates:

  1. Cancels outgoing queries
  2. Takes a snapshot of the current state
  3. Updates the cache optimistically
  4. Returns the previous state for rollback

This follows the recommended pattern for optimistic updates with TanStack Query.


71-81: Complete error handling with cache invalidation.

The service properly handles errors by rolling back to the previous state and ensures that the cache is invalidated after settlement, which will trigger a refetch.

examples/angular/unit-testing/angular.json (1)

1-113: Angular workspace configuration looks correct and follows best practices.

The configuration is standard, well-structured, and integrates Jest for testing. No issues found.

} from '@angular/common/http'
import type { Observable } from 'rxjs'

let callNumber = 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential test isolation issue with shared static variable.

The callNumber variable is declared at module level and not reset between tests, which could cause test interdependency issues.

Consider moving this variable inside the interceptor function or adding a reset method to ensure test isolation:

-let callNumber = 0;
+export const mockInterceptor: HttpInterceptorFn = (
+  req: HttpRequest<unknown>,
+  next: HttpHandlerFn,
+): Observable<HttpEvent<any>> => {
+  // Move counter inside function scope for better test isolation
+  static let callNumber = 0;

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +45 to +57
const tasks = JSON.parse(
sessionStorage.getItem('unit-testing-tasks') || '[]',
)
tasks.push(req.body)
sessionStorage.setItem(
'unit-testing-tasks',
JSON.stringify(tasks),
)
return respondWith(201, {
status: 'success',
task: req.body,
})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Wrap variable declaration in block scope within switch case.

The tasks variable declaration inside the switch case should be wrapped in a block to avoid potential scope issues.

case 'POST':
+  {
    const tasks = JSON.parse(
      sessionStorage.getItem('unit-testing-tasks') || '[]',
    )
    tasks.push(req.body)
    sessionStorage.setItem(
      'unit-testing-tasks',
      JSON.stringify(tasks),
    )
    return respondWith(201, {
      status: 'success',
      task: req.body,
    })
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const tasks = JSON.parse(
sessionStorage.getItem('unit-testing-tasks') || '[]',
)
tasks.push(req.body)
sessionStorage.setItem(
'unit-testing-tasks',
JSON.stringify(tasks),
)
return respondWith(201, {
status: 'success',
task: req.body,
})
}
case 'POST': {
const tasks = JSON.parse(
sessionStorage.getItem('unit-testing-tasks') || '[]',
)
tasks.push(req.body)
sessionStorage.setItem(
'unit-testing-tasks',
JSON.stringify(tasks),
)
return respondWith(201, {
status: 'success',
task: req.body,
})
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 45-47: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

Comment on lines +26 to +44
if (req.url === '/api/tasks') {
switch (req.method) {
case 'GET':
callNumber++;
if (callNumber === 1) {
return respondWith(
200,
JSON.parse(
sessionStorage.getItem('unit-testing-tasks') || '[]',
),
) } else {
return respondWith(
200,
JSON.parse(
sessionStorage.getItem('unit-testing-tasks') || '[]',
).concat([`CallNumber ${callNumber}`]),
)
}
case 'POST':
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Switch case with declaration issue.

There's a potential scoping issue with the variable declaration in the switch case. Additionally, the conditional response logic based on callNumber adds complexity to the mock.

Wrap the declaration in a block to restrict its access to the switch clause:

case 'GET':
  callNumber++;
  if (callNumber === 1) {
+  {
    return respondWith(
      200,
      JSON.parse(
        sessionStorage.getItem('unit-testing-tasks') || '[]',
      ),
    ) 
+  }
  } else {
+  {
    return respondWith(
      200,
      JSON.parse(
        sessionStorage.getItem('unit-testing-tasks') || '[]',
      ).concat([`CallNumber ${callNumber}`]),
    )
+  }
  }

Committable suggestion skipped: line range outside the PR's diff.

title: Testing
---

As there is currently no simple way to await a signal to reach a specific value we will use polling to wait in our test (instead of transforming our signals in observable and use RxJS features to filter the values). If you want to do like us for the polling you can use the angular testing library.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Polish grammar, punctuation, and style for clarity and professionalism.

The technical content is excellent, but the following refinements are recommended:

  • Add missing commas for better readability.
  • Use "into" instead of "in" for "transforming our signals into observables".
  • Use gerund form ("recommend using") for correct verb usage.
  • Fix repeated word ("the the Interceptor").
  • Add missing articles ("multiple ways of handling the mocking").

Here is a suggested diff for the most important corrections:

-As there is currently no simple way to await a signal to reach a specific value we will use polling to wait in our test (instead of transforming our signals in observable and use RxJS features to filter the values). If you want to do like us for the polling you can use the angular testing library.
+As there is currently no simple way to await a signal to reach a specific value, we will use polling to wait in our test (instead of transforming our signals into observables and using RxJS features to filter the values). If you want to do as we do for polling, you can use the Angular Testing Library.

-Otherwise we recommend to use the toObservable feature from Angular.
+Otherwise, we recommend using the toObservable feature from Angular.

-Because the recommendation is to use services that provide the Query options through function this is what we are going to do.
+Because the recommendation is to use services that provide the Query options through a function, this is what we are going to do.

-The library defaults to three retries with exponential backoff, which means that your tests are likely to timeout if you want to test an erroneous query. The easiest way to turn retries off is via the provideTanStackQuery during the TestBed setup as shown in the above example.
+The library defaults to three retries with exponential backoff, which means that your tests are likely to time out if you want to test an erroneous query. The easiest way to turn retries off is via provideTanStackQuery during the TestBed setup, as shown in the above example.

-Instead of targetting a server for the data you should mock the requests. There are multiple way of handling the mocking, we recommend to use the Interceptor from Angular, see [here](https://angular.dev/guide/http/interceptors) for more details.
-You can see the the Interceptor setup in the "Unit testing / Jest" examples.
+Instead of targeting a server for the data, you should mock the requests. There are multiple ways of handling the mocking; we recommend using the Interceptor from Angular. See [here](https://angular.dev/guide/http/interceptors) for more details.
+You can see the Interceptor setup in the "Unit testing / Jest" examples.

Also applies to: 13-14, 18-18, 166-166, 170-171

🧰 Tools
🪛 LanguageTool

[uncategorized] ~6-~6: A comma might be missing here.
Context: ...y to await a signal to reach a specific value we will use polling to wait in our test...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~6-~6: The preposition “into” seems more likely in this position than the preposition “in”.
Context: ...st (instead of transforming our signals in observable and use RxJS features to fil...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION_IN_INTO)


[uncategorized] ~6-~6: This verb may not be in the correct form. Consider using a different form for this context.
Context: ...nsforming our signals in observable and use RxJS features to filter the values). If...

(AI_EN_LECTOR_REPLACEMENT_VERB_FORM)


[uncategorized] ~6-~6: A comma might be missing here.
Context: ...ues). If you want to do like us for the polling you can use the angular testing library...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants