mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(http): Dynamicaly call the global fetch implementation (#57531)
Instead of using the reference that existing when `FetchBackend` is setup. fixes #57527 PR Close #57531
This commit is contained in:
parent
fe5c4e086a
commit
c2892fee58
2 changed files with 44 additions and 2 deletions
|
|
@ -52,9 +52,11 @@ function getResponseUrl(response: Response): string | null {
|
|||
*/
|
||||
@Injectable()
|
||||
export class FetchBackend implements HttpBackend {
|
||||
// We need to bind the native fetch to its context or it will throw an "illegal invocation"
|
||||
// We use an arrow function to always reference the current global implementation of `fetch`.
|
||||
// This is helpful for cases when the global `fetch` implementation is modified by external code,
|
||||
// see https://github.com/angular/angular/issues/57527.
|
||||
private readonly fetchImpl =
|
||||
inject(FetchFactory, {optional: true})?.fetch ?? fetch.bind(globalThis);
|
||||
inject(FetchFactory, {optional: true})?.fetch ?? ((...args) => globalThis.fetch(...args));
|
||||
private readonly ngZone = inject(NgZone);
|
||||
|
||||
handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,14 @@ import {Observable, of, Subject} from 'rxjs';
|
|||
import {catchError, retry, scan, skip, take, toArray} from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
HttpClient,
|
||||
HttpDownloadProgressEvent,
|
||||
HttpErrorResponse,
|
||||
HttpHeaderResponse,
|
||||
HttpParams,
|
||||
HttpStatusCode,
|
||||
provideHttpClient,
|
||||
withFetch,
|
||||
} from '../public_api';
|
||||
import {FetchBackend, FetchFactory} from '../src/fetch';
|
||||
|
||||
|
|
@ -416,6 +419,43 @@ describe('FetchBackend', async () => {
|
|||
fetchMock.mockFlush(0, 'CORS 0 status');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dynamic global fetch', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideHttpClient(withFetch())],
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the current implementation of the global fetch', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
try {
|
||||
const fakeFetch = jasmine
|
||||
.createSpy('', () => Promise.resolve(new Response(JSON.stringify({foo: 'bar'}))))
|
||||
.and.callThrough();
|
||||
globalThis.fetch = fakeFetch;
|
||||
|
||||
const client = TestBed.inject(HttpClient);
|
||||
expect(fakeFetch).not.toHaveBeenCalled();
|
||||
let response = await client.get<unknown>('').toPromise();
|
||||
expect(fakeFetch).toHaveBeenCalled();
|
||||
expect(response).toEqual({foo: 'bar'});
|
||||
|
||||
// We dynamicaly change the implementation of fetch
|
||||
const fakeFetch2 = jasmine
|
||||
.createSpy('', () => Promise.resolve(new Response(JSON.stringify({foo: 'baz'}))))
|
||||
.and.callThrough();
|
||||
globalThis.fetch = fakeFetch2;
|
||||
response = await client.get<unknown>('').toPromise();
|
||||
expect(response).toEqual({foo: 'baz'});
|
||||
} finally {
|
||||
// We need to restore the original fetch implementation, else the tests might become flaky
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export class MockFetchFactory extends FetchFactory {
|
||||
|
|
|
|||
Loading…
Reference in a new issue