Skip to content

Conversation

arvi18
Copy link

@arvi18 arvi18 commented Aug 11, 2025

New Features

Introduced a comprehensive guide on testing Angular applications with TanStack Query, including examples and best practices.
Added a new Angular unit testing example project with full configuration, sample service, component, and mock API interceptor.
Provided ready-to-use Jest and ESLint configurations for Angular unit testing.
Included detailed documentation and setup instructions for the new unit testing example.
Bug Fixes

Improved error simulation in mock API interceptors for more accurate testing scenarios.
Refactor

Enhanced mutation and query lifecycle handling in Angular service examples for better type safety and control flow.
Documentation

Updated Angular documentation to include new testing guides and unit testing examples.

Summary by CodeRabbit

  • New Features

    • Added a Jest-based Angular unit-testing example (app, component, service, mock API, devcontainer).
  • Tests

    • Added integration-style tests for TasksService covering optimistic updates, rollbacks, success and failure flows.
  • Documentation

    • Added an Angular Testing guide, example README, and updated docs navigation with "Testing" and "Unit Testing / Jest".
  • Refactor

    • Tweaked example mutation behaviors and adjusted error signaling and cache invalidation flows.
  • Chores

    • Added configs for ESLint, Jest, Angular workspace, TypeScript, and devcontainer.

Copy link

coderabbitai bot commented Aug 11, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new Angular unit-testing example (app, tests, mocks, configs), a new Angular testing guide, and documentation navigation entries. Also updates several Angular example services and interceptors (mutation handlers, invalidateQueries returns, error signaling) and toggles a tsconfig flag for the devtools panel.

Changes

Cohort / File(s) Summary
Docs navigation entries
docs/config.json
Added "Testing" guide link and "Unit Testing / Jest" example link for Angular in documentation navigation.
Angular testing guide
docs/framework/angular/guides/testing.md
New guide describing Angular testing patterns, TasksService examples, TestBed setup, mock interceptor, retry guidance, and polling-for-signal testing approach.
Auto-refetching example
examples/angular/auto-refetching/src/app/services/tasks.service.ts
addTask and clearAllTasks onSuccess handlers now return the Promise from invalidateQueries instead of performing it as a void side-effect.
Optimistic-updates example
examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts, .../interceptor/mock-api.interceptor.ts, .../services/tasks.service.ts
Removed clearMutation binding. Interceptor error path now emits a delayed error via throwError. Service: typed onMutate/onError params, onSuccess made no-op, and onSettled now returns the invalidateQueries Promise.
Unit testing example — tooling & config
examples/angular/unit-testing/.devcontainer/devcontainer.json, .../.eslintrc.cjs, .../angular.json, .../jest.config.ts, .../package.json, .../tsconfig*.json, .../README.md
New Angular 19 + Jest example workspace: devcontainer, ESLint stub, workspace angular.json, Jest config, package.json, TypeScript configs, and README.
Unit testing example — app, tests & mocks
examples/angular/unit-testing/src/... (e.g. app.component.ts, app.config.ts, components/unit-testing.component.ts, interceptor/mock-api.interceptor.ts, services/tasks.service.ts, services/tasks.service.spec.ts, index.html, main.ts)
New standalone app, UnitTestingComponent, TasksService with query + optimistic add mutation, mockInterceptor using sessionStorage and delayed responses, and Jest tests validating optimistic updates and rollbacks; bootstrap files added.
Devtools panel config
examples/angular/devtools-panel/tsconfig.json
Toggled compileOnSave from false to true.

Sequence Diagram(s)

sequenceDiagram
  participant C as UnitTestingComponent
  participant S as TasksService
  participant QC as QueryClient
  participant HTTP as HttpClient
  participant INT as MockInterceptor

  C->>QC: injectQuery(allTasks)
  QC->>S: call queryFn()
  S->>HTTP: GET /api/tasks
  HTTP->>INT: intercept GET
  INT-->>HTTP: respond 200 [] (delayed)
  HTTP-->>S: return []
  S-->>QC: fulfill query
  QC-->>C: data=[]
  
  C->>QC: injectMutation(addTask).mutate({task, fail?})
  QC->>S: call mutationFn()
  S->>QC: onMutate (cancel + optimistic update)
  S->>HTTP: POST /api/tasks or /api/tasks-wrong-url
  HTTP->>INT: intercept POST
  alt success
    INT-->>HTTP: 201 success (delayed)
    HTTP-->>S: success
    S->>QC: onSuccess (no-op)
    S->>QC: onSettled -> return invalidateQueries()
  else error
    INT-->>HTTP: throw Error (delayed)
    HTTP-->>S: error
    S->>QC: onError -> rollback using context
    S->>QC: onSettled -> return invalidateQueries()
  end
  QC->>S: refetch allTasks
Loading
sequenceDiagram
  participant TB as TestBed
  participant RIC as runInInjectionContext
  participant S as TasksService
  participant QC as QueryClient
  participant INT as MockInterceptor

  TB->>RIC: provideHttpClient(withInterceptors([mockInterceptor]))
  TB->>RIC: provideTanStackQuery(new QueryClient({ defaultOptions..., retries:0 }))
  RIC->>S: instantiate TasksService
  RIC->>QC: create query & mutation (allTasks, addTask)
  QC->>INT: HTTP interactions intercepted & delayed
  TB->>QC: poll isFetching / inspect getQueryData for optimistic updates and rollbacks
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🥕 I hopped through code with whiskers bright,
New mocks and tests to cheer the night.
Queries leap, then gently fall back,
Optimistic hops keep state on track.
Docs and demos — a carrot-stack delight.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch clone-feat/docsAngularTesting

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit 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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @arvi18, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

I've focused on significantly improving the Angular documentation and examples related to testing with TanStack Query. This pull request introduces a dedicated guide for testing best practices and a fully functional unit testing example project. Additionally, I've made minor but important adjustments to existing examples to enhance error simulation and refine mutation lifecycle handling, ensuring a more robust and type-safe experience for developers.

Highlights

  • New Angular Testing Documentation: I've added a brand new documentation guide at docs/framework/angular/guides/testing.md. This guide provides comprehensive information on how to effectively test Angular applications that utilize TanStack Query, including strategies for handling asynchronous operations like polling with @testing-library/angular and managing query retries in tests.
  • New Angular Unit Testing Example Project: A complete new example project, examples/angular/unit-testing/, has been introduced. This project serves as a practical demonstration of setting up and performing unit tests for Angular applications with TanStack Query, featuring Jest and ESLint configurations, a sample service, a component, and a mock API interceptor for realistic testing scenarios.
  • Improved Error Simulation in Mock API Interceptors: The mock API interceptors in both the optimistic-updates example and the new unit-testing example have been refined. They now use throwError from RxJS for simulating network errors, which provides a more accurate and robust way to test error handling compared to simply returning a 500 status response.
  • Enhanced Mutation and Query Lifecycle Handling: I've enhanced the mutation and query lifecycle handling in the examples/angular/auto-refetching/src/app/services/tasks.service.ts and examples/angular/optimistic-updates/src/app/services/tasks.service.ts files. This includes explicitly returning invalidateQueries calls in onSuccess and onSettled callbacks and improving type safety for onMutate and onError callback parameters, leading to more predictable and robust state management.
  • Documentation Navigation Updates: The docs/config.json file has been updated to seamlessly integrate the new content into the documentation navigation. This includes adding direct links to the new 'Testing' guide and the 'Unit Testing / Jest' example, making them easily discoverable for users.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

refacto-test bot commented Aug 11, 2025

🤖 Refacto.AI: PR Summary & Reviewer Guide

🎯 1. The Goal (Why was this PR raised?)

This PR adds comprehensive documentation and examples for testing Angular applications that use TanStack Query. It introduces both testing guides and a complete working example demonstrating best practices for unit testing Angular services with TanStack Query.

🗺️ 2. The Changes (What was implemented?)

