Calculate the expected intercepted-request count and classify authored progress, header, final-response, and error events. Final acceptance depends on runtime body checks, not a TypeScript type annotation.

One observable can produce multiple requests
Angular's HttpClient returns observables that send a request when subscribed to. Each new subscription makes another request. Constructing the observable alone does not send it. FormData is a supported request body, and a generic response type describes a TypeScript expectation rather than validating returned data. Angular making-requests guide.
Use this original fixture: a synthetic file named demo.txt, containing the ASCII text 0123456789. Its file payload is ten bytes. Append it under the FormData field file and describe a POST to /demo-upload, intercepted by a native Angular test backend. The ten-byte file length is not the length of the entire multipart request.
In three isolated test cases, expect these counts:
| Case | Subscriptions to the request observable | Expected intercepted requests |
|---|---|---|
| Construct only | 0 | 0 |
| One designated owner subscribes | 1 | 1 |
| Diagnostic double subscription | 2 | 2 |
Separate progress, headers, and a validated response
Route events by their actual HttpEvent type rather than assuming every emission is a final response. Use this authored event sequence as an expected-result ledger:
| Event | What the handler should record |
|---|---|
| Sent | Request lifecycle started; acceptance still pending |
| UploadProgress, loaded 5, total 10 | Mock progress 50%; acceptance still pending |
| ResponseHeader | Headers received; final-body acceptance still pending |
Response, body {accepted: true, id: "demo-01"} |
Validate the body before marking accepted |
The 5/10 event values are supplied mock transfer values. They are not a measurement of the ten-byte file or its multipart envelope on the wire. If total is missing, show indeterminate progress. Do not turn an unknown denominator into 0% or 100%.
For this example's application contract, accept the final body only when accepted === true and id is a string containing at least one non-whitespace character. An HTTP 200 body such as {accepted: "true", id: ""} fails that contract. Record it as an invalid body, even if the TypeScript generic says it is an upload response. Keep the original identifier when valid; the whitespace check is validation, not an instruction to rewrite server data.
Exercise failures without transferring a file
Angular's TestRequest can deliver arbitrary HttpEvents with event() and resolve successful or unsuccessful HTTP responses with flush(). Use a final flush for each response case and a separate HTTP 400 flush for the error case. Angular TestRequest reference.
Keep the two-request diagnostic isolated in a native mock test. Name the subscription owner in each case and verify that every pending request is accounted for after the final response or error. For progress without a total, retain the loaded value but show an indeterminate state. Receiving headers does not satisfy the final-body contract, and an HTTP error must not silently start another request.
Try the worked example
Inspect two subscriptions to predict two mock requests. Select progress 5/10 for 50%, then Headers to see acceptance still pending. Classify a final body with accepted true and id demo-01, then change accepted to the string “true” to see the body rejected. A final HTTP response still needs body validation.
This is a local acceptance-contract inspector, not Angular Observable execution, an actual upload, network progress, or server storage.
Recreate this example
Build one complete self-contained HTML page for this task: inspect the Angular mock-request contract without transferring a file.
Use these exact original practice data and worked expectations: Synthetic demo.txt contains 0123456789, ten payload bytes; FormData field file; POST /demo-upload. Zero, one, or two subscriptions imply zero, one, or two expected intercepted requests. Progress 5/10 = 50%. Headers do not establish acceptance. The final body requires accepted === true and a nonblank string id.
Implement the following useful local interactions: Subscription-count input, Calculate expected requests. Event buttons for progress/header/final/error, loaded/total fields and JSON body field. Missing total is indeterminate; retain the validated loaded byte count; bad HTTP 200 body held; error no automatic retry. Explicitly label all events authored local cases, not native Angular execution. Preserve the supplied source text and distinguish an uncomputed state, a successful local result, and an invalid or incomplete input. Update output only from the current input snapshot. An input edit must retire stale feedback until the reader runs the relevant action again. Do not silently invent missing values or replace a failing case with a success.
Design the page as a compact source-and-result developer workbench with readable code, labeled inputs, and a separate explanation area. Put a compact task heading, actual sample data, the main result, and usable controls early enough to be useful on a narrow screen. Use readable text of at least 16px, generous paragraph spacing, strong contrast, native labels, accessible buttons, and visible keyboard focus. Stack long records at 360px and wrap code without page-level horizontal overflow.
Use type=button and explicit querySelector-based click handlers rather than relying on form submission. If Enter is useful, handle it only on the relevant input and ignore isComposing. Keep effects within this open page; no remote dependencies, network calls, persistence, fake downloads, or automated sending. This is a local acceptance-contract inspector, not Angular Observable execution, an actual upload, network progress, or server storage. Place worked explanations beside their data and identify teacher or synthetic examples clearly. Output only the complete HTML document, with inline styles and script and LF line endings.
Review the request-count and event-contract inspector in NOS
Transfer these cases into your installed Angular test backend and record the actual request count. Use the contract explanation in NOS to review which handler owns the subscription and why headers leave acceptance pending, then revise eligible static event descriptions in visual editing mode.