diff --git a/apps/cookbook/docs/angular/02-glossary.md b/apps/cookbook/docs/angular/02-glossary.md
index f4b4978..5e79dc3 100644
--- a/apps/cookbook/docs/angular/02-glossary.md
+++ b/apps/cookbook/docs/angular/02-glossary.md
@@ -1,6 +1,7 @@
---
title: Glossary
slug: /angular/glossary
+sidebar_position: 10
---
## 3X
@@ -11,6 +12,14 @@ Explore, Expand, and Extract are the three phases of software development that K
Affected tests are tests that are impacted by a change in the codebase. By running only the affected tests, developers can get faster feedback on the changes they have made. This can be achieved using features like [Nx Affected Graph](https://nx.dev/ci/features/affected#run-only-tasks-affected-by-a-pr) and/or [Vitest's `changed` option](https://vitest.dev/guide/cli.html#changed).
+## Angular Synchronization
+
+Angular synchronization is the process that keeps the UI in sync with the application state. It handles:
+
+- flushing views _(i.e., change detection)_
+- running reactive effects
+- triggering render hooks
+
## Canary Release
Canary release is a technique used to reduce the risk of introducing a new feature or change to a large audience. By releasing the change to a small subset of users first, the team can monitor the impact and gather feedback before rolling out the change to the entire user base.
@@ -60,6 +69,10 @@ Narrow tests are tests that are fast, easy to isolate and parallelize, and have
Cf. [Narrow Tests Definition](./01-testing/01-beyond-unit-vs-integration/index.mdx#narrow-tests)
+## Over-Narrow Tests
+
+Over-narrow tests are more specific than necessary, often leading to over-specification.
+
## Over-Specification
Over-specification occurs when tests are too tightly coupled to the implementation details of the System Under Test. This can make tests brittle and hard to maintain, as any changes to the implementation will require corresponding changes to the tests.
diff --git a/apps/cookbook/docs/angular/08-prep-station/01-v20/01-flushing-flusheffects.md b/apps/cookbook/docs/angular/08-prep-station/01-v20/01-flushing-flusheffects.md
new file mode 100644
index 0000000..9fe6e0f
--- /dev/null
+++ b/apps/cookbook/docs/angular/08-prep-station/01-v20/01-flushing-flusheffects.md
@@ -0,0 +1,262 @@
+---
+slug: /angular/flushing-flusheffects
+sidebar_label: Flushing "flushEffects"
+---
+
+# V20 Flushes `flushEffects` Down the Sink
+
+:::warning
+In Angular 20, [`TestBed.flushEffects()`](https://v19.angular.dev/api/core/testing/TestBedStatic#flushEffects) didn't survive developer preview and has been removed in favor of [`TestBed.tick()`](https://next.angular.dev/api/core/testing/TestBedStatic#tick).
+:::
+
+`TestBed.tick()` is **not** a drop-in replacement for `TestBed.flushEffects()` — it does more than just flushing effects. It triggers Angular [synchronization](../../02-glossary.md#angular-synchronization) _(change detection, effects, etc...)_, making tests more symmetric to production, and therefore more reliable.
+
+In most cases, that's an improvement, but some tests with questionable design might break.
+
+:::tip TL;DR
+
+1. [Monkey-patch](#incremental-migration) `TestBed.flushEffects()` **temporarily** and fix broken tests before migrating to v20 and `TestBed.tick()`.
+2. Prefer using a utility such as [`runInAngular()`](#run-in-angular) when narrowing down your tests to reactive logic that lives beneath components and services.
+3. Think twice before narrowing down your tests to such granularity.
+ :::
+
+## `TestBed.tick()` Might Not Be What You Need
+
+Angular's synchronization should be treated as an implementation detail. Tests should generally avoid interfering with it.
+
+Let's start with a typical test using `TestBed.tick()`:
+
+
+
+After refactoring our code to wait for some async operation to complete, the test fails because the assertion is made before the microtask is flushed:
+
+
+
+:::info
+Under the hood, [`resource`](https://angular.dev/api/core/resource) tells Angular that it is loading by leveraging the [`PendingTasks`](https://angular.dev/api/core/PendingTasks) service.
+
+If you want the same behavior in your own utilities, you should use `pendingTasks.run()`.
+:::
+
+### 2. Polling
+
+Use [Vitest's `expect.poll()`](https://vitest.dev/api/expect.html#poll) — or [Angular Testing Library's `waitFor` utility](https://testing-library.com/docs/dom-testing-library/api-async/#waitfor) for other testing frameworks:
+
+
+
+:::warning Potential for False Negatives
+Polling may seem robust, but it can yield [false negatives](../../02-glossary.md#false-negative): the result might appear valid during a brief window, only to become invalid once the application stabilizes.
+:::
+
+## Testing Signal Factories
+
+You will often want to test your signal factories such as `autoSave` without leveraging a component or service.
+Given that under the hood, it is using dependency injection, you will have to run it in an injection context.
+
+
+
+## Incremental Migration
+
+Before migrating to Angular 20, you can already check whether `TestBed.tick()` breaks anything by monkey-patching `TestBed.flushEffects()`:
+
+```ts title="src/test-setup.ts"
+/* DO NOT KEEP THIS. IT'S ONLY FOR MIGRATION PREPARATION. */
+import { TestBed } from '@angular/core/testing';
+
+TestBed.flushEffects = () => TestBed.inject(ApplicationRef).tick();
+```
+
+In the rare occurrence where switch to `tick()` causes trouble:
+
+1. I'd love to see your tests 😊.
+2. You can implement a transitional utility function to avoid the big-bang switch:
+
+```ts
+export function triggerTick() {
+ TestBed.inject(ApplicationRef).tick();
+}
+```
+
+You can then incrementally replace calls to `TestBed.flushEffects()` with `triggerTick()` and fix your broken tests before migrating to Angular 20.
+
+Happy migration!
+
+## Today’s Dash: `runInAngular` {#run-in-angular}
+
+_Ready to be Copied, Stirred, and Served._
+
+```ts
+async function runInAngular(
+ fn: () => RETURN | Promise,
+): Promise {
+ return TestBed.runInInjectionContext(async () => {
+ const appRef = inject(ApplicationRef);
+ const result = await fn();
+ await appRef.whenStable();
+ return result;
+ });
+}
+```
diff --git a/apps/cookbook/docs/angular/08-prep-station/01-v20/02-tests-error-sensitivity.md b/apps/cookbook/docs/angular/08-prep-station/01-v20/02-tests-error-sensitivity.md
new file mode 100644
index 0000000..ba2e17e
--- /dev/null
+++ b/apps/cookbook/docs/angular/08-prep-station/01-v20/02-tests-error-sensitivity.md
@@ -0,0 +1,200 @@
+---
+slug: /angular/tests-error-sensitivity
+sidebar_label: Tests Error Sensitivity
+---
+
+# V20 Cranks the Heat on Half-Baked Tests
+
+:::warning
+In Angular 20, the [`TestBed`](https://angular.dev/api/core/testing/TestBed) is becoming more error-sensitive.
+
+More specifically, errors thrown in event or output listeners might break your tests.
+:::
+
+:::tip TL;DR
+
+1. Add a `ThrowingErrorHandler` to your tests to see if any of your tests will break in v20.
+2. If you can't solve the problem, swallow the specific errors with a custom `ErrorHandler`.
+ This will help you keep a list of errors to fix later without creating new problems.
+3. Avoid disabling `rethrowApplicationErrors` option as this will open the door for even more [false negatives](../../02-glossary.md#false-negative).
+ :::
+
+## Previously, in Angular...
+
+### Pre `18.2.0-next.3` Era
+
+Before version 18.2.0-next.3, most errors were ignored by the tests. For instance, the test below would pass, and the error would simply be logged and forgotten about.
+
+```ts
+@Component({
+ template: `
+ Welcome
+ {{ fail() }}
+ `,
+})
+class Greetings {
+ fail() {
+ throw new Error('💥');
+ }
+}
+
+const fixture = TestBed.createComponent(Greetings);
+await fixture.whenStable();
+expect(fixture.nativeElement.textContent).toContain('Welcome');
+```
+
+### Post `18.2.0-next.3` Era — The Hidden Flag Episode
+
+A crucial thing in testing is avoiding false negatives: tests that pass while they shouldn't.
+
+In Angular 18.2.0-next.3, the Angular Team, and specially [Andrew Scott](https://x.com/AScottAngular) started working on making the TestBed more error-sensitive.
+It started with a [hidden flag](https://github.com/atscott/angular/blob/b422ac4c873095ed3ec32e43b464a365b2ba55f8/packages/core/testing/src/test_bed_common.ts#L78) to increase the error sensitivity on Google's internal codebase.
+
+Interestingly, this broke ~200 tests internally at Google. This clearly highlights that such false negatives are not uncommon. Of course, it is hard to tell whether the components were really broken or not _(e.g. unrealistic data, or unrealistic mocking)_.
+
+### Post `19.0.0-next.0` Era
+
+19.0.0-next.0 introduced the new `rethrowApplicationErrors` option to the `TestBed.configureTestingModule()`. It is set to `true` by default.
+
+This causes the `TestBed` to rethrow the errors that are caught by Angular instead of swallowing them.
+
+:::info
+If the error happens while you are waiting for stability with `fixture.whenStable()`, the promise will reject with the error.
+:::
+
+### Post `20.0.0-next.5` Era
+
+20.0.0-next.5 went a step further by rethrowing errors coming from event or output listeners.
+
+The following test would now throw an error:
+
+```ts
+import { fireEvent, screen } from '@testing-library/dom';
+
+@Component({
+ template: ``,
+})
+class CookButton {
+ private readonly _cooked = signal(false);
+
+ cook() {
+ if (this._cooked()) {
+ throw new Error('🔥');
+ }
+
+ this._cooked.set(true);
+ }
+}
+
+TestBed.createComponent(CookButton);
+
+const buttonEl = await screen.findByRole('button');
+
+await fireEvent.click(buttonEl);
+
+/* Second click overcooks and throws an error. */
+await fireEvent.click(buttonEl);
+```
+
+_**Another example**: triggering side-effect events such as `mouseenter` when clicking with [`@testing-library/user-event`](https://testing-library.com/docs/user-event/intro/), and the `mouseenter` listener throws because some test double is not realistic._
+
+## How to prepare for this change?
+
+### 1. Rethrow errors
+
+To reproduce the v20's behavior in v19 — or earlier — and make sure that your tests are not affected, you can implement a custom `ErrorHandler` that rethrows errors instead of swallowing them.
+
+```ts title="src/throwing-error-handler.ts"
+@Injectable()
+class ThrowingErrorHandler implements ErrorHandler {
+ handleError(error: unknown) {
+ throw error;
+ }
+}
+
+function provideThrowingErrorHandler(): EnvironmentProviders {
+ return makeEnvironmentProviders([
+ {
+ provide: ErrorHandler,
+ useClass: ThrowingErrorHandler,
+ },
+ ]);
+}
+```
+
+```ts title="src/test-setup.ts"
+TestBed.configureTestingModule({
+ providers: [provideThrowingErrorHandler()],
+});
+```
+
+### 2. Swallow specific errors if you need more time to fix them
+
+In case this breaks some tests, and you need more time to fix them, you could make the error handler temporarily ignore the test's specific errors.
+
+```ts title="src/throwing-error-handler.ts"
+@Injectable()
+class ThrowingErrorHandler implements ErrorHandler {
+ private _swallowingPredicates: ((error: unknown) => boolean)[] = [];
+
+ handleError(error: unknown) {
+ // highlight-start
+ if (this._swallowingPredicates.some((predicate) => predicate(error))) {
+ console.warn('ThrowingErrorHandler swallowed error:', error);
+ return;
+ }
+ // highlight-end
+ throw error;
+ }
+
+ // highlight-start
+ swallowError(predicate: (error: any) => boolean) {
+ this._swallowingPredicates.push(predicate);
+ }
+ // highlight-end
+}
+
+function provideThrowingErrorHandler(): EnvironmentProviders {
+ return makeEnvironmentProviders([
+ // highlight-start
+ ThrowingErrorHandler,
+ {
+ provide: ErrorHandler,
+ useExisting: ThrowingErrorHandler,
+ },
+ // highlight-end
+ ]);
+}
+```
+
+```ts title="src/cook-button.spec.ts"
+// highlight-start
+TestBed.inject(ThrowingErrorHandler).swallowError((error) =>
+ error?.message?.includes('🔥'),
+);
+// highlight-end
+
+TestBed.createComponent(CookButton);
+
+const buttonEl = await screen.findByRole('button');
+
+await fireEvent.click(buttonEl);
+
+await fireEvent.click(buttonEl);
+```
+
+### 3. Disable `rethrowApplicationErrors` but keep the `ThrowingErrorHandler`
+
+If you really can't fix the errors by the time you migrate to v20, you can disable `rethrowApplicationErrors` **while keeping `ThrowingErrorHandler` to make sure no new errors are introduced**.
+
+## Special Thanks
+
+Special thanks to [@AndrewScott](https://x.com/AScottAngular) for raising my awareness about this issue while discussing [Flushing `flushEffects`](./01-flushing-flusheffects.md).
+
+## Related Angular PRs
+
+For more detailed understanding, you can dive into the related PRs.
+
+- [PR #60251 - fix(core): Ensure errors in listeners report to the application error](https://github.com/angular/angular/pull/60251)
+- [PR #57200 - feat(core): rethrow errors during ApplicationRef.tick in TestBed](https://github.com/angular/angular/pull/57200)
+- [PR #57153 - refactor(core): Private option to rethrow ApplicationRef.tick errors in tests](https://github.com/angular/angular/pull/57153)
diff --git a/apps/cookbook/docs/angular/08-prep-station/01-v20/_category_.yml b/apps/cookbook/docs/angular/08-prep-station/01-v20/_category_.yml
new file mode 100644
index 0000000..ede470d
--- /dev/null
+++ b/apps/cookbook/docs/angular/08-prep-station/01-v20/_category_.yml
@@ -0,0 +1,4 @@
+label: Angular v19 to v20
+link:
+ slug: /angular/v19-to-v20
+ type: generated-index
diff --git a/apps/cookbook/docs/angular/08-prep-station/index.md b/apps/cookbook/docs/angular/08-prep-station/index.md
new file mode 100644
index 0000000..dd981df
--- /dev/null
+++ b/apps/cookbook/docs/angular/08-prep-station/index.md
@@ -0,0 +1,11 @@
+---
+title: Prep Station
+---
+
+Welcome to the Prep Station — your "mise en place" before Angular updates, and migrations.
+
+This section helps you get ready for Angular version bumps and other breaking changes. Start here before things catch fire.
+
+import DocCardList from '@theme/DocCardList';
+
+
diff --git a/apps/cookbook/src/css/code.css b/apps/cookbook/src/css/code.css
new file mode 100644
index 0000000..dfc1fb2
--- /dev/null
+++ b/apps/cookbook/src/css/code.css
@@ -0,0 +1,31 @@
+.meh {
+ --code-background: rgba(255, 180, 0, 0.05);
+ --code-glow: rgb(255, 180, 0, 0.1);
+}
+
+.good {
+ --code-background: rgba(0, 255, 0, 0.05);
+ --code-glow: rgb(0, 255, 0, 0.1);
+}
+
+.bad {
+ --code-background: rgba(255, 0, 0, 0.05);
+ --code-glow: rgb(255, 0, 0, 0.1);
+}
+
+.meh,
+.good,
+.bad {
+ .theme-code-block > div {
+ box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
+ transition: box-shadow 0.3s ease-in-out;
+
+ &:hover {
+ box-shadow: 0 0 5px 5px var(--code-glow);
+ }
+ }
+
+ code {
+ background-color: var(--code-background);
+ }
+}
diff --git a/apps/cookbook/src/css/custom.css b/apps/cookbook/src/css/custom.css
index 70c21e4..bd20f67 100644
--- a/apps/cookbook/src/css/custom.css
+++ b/apps/cookbook/src/css/custom.css
@@ -5,6 +5,7 @@
*/
@import './mermaid.css';
+@import './code.css';
/* You can override the default Infima variables here. */
:root {