File Name Implementation Details Goal / Requirement Addressed
docs/framework/angular/guides/testing.md (Added) New documentation guide for testing Angular applications with TanStack Query, including code examples and best practices. Add Angular testing documentation
docs/config.json (Modified) Updated navigation configuration to include the new testing guide and example in the documentation menu. Add navigation links to new content
examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (Added) Added comprehensive test file for TasksService demonstrating how to test queries and mutations, including optimistic updates. Provide working test example
examples/angular/unit-testing/src/app/services/tasks.service.ts (Added) Implementation of TasksService with query and mutation methods that will be tested. Provide service implementation for tests
examples/angular/unit-testing/src/app/components/unit-testing.component.ts (Added) Angular component that uses the TasksService to demonstrate functionality. Create demo component for the example
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (Added) HTTP interceptor that mocks API responses for testing. Implement mock API for testing
examples/angular/unit-testing/src/app/app.config.ts (Added) Application configuration that sets up HTTP client and TanStack Query. Configure the example application
examples/angular/unit-testing/src/app/app.component.ts (Added) Root component that renders the unit testing example. Set up example application structure
examples/angular/unit-testing/angular.json (Added) Angular CLI configuration for the unit testing example project. Configure Angular project
examples/angular/unit-testing/package.json (Added) NPM package configuration with dependencies for the example. Set up project dependencies
examples/angular/unit-testing/tsconfig.json (Added) TypeScript configuration for the example project. Configure TypeScript
examples/angular/unit-testing/tsconfig.app.json (Added) TypeScript configuration specific to the application build. Configure TypeScript for app
examples/angular/unit-testing/tsconfig.spec.json (Added) TypeScript configuration specific to tests. Configure TypeScript for tests
examples/angular/unit-testing/jest.config.ts (Added) Jest configuration for running tests. Configure test runner
examples/angular/unit-testing/.eslintrc.cjs (Added) ESLint configuration for the example. Set up linting
examples/angular/unit-testing/src/index.html (Added) HTML entry point for the example application. Create application entry point
examples/angular/unit-testing/src/main.ts (Added) Application bootstrap file. Set up application initialization
examples/angular/unit-testing/README.md (Added) Documentation for running the example. Provide usage instructions
examples/angular/unit-testing/.devcontainer/devcontainer.json (Added) Development container configuration. Support containerized development
examples/angular/optimistic-updates/src/app/services/tasks.service.ts (Modified) Updated type definitions and return statements in service methods. Improve type safety and consistency
examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts (Modified) Changed error response handling to use throwError instead of returning a response object. Improve error handling consistency
examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts (Modified) Removed unused clearMutation declaration. Clean up unused code
examples/angular/auto-refetching/src/app/services/tasks.service.ts (Modified) Updated invalidateQueries calls to return the result. Improve promise chain handling

🤔 3. Key Areas for Human Review

Area of Concern: Testing Documentation Completeness

  • File: docs/framework/angular/guides/testing.md
  • Why: This is the main documentation file that explains testing practices for Angular with TanStack Query. It's critical that it covers all necessary aspects of testing.
  • Testing Instruction: Review the documentation to ensure it covers: setting up test environment, mocking HTTP requests, testing queries, testing mutations, and handling asynchronous operations. Compare with existing testing docs for other frameworks to ensure consistent coverage.

Area of Concern: Test Example Implementation

  • File: examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (Lines 50-111)
  • Why: This file contains complex test logic including optimistic updates and error handling tests. These are advanced scenarios that need careful review.
  • Testing Instruction: Run the tests in the example project to verify they pass. Check that the tests properly validate both success and error paths, and that they correctly test optimistic updates behavior.

Area of Concern: Mock API Interceptor

  • File: examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts
  • Why: The mock interceptor is critical for testing as it simulates API responses. Any issues here could lead to misleading test results.
  • Testing Instruction: Review the interceptor implementation to ensure it properly simulates both success and error responses, and that it correctly handles different HTTP methods. Verify that the delay simulation works as expected.

Area of Concern: Consistency with Other Frameworks

  • File: docs/framework/angular/guides/testing.md and examples/angular/unit-testing/
  • Why: TanStack Query supports multiple frameworks, and testing approaches should be consistent where appropriate while respecting Angular's testing patterns.
  • Testing Instruction: Compare the Angular testing documentation with existing documentation for React or Vue to ensure consistent recommendations where applicable, while respecting Angular's specific testing patterns and tools.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces comprehensive documentation and a new example project for testing Angular applications with TanStack Query. The changes are extensive and well-structured. My review focuses on improving the new documentation and example code by enhancing type safety and addressing potential issues like stateful mocks that could lead to flaky tests. I've provided specific suggestions to use proper TypeScript types instead of any, fix an error in a code example, and improve the robustness of the test setup.

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

let callNumber = 0;

Choose a reason for hiding this comment

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

high

The callNumber variable is declared at the module level, which makes it stateful across multiple tests if they run in the same process without module cache invalidation. This can lead to flaky and unpredictable test outcomes. It's crucial to reset this state before each test. Consider exporting a reset function from this module to be called in a beforeEach block in your spec files.

Example:

// In mock-api.interceptor.ts
export function resetCallNumber() {
  callNumber = 0;
}

// In your spec file
import { resetCallNumber } from '...';

describe('...', () => {
  beforeEach(() => {
    resetCallNumber();
    // ... other setup
  });
});

),
mutationKey: ['tasks'],
onSuccess: () => {},
onMutate: async ({ task }) => {

Choose a reason for hiding this comment

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

medium

The onMutate callback parameter is not typed. Adding a type for the destructured task property will improve type safety and readability.

      onMutate: async ({ task }: { task: string }) => {


return previousTodos
},
onError: (_err, _variables, context) => {

Choose a reason for hiding this comment

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

medium

The parameters for the onError callback are not typed, which can lead to less maintainable code. It's best practice to explicitly type them.

      onError: (_err: Error, _variables: { task: string; failMutation: boolean; }, context: string[] | undefined) => {

@arvi18
Copy link
Author

arvi18 commented Aug 11, 2025

/refacto-test

Copy link

refacto-test bot commented Aug 11, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

Copy link

refacto-test bot commented Aug 11, 2025

Code Review: Angular Testing with TanStack Query

👍 Well Done
Comprehensive Test Examples

Thorough test patterns demonstrate proper error handling and query invalidation.

Mock Interceptor Implementation

Properly simulates API responses with error handling for testing reliability.

📌 Files Processed
  • examples/angular/unit-testing/src/app/services/tasks.service.spec.ts
  • examples/angular/unit-testing/src/app/components/unit-testing.component.ts
  • examples/angular/unit-testing/src/app/services/tasks.service.ts
  • pnpm-lock.yaml
  • examples/angular/unit-testing/src/app/app.config.ts
  • examples/angular/unit-testing/angular.json
  • examples/angular/unit-testing/package.json
  • examples/angular/unit-testing/tsconfig.json
  • docs/framework/angular/guides/testing.md
  • examples/angular/unit-testing/src/index.html
  • examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts
  • examples/angular/unit-testing/.eslintrc.cjs
  • docs/config.json
  • examples/angular/unit-testing/src/app/app.component.ts
  • examples/angular/optimistic-updates/src/app/services/tasks.service.ts
  • examples/angular/unit-testing/tsconfig.app.json
  • examples/angular/unit-testing/jest.config.ts
  • examples/angular/unit-testing/tsconfig.spec.json
  • examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts
  • examples/angular/unit-testing/README.md
  • examples/angular/unit-testing/src/main.ts
  • examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts
  • examples/angular/auto-refetching/src/app/services/tasks.service.ts
  • examples/angular/unit-testing/.devcontainer/devcontainer.json
📝 Additional Comments
examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (2)
Missing Session Cleanup

Tests don't clean up sessionStorage between runs. Residual data from previous test runs could affect test reliability and cause inconsistent test results.

beforeEach(() => {
  // Clear sessionStorage to prevent test pollution
  sessionStorage.removeItem('unit-testing-tasks');
  
  TestBed.configureTestingModule({
    // Rest of the existing configuration...

Standards:

  • ISO-IEC-25010-Reliability-Recoverability
  • SRE-Test-Isolation
Timeout Value Hardcoding

Multiple hardcoded 10-second timeouts in tests can lead to unnecessarily long test execution times. Tests should fail fast when conditions aren't met, improving CI/CD pipeline efficiency.

// At the top of the file
const TEST_TIMEOUT = 3000; // 3 seconds is typically sufficient

// Then in the tests
await waitFor(() => expect(allTasks.isFetching()).toBe(false), {timeout: TEST_TIMEOUT});
await waitFor(() => expect(allTasks.data()).toEqual([task, 'CallNumber 2']), {timeout: TEST_TIMEOUT});
await waitFor(() => expect(addTask.isIdle()).toBe(false), {timeout: TEST_TIMEOUT});
await waitFor(() => expect(addTask.isPending()).toBe(false), {timeout: TEST_TIMEOUT});

Standards:

  • ISO-IEC-25010-Performance-Time-Behaviour
  • Netflix-Test-Efficiency
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (1)
Session Storage Security

Using sessionStorage for sensitive data in tests. While acceptable for testing, this pattern could lead to insecure storage in production code. Sensitive data could be exposed to client-side scripts.

// NOTE: sessionStorage is used only for testing purposes
// In production, sensitive data should use secure server-side storage
JSON.parse(
  sessionStorage.getItem('unit-testing-tasks') || '[]',
),

Standards:

  • CWE-922
  • OWASP-A04

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: 5

🧹 Nitpick comments (23)
examples/angular/unit-testing/jest.config.ts (1)

1-1: Fix TypeScript import for better type safety.

The ESLint error indicates that import() type annotations should be avoided in favor of explicit imports.

Apply this diff to use a proper import:

+import type { Config } from 'jest';
+
-const config: import('jest').Config = {
+const config: Config = {
examples/angular/unit-testing/tsconfig.spec.json (1)

3-7: Consider adding Node type definitions if tests reference Node globals

If your Jest setup or helpers reference Node globals (e.g., process, __dirname, setImmediate), add node to the types array. Otherwise, this is fine as-is.

Example:

-      "types": ["jest"]
+      "types": ["jest", "node"]
examples/angular/unit-testing/.eslintrc.cjs (1)

3-6: Empty ESLint config — consider extending recommended presets for Angular, TypeScript, and Jest

If this example is meant to be self-contained, extend standard presets. If you rely on a repo-level config, ignore this.

Proposed minimal config (requires corresponding deps installed):

-/** @type {import('eslint').Linter.Config} */
-const config = {}
-
-module.exports = config
+/** @type {import('eslint').Linter.Config} */
+const config = {
+  root: true,
+  env: { es2021: true, browser: true, jest: true },
+  extends: [
+    'eslint:recommended',
+    'plugin:@typescript-eslint/recommended',
+    'plugin:@angular-eslint/recommended',
+    'plugin:jest/recommended',
+  ],
+  parser: '@typescript-eslint/parser',
+  parserOptions: {
+    sourceType: 'module',
+    project: ['./tsconfig.json', './tsconfig.spec.json'],
+    tsconfigRootDir: __dirname,
+  },
+  overrides: [
+    {
+      files: ['**/*.spec.ts'],
+      env: { jest: true },
+    },
+  ],
+}
+
+module.exports = config
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (2)

18-19: Stateful callNumber may cause cross-test coupling

callNumber persists across GETs and tests. Ensure tests reset it (or sessionStorage) between runs to avoid order-dependent assertions.

Options:

  • Reset between tests (e.g., in beforeEach): callNumber = 0; sessionStorage.removeItem('unit-testing-tasks')
  • Or derive the suffix purely from storage length to keep it stateless.

28-43: Minor readability: de-duplicate JSON.parse and simplify GET case

Not required, but factoring tasks into a local constant avoids repeating JSON.parse and clarifies the first vs subsequent call paths.

Example:

-      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 'GET': {
+        callNumber++
+        const tasks: string[] = JSON.parse(
+          sessionStorage.getItem('unit-testing-tasks') || '[]',
+        )
+        return callNumber === 1
+          ? respondWith(200, tasks)
+          : respondWith(200, tasks.concat([`CallNumber ${callNumber}`]))
+      }
examples/angular/optimistic-updates/src/app/services/tasks.service.ts (2)

50-50: Remove no-op onSuccess for clarity

Since onSettled handles invalidation, this empty handler adds noise.

-      onSuccess: () => {},

51-75: Align onMutate/variables typing to the mutation input for clarity

onMutate narrows variables to { task: string } while mutationFn accepts { task: string; failMutation: boolean }. It works, but aligning types improves readability and future refactors.

Example:

-      onMutate: async ({ task } : {task: string}) => {
+      onMutate: async ({ task }: { task: string; failMutation: boolean }) => {
         // Cancel any outgoing refetches

Optionally define a shared type:

type AddTaskVariables = { task: string; failMutation: boolean }

…and use it in mutationFn, onMutate, and onError.

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

15-16: Use readonly private fields for consistency and intent

Matches other examples and prevents accidental reassignment.

-  #queryClient = inject(QueryClient) // Manages query state and caching
-  #http = inject(HttpClient) // Handles HTTP requests
+  readonly #queryClient = inject(QueryClient) // Manages query state and caching
+  readonly #http = inject(HttpClient) // Handles HTTP requests

31-33: Update the comment to reflect actual invalidation behavior

Invalidation happens in onSettled, while onSuccess is a no-op here.

-   * On success, invalidates and refetch the "tasks" query cache to update the task list.
+   * Uses optimistic updates; always invalidates and refetches the "tasks" query in onSettled.

51-55: Add an explicit type to onMutate parameter for clarity

It’s inferred, but explicit typing helps readability and prevents regressions.

-      onMutate: async ({ task }) => {
+      onMutate: async ({ task }: { task: string; failMutation: boolean }) => {

Optional: define a shared type and reuse across mutationFn, onMutate, and onError:

type AddTaskVariables = { task: string; failMutation: boolean }
examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (6)

1-1: Use single quotes for consistency

Angular style guide recommends single quotes for imports.

-import { TestBed } from "@angular/core/testing";
+import { TestBed } from '@angular/core/testing';

2-2: Use single quotes for consistency

-import { provideHttpClient, withFetch, withInterceptors } from "@angular/common/http";
+import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';

3-3: Use single quotes for consistency

-import { QueryClient, injectMutation, injectQuery, provideTanStackQuery } from "@tanstack/angular-query-experimental";
+import { QueryClient, injectMutation, injectQuery, provideTanStackQuery } from '@tanstack/angular-query-experimental';

4-4: Use single quotes for consistency

-import { Injector, inject, runInInjectionContext } from "@angular/core";
+import { Injector, inject, runInInjectionContext } from '@angular/core';

6-8: Use single quotes for consistency

-import { mockInterceptor } from "../interceptor/mock-api.interceptor";
-import { TasksService } from "./tasks.service";
-import type { CreateQueryResult} from "@tanstack/angular-query-experimental";
+import { mockInterceptor } from '../interceptor/mock-api.interceptor';
+import { TasksService } from './tasks.service';
+import type { CreateQueryResult, CreateMutationResult } from '@tanstack/angular-query-experimental';

110-110: Add missing semicolon

-      expect(allTasks.data()).toEqual([task, 'CallNumber 2'])
+      expect(allTasks.data()).toEqual([task, 'CallNumber 2']);
examples/angular/unit-testing/angular.json (2)

81-81: Default build to development for examples (faster DX)

For a docs/example project, using development as the default build configuration improves iteration speed and keeps source maps on. Consider switching the default or aligning serve/build defaults.

-          "defaultConfiguration": "production"
+          "defaultConfiguration": "development"

15-41: Reconsider global skipTests in a testing-focused example

Given this project teaches testing, you might want the schematics to generate test files by default (at least for services/components) to reduce friction for learners.

-        "@schematics/angular:component": {
-          "inlineTemplate": true,
-          "inlineStyle": true,
-          "skipTests": true
-        },
+        "@schematics/angular:component": {
+          "inlineTemplate": true,
+          "inlineStyle": true,
+          "skipTests": false
+        },
-        "@schematics/angular:service": {
-          "skipTests": true
-        }
+        "@schematics/angular:service": {
+          "skipTests": false
+        }
docs/framework/angular/guides/testing.md (5)

6-15: Clarify guidance, fix grammar, and reference toObservable correctly

Tighten phrasing, correct grammar, and add the import location for toObservable.

-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’ll use polling in tests (instead of transforming signals into observables and filtering with RxJS). If you prefer polling, you can use the Angular Testing Library.
@@
-Otherwise we recommend to use the toObservable feature from Angular.
+Otherwise, we recommend using Angular’s toObservable utility:
+
+import { toObservable } from '@angular/core/rxjs-interop'

41-51: Return type comment mismatch: this returns a Promise, not an Observable

The code uses lastValueFrom and returns a Promise. Adjust the comment to avoid confusion.

-  /**
-   * Fetches all tasks from the API.
-   * Returns an observable containing an array of task strings.
-   */
+  /**
+   * Fetches all tasks from the API.
+   * Returns a Promise that resolves to an array of task strings.
+   */

56-65: Optional: strengthen types for the query result

Since you already import CreateQueryResult, you can type the variable for better IntelliSense and safety.

-import type { CreateQueryResult} from "@tanstack/angular-query-experimental";
+import type { CreateQueryResult } from "@tanstack/angular-query-experimental";
@@
-  let allTasks: any;
+  let allTasks: CreateQueryResult<Array<string>>;

Also applies to: 91-95


126-133: Speed up tests by reducing artificial delays or using fake timers

The interceptor adds 1000ms delays to every call, which slows the suite. Consider reducing to 50–100ms, or use Jest fake timers and advanceTimersByTime in tests.

-  const respondWith = (status: number, body: any) =>
-    of(new HttpResponse({ status, body })).pipe(delay(1000))
+  const respondWith = (status: number, body: any) =>
+    of(new HttpResponse({ status, body })).pipe(delay(100))
@@
-  if (req.url === '/api/tasks-wrong-url') {
-    return throwError(() => new Error('error')).pipe(delay(1000));
+  if (req.url === '/api/tasks-wrong-url') {
+    return throwError(() => new Error('error')).pipe(delay(100));

Also applies to: 156-161


170-171: Fix typos and pluralization

Minor editorial fixes for clarity.

-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 data, you should mock requests. There are multiple ways to handle mocking; we recommend using an Angular HTTP interceptor. See [the docs](https://angular.dev/guide/http/interceptors) for details.
+You can see the interceptor setup in the “Unit testing / Jest” examples.
📜 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 (7)
examples/angular/unit-testing/src/app/app.component.ts (2)
examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts (1)
  • Component (10-72)
examples/angular/unit-testing/src/app/components/unit-testing.component.ts (1)
  • Component (10-67)
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/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/app/components/unit-testing.component.ts (4)
examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts (1)
  • Component (10-72)
examples/angular/unit-testing/src/app/app.component.ts (1)
  • Component (4-11)
packages/angular-query-experimental/src/inject-query.ts (1)
  • injectQuery (218-225)
packages/angular-query-experimental/src/inject-mutation.ts (1)
  • injectMutation (41-174)
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.spec.ts (7)
examples/angular/optimistic-updates/src/app/services/tasks.service.ts (1)
  • addTask (34-81)
examples/angular/unit-testing/src/app/services/tasks.service.ts (1)
  • addTask (34-82)
examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts (1)
  • mockInterceptor (18-53)
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (1)
  • mockInterceptor (20-64)
packages/angular-query-experimental/src/providers.ts (1)
  • provideTanStackQuery (93-111)
packages/angular-query-experimental/src/inject-query.ts (1)
  • injectQuery (218-225)
packages/angular-query-experimental/src/inject-mutation.ts (1)
  • injectMutation (41-174)
examples/angular/unit-testing/src/app/services/tasks.service.ts (2)
examples/angular/auto-refetching/src/app/services/tasks.service.ts (1)
  • Injectable (11-59)
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)

🪛 Biome (2.1.2)
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:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (19)
examples/angular/unit-testing/.devcontainer/devcontainer.json (1)

1-4: LGTM!

The devcontainer configuration is clean and appropriate for Node.js 22 development environment.

examples/angular/unit-testing/README.md (1)

1-7: LGTM!

The README provides clear and comprehensive instructions covering all major package managers and essential commands.

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

1-13: LGTM!

Standard and well-structured HTML5 boilerplate for Angular application with appropriate meta tags and bootstrap element.

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

1-5: Jest configuration is functional and appropriate.

The configuration correctly sets up Jest for the Angular project with proper source directory roots.

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

40-42: Improved async handling in mutation callback.

Returning the promise from invalidateQueries provides better control flow and allows callers to wait for cache invalidation completion.


54-56: Consistent async handling across mutations.

Good consistency with the addTask mutation - both now return promises from their onSuccess callbacks.

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

9-9: Good addition of throwError for improved error simulation.

Adding throwError import enables more realistic error scenarios for testing.


49-49: Enhanced error simulation for better testing.

Using throwError instead of a 500 HttpResponse provides more realistic error scenarios that better simulate actual network failures.

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

1-37: Comprehensive and well-structured package configuration.

The dependencies are appropriate for Angular 19 with Jest testing setup. Good inclusion of Testing Library packages and consistent version pinning.

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

1-32: Excellent TypeScript configuration with comprehensive strict settings.

The configuration enables all recommended strict type checking options and Angular compiler strictness settings, promoting type safety and best practices.

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

77-79: LGTM: returning invalidateQueries from onSettled

Returning the promise is useful for test determinism and consistent async handling.

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

77-79: LGTM: returning invalidateQueries from onSettled

Good for deterministic tests and consistent async flows.

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

1-9: LGTM!

Standard Angular TypeScript configuration setup. The empty types array correctly excludes test types from the app build.

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

1-5: LGTM!

Standard Angular standalone application bootstrap with proper error handling.

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

1-11: LGTM!

Clean root component setup with OnPush change detection strategy and proper standalone configuration.

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

1-28: LGTM!

Well-configured application setup with HTTP client, mock interceptor, and TanStack Query. The 24-hour gcTime is appropriate for the testing example.

docs/config.json (1)

710-713: LGTM!

Documentation navigation entries correctly added for Angular testing guide and unit testing example.

Also applies to: 1291-1294

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

1-67: LGTM!

Well-structured component demonstrating TanStack Query integration with Angular. Proper use of OnPush change detection, new control flow syntax, and reactive state management.

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

46-111: Well-structured and comprehensive test coverage!

The test suite effectively covers:

  • Initial query state validation
  • Successful mutations with optimistic updates
  • Error handling with rollback behavior
  • Query cache inspection and validation
  • Proper async testing patterns with waitFor

Great job on providing thorough test examples for the Angular TanStack Query integration.

Comment on lines +95 to +101
"test": {
"builder": "@angular-builders/jest:run",
"options": {
"tsConfig": "tsconfig.spec.json",
"configPath": "jest.config.ts",
"rootDir": "."
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify Jest builder/config presence and dependencies

Ensure the referenced Jest setup exists and required packages are installed to avoid CI/runtime failures.

Run this script to validate presence and dependencies:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

BASE="examples/angular/unit-testing"

# Check files referenced by the 'test' target
for f in "$BASE/jest.config.ts" "$BASE/tsconfig.spec.json" "$BASE/angular.json"; do
  if [[ ! -f "$f" ]]; then
    echo "Missing file: $f" && exit 1
  fi
done

# Check package.json for jest builder and preset
PKG="$BASE/package.json"
if [[ -f "$PKG" ]]; then
  echo "Checking devDependencies in $PKG"
  jq -e '.devDependencies["@angular-builders/jest"]' "$PKG" >/dev/null || { echo "Missing @angular-builders/jest"; exit 1; }
  jq -e '.devDependencies["jest"]' "$PKG" >/dev/null || { echo "Missing jest"; exit 1; }
  jq -e '.devDependencies["jest-preset-angular"]' "$PKG" >/dev/null || { echo "Missing jest-preset-angular"; exit 1; }
else
  echo "Missing $PKG" && exit 1
fi

# Check zone.js is installed (polyfills includes it)
jq -e '.dependencies["zone.js"] // .devDependencies["zone.js"]' "$PKG" >/dev/null || { echo "Missing zone.js dependency"; exit 1; }

echo "Jest setup looks present."

Length of output: 1073


🏁 Script executed:

#!/bin/bash
set -euo pipefail

PKG="examples/angular/unit-testing/package.json"

echo "Zone.js in dependencies:  $(jq -r '.dependencies["zone.js"] // "none"' $PKG)"
echo "Zone.js in devDependencies: $(jq -r '.devDependencies["zone.js"] // "none"' $PKG)"

Length of output: 399


Missing jest-preset-angular in devDependencies

Our verification shows that while @angular-builders/jest, jest, and zone.js are present, the jest-preset-angular package is not listed under devDependencies in examples/angular/unit-testing/package.json. Without it, the Jest setup will fail at runtime.

Please update your package.json:

• File: examples/angular/unit-testing/package.json

 "devDependencies": {
   "@angular-builders/jest": "<your-version>",
   "jest": "<your-version>",
+    "jest-preset-angular": "<compatible-version>"
 }

Once added, re-run npm install (or yarn) and verify the tests execute successfully.

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

🤖 Prompt for AI Agents
examples/angular/unit-testing/package.json: add the missing jest-preset-angular
entry to devDependencies so Jest can run with the Angular preset; update
package.json by adding a compatible "jest-preset-angular" version to
devDependencies (choose a version that matches your Angular/Jest setup), save
the file, then run npm install or yarn and verify tests run successfully.

Comment on lines +44 to +57
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,
})
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix switch-case declaration scoping (noSwitchDeclarations)

Declaring const tasks directly in a switch case can leak across cases. Wrap the case body in a block.

-      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,
-        })
+      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 (2.1.2)

[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:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🤖 Prompt for AI Agents
In examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts
around lines 44 to 57, the switch case for 'POST' declares const tasks directly
which can leak into other cases; wrap the entire case body in a block (add { ...
} after case 'POST':) and move the const/let declarations and sessionStorage
manipulations inside that block so the variables are scoped to the case.

Comment on lines +59 to +61
if (req.url === '/api/tasks-wrong-url') {
return throwError(() => new Error('error')).pipe(delay(1000));
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simulate HTTP errors with HttpErrorResponse for better realism

Returning a plain Error loses HTTP context (status, statusText, url). Prefer HttpErrorResponse so HttpClient error flows mirror real backend failures.

-  if (req.url === '/api/tasks-wrong-url') {
-    return throwError(() => new Error('error')).pipe(delay(1000));
-  }
+  if (req.url === '/api/tasks-wrong-url') {
+    return throwError(
+      () =>
+        new HttpErrorResponse({
+          status: 500,
+          statusText: 'Internal Server Error',
+          url: req.url,
+          error: { message: 'error' },
+        }),
+    ).pipe(delay(1000))
+  }

Additionally add this import at the top:

-import { HttpResponse } from '@angular/common/http'
+import { HttpResponse, HttpErrorResponse } from '@angular/common/http'
📝 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
if (req.url === '/api/tasks-wrong-url') {
return throwError(() => new Error('error')).pipe(delay(1000));
}
// At the top of mock-api.interceptor.ts
-import { HttpResponse } from '@angular/common/http';
+import { HttpResponse, HttpErrorResponse } from '@angular/common/http';
// …later, around line 59…
if (req.url === '/api/tasks-wrong-url') {
- return throwError(() => new Error('error')).pipe(delay(1000));
+ return throwError(
+ () =>
+ new HttpErrorResponse({
+ status: 500,
+ statusText: 'Internal Server Error',
+ url: req.url,
+ error: { message: 'error' },
+ }),
+ ).pipe(delay(1000));
}
🤖 Prompt for AI Agents
In examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts
around lines 59 to 61, the interceptor returns a plain Error for the simulated
HTTP failure which loses HTTP context; replace the throwError(() => new
Error('error')) with throwError(() => new HttpErrorResponse({ status: 500,
statusText: 'Server Error', url: req.url, error: 'error' })) and ensure you add
the import for HttpErrorResponse from '@angular/common/http' at the top of the
file so the simulated error includes status, statusText and url like a real
backend response.

@arvi18
Copy link
Author

arvi18 commented Aug 11, 2025

/refacto-test

Copy link

refacto-test bot commented Aug 11, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

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: 0

🧹 Nitpick comments (1)
examples/angular/devtools-panel/tsconfig.json (1)

2-2: Is compileOnSave necessary here? It’s editor-specific and often a no-op with modern Angular toolchains.

This flag is primarily honored by certain editors (e.g., Visual Studio). VS Code and CLI/vite watchers typically ignore it, so it may add confusion without effect. If this is intentional for a specific workflow, consider documenting it in the devtools-panel README. Otherwise, consider removing for clarity.

Optional cleanup:

-  "compileOnSave": true,
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cb6cea3 and 2d6c145.

📒 Files selected for processing (1)
  • examples/angular/devtools-panel/tsconfig.json (1 hunks)

@arvi18
Copy link
Author

arvi18 commented Sep 2, 2025

@coderabbitai full review

Copy link

coderabbitai bot commented Sep 2, 2025

✅ Actions performed

Full review triggered.

@arvi18
Copy link
Author

arvi18 commented Sep 2, 2025

/refacto-test

Copy link

refacto-test bot commented Sep 2, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

Copy link

refacto-test bot commented Sep 2, 2025

Code Review: Angular Testing Implementation

👍 Well Done
Comprehensive Testing Documentation

Detailed testing guidance improves reliability through structured test practices.

Optimistic Update Implementation

Efficient cache management with optimistic updates reduces perceived latency.

Mock Interceptor Implementation

Proper HTTP request mocking enables reliable test execution.

📌 Files Processed
  • examples/angular/unit-testing/src/app/services/tasks.service.spec.ts
  • examples/angular/unit-testing/src/app/components/unit-testing.component.ts
  • examples/angular/unit-testing/src/app/services/tasks.service.ts
  • pnpm-lock.yaml
  • examples/angular/unit-testing/src/app/app.config.ts
  • examples/angular/unit-testing/angular.json
  • examples/angular/unit-testing/package.json
  • examples/angular/unit-testing/tsconfig.json
  • docs/framework/angular/guides/testing.md
  • examples/angular/unit-testing/src/index.html
  • examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts
  • examples/angular/unit-testing/.eslintrc.cjs
  • docs/config.json
  • examples/angular/unit-testing/src/app/app.component.ts
  • examples/angular/optimistic-updates/src/app/services/tasks.service.ts
  • examples/angular/devtools-panel/tsconfig.json
  • examples/angular/unit-testing/tsconfig.app.json
  • examples/angular/unit-testing/jest.config.ts
  • examples/angular/unit-testing/tsconfig.spec.json
  • examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts
  • examples/angular/unit-testing/README.md
  • examples/angular/unit-testing/src/main.ts
  • examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts
  • examples/angular/auto-refetching/src/app/services/tasks.service.ts
  • examples/angular/unit-testing/.devcontainer/devcontainer.json
📝 Additional Comments
examples/angular/unit-testing/src/app/services/tasks.service.ts (2)
Missing Return Value

The onSuccess handler doesn't return the invalidation promise unlike other examples. While the code works, returning the promise enables proper promise chaining for reliable test execution.

Standards:

  • ISO-IEC-25010-Functional-Correctness-Appropriateness
  • DbC-Resource-Mgmt
Secure Error Handling

Error handling silently ignores errors without logging. Attackers could exploit failed operations without detection. Implement proper error logging for security monitoring.

Standards:

  • CWE-778
  • OWASP-A09
examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (4)
Missing Error Handling

Empty onError handler in test lacks error assertions. This could mask test failures by silently ignoring errors, reducing test reliability and error detection.

Standards:

  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness
Test Timeout Value

The test uses a 10-second timeout which is unnecessarily high for a mocked API that has a 1-second delay. This could slow down the test suite execution time. Consider reducing the timeout value to 2-3 seconds which would still provide sufficient buffer for the mock API's 1-second delay while keeping test execution times optimal.

Standards:

  • ISO-IEC-25010-Performance-Time-Behaviour
  • Google-Performance-Best-Practices
  • Test-Performance-Optimization
Redundant Assertion Logic

Test logic checks queryClient state directly and then redundantly via allTasks.data(). Duplicated assertions create maintenance burden and potential inconsistency if one assertion is updated without the other.

Standards:

  • Logic-Verification-Efficiency
  • Algorithm-Correctness-Test-Design
Type Safety Improvement

Using 'any' type bypasses TypeScript's type checking benefits. This reduces code maintainability by hiding potential type errors during refactoring and making IDE assistance less effective.

Standards:

  • Clean-Code-Type-Safety
  • Design-Pattern-Type-Declaration
examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts (1)
Unused Import Removal

Removed clearMutation but left unused import. Removing unused code improves maintainability by reducing cognitive load and preventing confusion about which functionality is actually used.

Standards:

  • Clean-Code-Unused-Code
  • Clean-Code-Simplicity

this.#queryClient.invalidateQueries({ queryKey: ['tasks'] })
},
onMutate: async ({ task }) => {
onSuccess: () => {},
Copy link

Choose a reason for hiding this comment

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

Inconsistent Query Invalidation

The onSuccess handler no longer invalidates queries unlike other examples. This inconsistency could cause stale data to persist in the cache, affecting functional correctness.

Standards
  • ISO-IEC-25010-Functional-Correctness-Appropriateness
  • ISO-IEC-25010-Reliability-Maturity

Comment on lines +97 to +99
onError: (data: any, _variables: any, _context: any) => {
expect(data).toEqual(new Error('error'));
}
Copy link

Choose a reason for hiding this comment

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

Incomplete Error Handling

Error callback doesn't verify that optimistic update was rolled back. Missing validation that queryClient state was properly restored after mutation error, which is a critical part of the optimistic update pattern.

Standards
  • Logic-Verification-Completeness
  • Business-Rule-Validation
  • Algorithm-Correctness-Error-Handling

this.#queryClient.invalidateQueries({ queryKey: ['tasks'] })
},
onMutate: async ({ task }) => {
onSuccess: () => {},
Copy link

Choose a reason for hiding this comment

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

Inconsistent Return Pattern

Empty onSuccess callback differs from other services that return invalidateQueries. Inconsistent return patterns across similar methods create maintenance confusion when developers need to understand callback behavior.

Standards
  • Clean-Code-Consistency
  • Design-Pattern-Observer

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: 5

♻️ Duplicate comments (8)
examples/angular/unit-testing/src/app/services/tasks.service.ts (1)

35-35: Type the callbacks and context; improve optimistic update when cache is empty

  • Provide generics to mutationOptions to propagate types to onMutate/onError.
  • Type onMutate/onError params explicitly.
  • Rename previousTodos and optimistically set when cache is empty.
-    return mutationOptions({
+    return mutationOptions<string, Error, { task: string; failMutation?: boolean }, string[] | undefined>({
@@
-      onSuccess: () => {},
-      onMutate: async ({ task }) => {
+      onSuccess: () => {},
+      onMutate: async ({ task }: { task: string }) => {
@@
-        const previousTodos = this.#queryClient.getQueryData<Array<string>>([
-          'tasks',
-        ])
+        const previousTasks = this.#queryClient.getQueryData<string[]>(['tasks'])
@@
-        if (previousTodos) {
-          this.#queryClient.setQueryData<Array<string>>(
-            ['tasks'],
-            [...previousTodos, task],
-          )
-        }
+        this.#queryClient.setQueryData<string[]>(
+          ['tasks'],
+          [...(previousTasks ?? []), task],
+        )
@@
-        return previousTodos
+        return previousTasks
       },
-      onError: (_err, _variables, context) => {
+      onError: (_err: Error, _variables: { task: string; failMutation?: boolean }, context?: string[]) => {
         if (context) {
           // Rollback the optimistic update
-          this.#queryClient.setQueryData<Array<string>>(['tasks'], context)
+          this.#queryClient.setQueryData<string[]>(['tasks'], context)
         }
       },

Also applies to: 50-51, 57-67, 71-76

docs/framework/angular/guides/testing.md (1)

90-103: Make test async; add strict typing; fix comment typo

The test uses await but isn’t async; also type allTasks and fix “interceptor” typo.

-    it('should get all the Tasks', () => {
-      let allTasks: any;
+    it('should get all the Tasks', async () => {
+      let allTasks: CreateQueryResult<string[], Error>;
       runInInjectionContext(injector, () => {
         allTasks = injectQuery(() => service.allTasks());
       });
       expect(allTasks.status()).toEqual('pending');
       expect(allTasks.isFetching()).toEqual(true);
       expect(allTasks.data()).toEqual(undefined);
       // We await the first result from the query
-      await waitFor(() => expect(allTasks.isFetching()).toBe(false), {timeout: 10000});
+      await waitFor(() => expect(allTasks.isFetching()).toBe(false), { timeout: 10000 });
       expect(allTasks.status()).toEqual('success');
-      expect(allTasks.data()).toEqual([]); // Considering that the inteceptor is returning [] at the first query request.
+      expect(allTasks.data()).toEqual([]); // Considering that the interceptor returns [] on the first request.
       // To have a more complete example have a look at "unit testing / jest"
     });
examples/angular/unit-testing/src/app/components/unit-testing.component.ts (1)

61-66: Handle mutation errors and clear input only on success

Add onError handling (previously requested) and only clear input after a successful mutation.

-    this.addMutation.mutate({
-      task: this.newItem,
-      failMutation: this.failMutation,
-    })
-    this.newItem = ''
+    this.addMutation.mutate(
+      {
+        task: this.newItem,
+        failMutation: this.failMutation,
+      },
+      {
+        onSuccess: () => (this.newItem = ''),
+        onError: (err) => console.error('Failed to add task:', err),
+      },
+    )
examples/angular/unit-testing/src/app/services/tasks.service.spec.ts (1)

14-14: Type addTask instead of using any

This improves IntelliSense and catches mistakes in callbacks/variables.

-    let addTask: any;
+    let addTask: CreateMutationResult<
+      { status: string; task: string },
+      Error,
+      { task: string; failMutation: boolean },
+      unknown
+    >;
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (3)

18-18: Reset module-scoped state to avoid test flakiness

Expose a reset to clear callNumber (and optionally storage) between tests.

 let callNumber = 0;
 
+// Useful for test isolation
+export function resetMockApiState() {
+  callNumber = 0
+  sessionStorage.removeItem('unit-testing-tasks')
+}

44-57: Wrap POST case body in a block (fix noSwitchDeclarations lint error)

Prevents case-local declarations leaking across cases and satisfies Biome rule.

-      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,
-        })
+      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,
+        })
+      }

8-8: Return HttpErrorResponse for realistic HTTP failures and generic message

Improves parity with real backend errors (status/url/statusText) and avoids leaking details.

-import { HttpResponse } from '@angular/common/http'
+import { HttpResponse, HttpErrorResponse } from '@angular/common/http'
-  if (req.url === '/api/tasks-wrong-url') {
-    return throwError(() => new Error('error')).pipe(delay(1000));
-  }
+  if (req.url === '/api/tasks-wrong-url') {
+    return throwError(
+      () =>
+        new HttpErrorResponse({
+          status: 500,
+          statusText: 'Internal Server Error',
+          url: req.url,
+          error: { message: 'Invalid request' },
+        }),
+    ).pipe(delay(1000))
+  }

Also applies to: 59-61

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

95-101: Verify Jest preset and configs are present to avoid CI failures

Ensure jest-preset-angular is in devDependencies and referenced files exist.

#!/bin/bash
set -euo pipefail
BASE="examples/angular/unit-testing"

# Files referenced by the 'test' target
for f in "$BASE/jest.config.ts" "$BASE/tsconfig.spec.json"; do
  [[ -f "$f" ]] || { echo "Missing file: $f"; exit 1; }
done

PKG="$BASE/package.json"
[[ -f "$PKG" ]] || { echo "Missing $PKG"; exit 1; }

echo "Checking devDependencies in $PKG"
jq -e '.devDependencies["jest-preset-angular"]' "$PKG" >/dev/null || { echo "Missing jest-preset-angular"; exit 1; }
jq -e '.devDependencies["jest"]' "$PKG" >/dev/null || { echo "Missing jest"; exit 1; }
jq -e '.devDependencies["@angular-builders/jest"]' "$PKG" >/dev/null || { echo "Missing @angular-builders/jest"; exit 1; }

echo "Jest setup looks present."
🧹 Nitpick comments (29)
examples/angular/unit-testing/.devcontainer/devcontainer.json (1)

1-4: Align devcontainer tooling with README (pnpm/yarn/bun) or clarify npm-only

The image provides npm, but README suggests yarn/pnpm/bun. Either install them via Corepack or trim the README to npm-only to avoid confusion.

Apply this minimal addition to auto-enable Corepack-managed PMs:

 {
   "name": "Node.js",
-  "image": "mcr.microsoft.com/devcontainers/javascript-node:22"
+  "image": "mcr.microsoft.com/devcontainers/javascript-node:22",
+  "postCreateCommand": "corepack enable && corepack prepare pnpm@latest --activate && corepack prepare yarn@stable --activate"
 }
examples/angular/devtools-panel/tsconfig.json (1)

2-2: Question: Do we need compileOnSave here?

Setting "compileOnSave": true is unusual for Angular CLI projects and diverges from the unit-testing example (false). Recommend removing for consistency unless there's a specific tooling need.

-  "compileOnSave": true,

If this was intentional, please note the rationale (e.g., editor plugin dependency).

examples/angular/unit-testing/README.md (1)

5-7: Clarify commands per package manager and fix minor wording

Explicitly list the test command variants and keep punctuation consistent to reduce friction for new contributors.

-- `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
+- `npm install` or `yarn` or `pnpm i` or `bun i`.
+- `npm run start` or `yarn start` or `pnpm start` or `bun start`.
+- Run tests: `npm run test` (or `yarn test` / `pnpm test` / `bun test`).

Given the devcontainer currently only guarantees npm, consider noting that yarn/pnpm/bun require local availability or enabling via Corepack.

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

41-42: LGTM; returning the invalidate promise is fine. Consider deduping the key and tightening types

Change looks good. To reduce duplication and strengthen typings:

  • Extract a shared query key constant.
  • Optionally annotate generics on mutationOptions for stricter types.

Example (outside the shown lines):

const TASKS_KEY = ['tasks'] as const;

// ...
onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: TASKS_KEY }),

Confirm that returning the Promise from onSuccess aligns with your test expectations across all examples (consistent behavior in other services).

Also applies to: 55-56

examples/angular/unit-testing/.eslintrc.cjs (1)

3-6: Empty ESLint config isn’t “ready-to-use”; add minimal env/ignores.

This keeps Jest globals recognized and avoids linting build artifacts without introducing extra deps.

Apply:

 /** @type {import('eslint').Linter.Config} */
-const config = {}
+const config = {
+  root: true,
+  ignorePatterns: ['dist', 'coverage'],
+  env: { browser: true, es2022: true, jest: true },
+}
examples/angular/optimistic-updates/src/app/services/tasks.service.ts (4)

30-34: Docstring is stale; invalidation now happens in onSettled.

Apply:

-  /**
-   * Creates a mutation for adding a task.
-   * On success, invalidates and refetches the "tasks" query cache to update the task list.
-   */
+  /**
+   * Creates a mutation for adding a task with an optimistic update.
+   * Always invalidates and refetches the "tasks" query cache in onSettled.
+   */

50-51: Remove no-op onSuccess handler.

It adds noise; onSettled already handles refetch.

Apply:

-      onSuccess: () => {},
-      onMutate: async ({ task } : {task: string}) => {
+      onMutate: async ({ task } : {task: string}) => {

57-69: Rename previousTodos → previousTasks for consistency with domain naming.

Pure readability.

Apply:

-        const previousTodos = this.#queryClient.getQueryData<Array<string>>([
+        const previousTasks = this.#queryClient.getQueryData<Array<string>>([
           'tasks',
         ])
@@
-        if (previousTodos) {
+        if (previousTasks) {
           this.#queryClient.setQueryData<Array<string>>(
             ['tasks'],
-            [...previousTodos, task],
+            [...previousTasks, task],
           )
         }
 
-        return previousTodos
+        return previousTasks

35-42: Optional: Type mutation generics to get a typed context.

This removes explicit any in onError and improves DX.

Apply:

-    return mutationOptions({
+    return mutationOptions<unknown, unknown, { task: string; failMutation: boolean }, Array<string> | undefined>({
       mutationFn: ({
         task,
         failMutation = false,
       }: {
         task: string
         failMutation: boolean
       }) =>
-      onError: (_err: any, _variables: any, context: any) => {
+      onError: (_err, _variables, context) => {
         if (context) {
           this.#queryClient.setQueryData<Array<string>>(['tasks'], context)
         }
       },

Also applies to: 71-75

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

18-21: JSDoc is misleading about return type

This returns queryOptions (whose queryFn resolves a Promise), not an Observable.

Apply:

-   * Returns an observable containing an array of task strings.
+   * Returns TanStack Query options; the queryFn resolves to a Promise<string[]>.

22-28: Add explicit generics to queryOptions for stronger types

Prevents accidental any in consuming tests/docs.

-  allTasks = () =>
-    queryOptions({
+  allTasks = () =>
+    queryOptions<string[], Error>({
       queryKey: ['tasks'],
       queryFn: () => {
-        return lastValueFrom(this.#http.get<Array<string>>('/api/tasks'));
+        return lastValueFrom(this.#http.get<string[]>('/api/tasks'));
       }
     })

36-42: Make failMutation optional to match default value

Type declares it required while a default is provided.

-      }: {
-        task: string
-        failMutation: boolean
-      }) =>
+      }: {
+        task: string
+        failMutation?: boolean
+      }) =>

77-81: Invalidate on success only to avoid unnecessary refetch on errors

Reduces wasted work when mutations fail. If you want “always refetch” for demo simplicity, ignore.

-      // Always refetch after error or success:
-      onSettled: () => {
-        return this.#queryClient.invalidateQueries({ queryKey: ['tasks'] })
-      },
+      // Refetch after success only:
+      onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['tasks'] }),

Also applies to: 50-50


26-26: Prefer firstValueFrom for single-emission HTTP

Slightly clearer for HttpClient streams.

-import { lastValueFrom } from 'rxjs'
+import { firstValueFrom } from 'rxjs'
@@
-        return lastValueFrom(this.#http.get<string[]>('/api/tasks'));
+        return firstValueFrom(this.#http.get<string[]>('/api/tasks'));
docs/framework/angular/guides/testing.md (3)

6-14: Tighten wording; proper casing

Minor grammar/wording polish.

-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.
+Because there’s no simple way to await a signal reaching a specific value, we’ll poll in tests (instead of converting signals to Observables and filtering with RxJS). If you prefer polling, you can use the Angular Testing Library.

18-20: Clarify sentence structure

-Because the recommendation is to use services that provide the Query options through function this is what we are going to do.
+We recommend exposing Query options via service functions, so the examples follow that pattern.

41-51: Align types and description with the implementation

Use string[] and correct return description.

-  /**
-   * Fetches all tasks from the API.
-   * Returns an observable containing an array of task strings.
-   */
+  /**
+   * Fetches all tasks from the API.
+   * Returns Query options whose queryFn resolves to string[].
+   */
@@
-        return lastValueFrom(this.#http.get<Array<string>>('/api/tasks'));
+        return lastValueFrom(this.#http.get<string[]>('/api/tasks'));
examples/angular/unit-testing/tsconfig.spec.json (1)

1-8: Broaden test type support and patterns (optional)

Include Node types (often used in Jest configs) and recognize *.test.ts files.

 {
   "extends": "./tsconfig.json",
   "compilerOptions": {
     "outDir": "./out-tsc/spec",
-      "types": ["jest"]
+      "types": ["jest", "node"]
   },
-    "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
+    "include": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.d.ts"]
 }
examples/angular/unit-testing/src/app/app.component.ts (1)

4-10: Prefer explicit closing tag for the child component (minor).

Self-closing custom elements can confuse some tooling. Optional change for clarity:

-  template: `<unit-testing />`,
+  template: `<unit-testing></unit-testing>`,
examples/angular/unit-testing/src/app/app.config.ts (2)

25-26: Gate devtools to non-production builds

Avoid bundling devtools in prod. Conditionally include it using isDevMode().

-import type { ApplicationConfig } from '@angular/core'
+import type { ApplicationConfig } from '@angular/core'
+import { isDevMode } from '@angular/core'
@@
-      withDevtools(),
+      ...(isDevMode() ? [withDevtools()] : []),

21-23: Double-check long gcTime for example app

24h cache GC is fine for demos, but can cause memory retention in bigger apps. Consider lowering or documenting why you want this.

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

31-33: Prevent accidental double submits

Disable the Create button when input is empty or the mutation is pending.

-        <button (click)="addItem()">Create</button>
+        <button (click)="addItem()" [disabled]="!newItem || addMutation.isPending()">Create</button>

34-37: Guard against undefined data and avoid duplicate track keys

Coalesce to an empty list and track by index to avoid runtime issues if data is undefined or contains duplicates.

-          @for (task of tasks.data(); track task) {
+          @for (task of tasks.data() ?? []; track $index) {
             <li>{{ task }}</li>
           }

20-22: Optional: add basic error state for the query

Consider showing an error when tasks query fails (e.g., network error) to aid testing and UX.

Also applies to: 43-46

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

8-8: Import CreateMutationResult for strong typing

Bring in CreateMutationResult to remove anys below.

-import type { CreateQueryResult} from "@tanstack/angular-query-experimental";
+import type { CreateQueryResult, CreateMutationResult } from "@tanstack/angular-query-experimental";

63-69: Type onSuccess callback parameters

Aligns with the mutation’s data and variables shapes.

-        onSuccess: (data: any, variables: any, _context: any) => {
+        onSuccess: (
+          data: { status: string; task: string },
+          _variables: { task: string; failMutation: boolean },
+          _context: unknown
+        ) => {

97-100: Type onError callback parameters

Use Error and explicit variable/context types.

-        onError: (data: any, _variables: any, _context: any) => {
+        onError: (
+          data: Error,
+          _variables: { task: string; failMutation: boolean },
+          _context: unknown
+        ) => {

24-31: Optional: disable mutation retries explicitly in tests

Mutations default to no retry, but making it explicit prevents surprises if defaults change.

           provideTanStackQuery(new QueryClient({
             defaultOptions: {
               queries: {
                 retry: false
-              }
+              },
+              mutations: {
+                retry: false
+              }
             }
           }))
examples/angular/unit-testing/src/app/interceptor/mock-api.interceptor.ts (1)

24-26: Add url/statusText in mocked responses for better fidelity

Minor realism boost; harmless to existing tests.

-  const respondWith = (status: number, body: any) =>
-    of(new HttpResponse({ status, body })).pipe(delay(1000))
+  const respondWith = (status: number, body: any) =>
+    of(
+      new HttpResponse({
+        status,
+        body,
+        url: req.url,
+        statusText: status === 201 ? 'Created' : 'OK',
+      }),
+    ).pipe(delay(1000))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

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

⛔ 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 (24)
  • 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/devtools-panel/tsconfig.json (1 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
🪛 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

[grammar] ~7-~7: There might be a mistake here.
Context: ...start-npm run test` to run the tests

(QB_NEW_EN)

docs/framework/angular/guides/testing.md

[grammar] ~170-~170: There might be a mistake here.
Context: ...ide/http/interceptors) for more details. You can see the the Interceptor setup in...

(QB_NEW_EN)


[grammar] ~171-~171: There might be a mistake here.
Context: ...tors) for more details. You can see the the Interceptor setup in the "Unit testing ...

(QB_NEW_EN)


[grammar] ~171-~171: There might be a mistake here.
Context: ...p in the "Unit testing / Jest" examples.

(QB_NEW_EN)

🪛 Biome (2.1.2)
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:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (10)
examples/angular/optimistic-updates/src/app/interceptor/mock-api.interceptor.ts (1)

9-9: Use HttpErrorResponse instead of Error in mock-api.interceptor.ts

Import HttpErrorResponse and throw an HttpErrorResponse to preserve status, url, and error payload:

-import { HttpResponse } from '@angular/common/http'
+import { HttpResponse, HttpErrorResponse } from '@angular/common/http'
@@
-  if (req.url === '/api/tasks-wrong-url') {
-    return throwError(() => new Error('error')).pipe(delay(1000));
-  }
+  if (req.url === '/api/tasks-wrong-url') {
+    return throwError(
+      () =>
+        new HttpErrorResponse({
+          status: 500,
+          statusText: 'Internal Server Error',
+          url: req.url,
+          error: { message: 'error' },
+        }),
+    ).pipe(delay(1000))
+  }

No existing spec tests assert on a generic Error in this example. Update any future tests to expect HttpErrorResponse instead.

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

1-32: Strict TS/Angular compiler settings LGTM.

Good defaults for tests and example apps; no blocking issues spotted.

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

15-16: Good use of Angular inject() with private fields

Clean DI with private fields for QueryClient and HttpClient.


15-16: tsconfig target supports private fields The examples/angular/unit-testing/tsconfig.json sets target to ES2022 (≥ ES2021), so class private fields (#) are supported.

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

1-9: LGTM — standard Angular app tsconfig.

This matches Angular’s default app config. Confirm that test-only types (e.g., jest, node) are declared in a separate tsconfig.spec.json so keeping "types": [] here won’t break test typings.

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

5-5: Bootstrap flow looks good.

Straightforward bootstrap with error logging. No changes needed.

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

1-13: LGTM.

Minimal and correct Angular index.html. If you plan to host under a subpath (e.g., GitHub Pages), remember to adjust <base href="/">.

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

5-9: Good choice using OnPush and a standalone root.

Solid defaults for perf and simplicity.

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

51-86: Nice work on realistic optimistic-update assertions

Good use of waitFor and cache inspection to verify optimistic updates and refetch behavior.

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

47-58: Build target configuration looks solid

Paths, polyfills, and tsconfig wiring are consistent for the sample app.

Comment on lines +710 to +713
{
"label": "Testing",
"to": "framework/angular/guides/testing"
},
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Nav additions look correct; verify targets resolve at build time.

The new Angular “Testing” guide and “Unit Testing / Jest” example entries point to:

  • framework/angular/guides/testing
  • framework/angular/examples/unit-testing

Please confirm the corresponding pages exist and routes render in the docs site.

Run:

Also applies to: 1290-1294


🏁 Script executed:

#!/bin/bash
# Verify guide page exists
fd -t f -a 'testing.md' docs/framework/angular/guides -x echo FOUND {} || true

# Verify example route directory (index.mdx/md/tsx)
fd -t f -a 'index.*' docs/framework/angular/examples/unit-testing -x echo FOUND {} || true

Length of output: 364


Missing example route directory
The path docs/framework/angular/examples/unit-testing does not exist, so its nav link in docs/config.json (lines 710–713 and 1290–1294) will 404. Add that directory with an index.* file or correct the config path.

🤖 Prompt for AI Agents
In docs/config.json around lines 710–713 (and also update the duplicate at
1290–1294), the nav entry points to docs/framework/angular/examples/unit-testing
which does not exist and will 404; either create the missing directory
docs/framework/angular/examples/unit-testing and add an index file (e.g.,
index.md or index.mdx) with the example content, or change both config.json
entries to point to the correct existing path for the Angular testing example so
the links resolve.

Comment on lines +166 to +171
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.

## Testing Network Calls

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.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typos and grammar in user-facing docs

“These are visible to readers; let’s polish.”

-## Testing Network Calls
-
-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.
+## Testing network calls
+
+Instead of targeting a server, mock the requests. There are multiple ways to do this; we recommend using an Angular HTTP interceptor (see the [docs](https://angular.dev/guide/http/interceptors) for details).
+You can see the interceptor setup in the “Unit testing / Jest” examples.
📝 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
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.
## Testing Network Calls
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.
## Testing network calls
Instead of targeting a server, mock the requests. There are multiple ways to do this; we recommend using an Angular HTTP interceptor (see the [docs](https://angular.dev/guide/http/interceptors) for details).
You can see the interceptor setup in the “Unit testing / Jest” examples.
🧰 Tools
🪛 LanguageTool

[grammar] ~170-~170: There might be a mistake here.
Context: ...ide/http/interceptors) for more details. You can see the the Interceptor setup in...

(QB_NEW_EN)


[grammar] ~171-~171: There might be a mistake here.
Context: ...tors) for more details. You can see the the Interceptor setup in the "Unit testing ...

(QB_NEW_EN)


[grammar] ~171-~171: There might be a mistake here.
Context: ...p in the "Unit testing / Jest" examples.

(QB_NEW_EN)

🤖 Prompt for AI Agents
In docs/framework/angular/guides/testing.md around lines 166 to 171, fix typos
and improve grammar: change "turn retries off" to "turn off retries" (or
"disable retries"), correct "targetting" to "targeting", change "multiple way"
to "multiple ways", remove the duplicated "the the" so it reads "You can see the
Interceptor setup...", and normalize capitalization/usage of "Interceptor" (use
lowercase "interceptor" unless it's a proper noun). Apply these edits inline to
the paragraph for a clearer, grammatical user-facing doc.

Comment on lines +1 to +5
const config: import('jest').Config = {
roots: ['<rootDir>/src/'],
setupFilesAfterEnv: []
};
export default config;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Fix ESLint violation and provide a working Angular Jest preset.

Your config won’t run Angular tests as-is and fails the ESLint rule banning import() type annotations. Replace the file with a preset-based config and proper typings.

-const config: import('jest').Config = {
-    roots: ['<rootDir>/src/'],
-    setupFilesAfterEnv: []
-  };
-  export default config;
+import type { Config } from 'jest'
+
+const config: Config = {
+  preset: 'jest-preset-angular',
+  testEnvironment: 'jsdom',
+  roots: ['<rootDir>/src/'],
+  testMatch: ['**/*.spec.ts'],
+  setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
+  transform: {
+    '^.+\\.(ts|mjs|js|html)$': [
+      'jest-preset-angular',
+      { tsconfig: '<rootDir>/tsconfig.spec.json', stringifyContentPathRegex: '\\.html$' }
+    ]
+  },
+  moduleFileExtensions: ['ts', 'html', 'js', 'json', 'mjs'],
+}
+
+export default config
📝 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 config: import('jest').Config = {
roots: ['<rootDir>/src/'],
setupFilesAfterEnv: []
};
export default config;
import type { Config } from 'jest'
const config: Config = {
preset: 'jest-preset-angular',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src/'],
testMatch: ['**/*.spec.ts'],
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{ tsconfig: '<rootDir>/tsconfig.spec.json', stringifyContentPathRegex: '\\.html$' }
]
},
moduleFileExtensions: ['ts', 'html', 'js', 'json', 'mjs'],
}
export default config
🧰 Tools
🪛 ESLint

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

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

🤖 Prompt for AI Agents
In examples/angular/unit-testing/jest.config.ts lines 1-5, the file uses the
banned import() type annotation and lacks a working Angular preset; replace it
with a preset-based config that avoids import() types by importing the Jest type
(e.g. import type { Config } from 'jest') and declare const config: Config = {
preset: 'jest-preset-angular', roots: ['<rootDir>/src/'], setupFilesAfterEnv:
['<rootDir>/setup-jest.ts'], testEnvironment: 'jest-environment-jsdom' } and
export default config so ESLint is satisfied and Angular tests run with the
proper preset and typings.

💡 Verification agent

🧩 Analysis chain

Ensure Jest loads a TypeScript config file in your environment.

If you’re not using a builder that can load TS configs, either:

  • keep this file as .ts and configure Jest to load it via ts-node, or
  • rename to jest.config.cjs/.js.

Also verify that these files exist and are referenced correctly: setup-jest.ts and tsconfig.spec.json.


🏁 Script executed:

#!/bin/bash
# Verify supporting files and preset
fd -a 'setup-jest.ts' -x echo 'FOUND {}' || true
fd -a 'tsconfig.spec.json' -x echo 'FOUND {}' || true
rg -n --hidden -S "jest-preset-angular" -g '!**/node_modules/**'

Length of output: 2044


Ensure Jest can load your TS config
Jest won’t pick up jest.config.ts out of the box—either rename it to jest.config.cjs (with module.exports = config) or invoke Jest with --require ts-node/register (and install ts-node) so it can process the .ts file.

Fix missing setup file
There’s no setup-jest.ts in the repo but setupFilesAfterEnv is empty—you’ll need to either add your setup-jest.ts under examples/angular/unit-testing/ or remove any docs/reference to it.

tsconfig.spec.json is present
The tsconfig.spec.json at examples/angular/unit-testing/tsconfig.spec.json exists and needs no changes.

🧰 Tools
🪛 ESLint

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

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

🤖 Prompt for AI Agents
In examples/angular/unit-testing/jest.config.ts lines 1-5 the TS Jest config
won't be loaded by Jest by default and setupFilesAfterEnv is empty while a setup
file is referenced elsewhere; fix by either renaming this file to
jest.config.cjs and export with module.exports = config, or keep the .ts file
and ensure test runs include --require ts-node/register and add ts-node as a dev
dependency so Jest can process TypeScript configs; then either add a
setup-jest.ts under examples/angular/unit-testing/ and reference it in
setupFilesAfterEnv, or remove any docs/references to a setup file to avoid a
missing-import error (tsconfig.spec.json at
examples/angular/unit-testing/tsconfig.spec.json is fine and needs no changes).

Comment on lines +23 to +36
"devDependencies": {
"@angular/build": "^19.2.5",
"@angular-builders/jest": "^19.0.0",
"@angular/cli": "^19.2.5",
"@angular/compiler-cli": "^19.2.4",
"@testing-library/angular": "17.3.7",
"@testing-library/dom": "^10.0.0",
"@testing-library/jest-dom": "^6.4.8",
"@types/jest": "^29.5.12",
"@types/node": "^12.11.1",
"jest": "^29.7.0",
"ts-node": "~10.8.1",
"typescript": "5.8.2"
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Update Node types and declare engines to match Angular 19.

@types/node 12 is outdated for modern Angular/Jest stacks and can cause ambient type conflicts. Declare a Node engine and bump types.

Apply:

   "devDependencies": {
@@
-    "@types/node": "^12.11.1",
+    "@types/node": "^20",
@@
-    "typescript": "5.8.2"
+    "typescript": "5.8.2"
   }
+  ,
+  "engines": {
+    "node": ">=20"
+  }

If tests use ESM, ensure Jest transform is configured accordingly (ts-jest or swc). Verify:


🏁 Script executed:

#!/bin/bash
rg -n 'transform|preset|ts-jest|@swc' examples/angular/unit-testing/jest.config.ts -C2 || true

Length of output: 95


🏁 Script executed:

#!/bin/bash
# List any Jest config files in the example directory
find examples/angular/unit-testing -maxdepth 1 -type f -iname 'jest.config*'

# If jest.config.ts exists, print its contents for inspection
if [[ -f examples/angular/unit-testing/jest.config.ts ]]; then
  sed -n '1,200p' examples/angular/unit-testing/jest.config.ts
fi

Length of output: 368


Update Node types & engines; configure Jest for TypeScript

  • In examples/angular/unit-testing/package.json, bump @types/node and declare a minimum Node version:
   "devDependencies": {
@@
-    "@types/node": "^12.11.1",
+    "@types/node": "^20",
@@
-    "typescript": "5.8.2"
+    "typescript": "5.8.2"
   },
+  "engines": {
+    "node": ">=20"
+  }
  • In examples/angular/unit-testing/jest.config.ts, there is no preset or transform configured. Add a TypeScript transformer or preset (for example,
    preset: 'jest-preset-angular',
    transform: { '^.+\\.(ts|html)$': 'ts-jest' }
    ) so Jest can compile and run .ts tests.
🤖 Prompt for AI Agents
In examples/angular/unit-testing/package.json around lines 23 to 36, bump the
@types/node dependency to a modern compatible version (e.g. ^18.x or ^20.x as
project requires) and add an "engines" field at the package root declaring the
minimum Node version (e.g. "engines": { "node": ">=18.0.0" }) to enforce runtime
compatibility; then in examples/angular/unit-testing/jest.config.ts (the
referenced config), add a preset and transform to compile TypeScript tests by
setting preset to "jest-preset-angular" and adding a transform mapping such as
'^.+\\.(ts|html)$' to 'ts-jest' (or use an equivalent ts transformer) so Jest
can process .ts/.html files, and ensure ts-jest and jest-preset-angular are
installed in devDependencies.

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.

2 participants