angular/packages/platform-browser/test/hydration_spec.ts
Matthieu Riegler 0a38dc3c26 refactor(core): throw an error when hydration marker is missing from DOM (#51170)
non-destructive hydration expects the DOM tree to have the same structure in both places.
With this commit, the app will throw an error if comments are stripped out by the http server (eg by some CDNs).

fixes #51160

PR Close #51170
2023-08-04 11:31:49 -04:00

92 lines
3.2 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {DOCUMENT} from '@angular/common';
import {HttpClient, provideHttpClient} from '@angular/common/http';
import {HttpTestingController, provideHttpClientTesting} from '@angular/common/http/testing';
import {ApplicationRef, Component, Injectable, ɵSSR_CONTENT_INTEGRITY_MARKER as SSR_CONTENT_INTEGRITY_MARKER} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {withBody} from '@angular/private/testing';
import {BehaviorSubject} from 'rxjs';
import {provideClientHydration, withNoHttpTransferCache} from '../public_api';
describe('provideClientHydration', () => {
@Component({selector: 'test-hydrate-app', template: ''})
class SomeComponent {
}
function makeRequestAndExpectOne(url: string, body: string): void {
TestBed.inject(HttpClient).get(url).subscribe();
TestBed.inject(HttpTestingController).expectOne(url).flush(body);
}
function makeRequestAndExpectNone(url: string): void {
TestBed.inject(HttpClient).get(url).subscribe();
TestBed.inject(HttpTestingController).expectNone(url);
}
@Injectable()
class ApplicationRefPatched extends ApplicationRef {
override isStable = new BehaviorSubject<boolean>(false);
}
describe('default', () => {
beforeEach(withBody(
`<!--${SSR_CONTENT_INTEGRITY_MARKER}--><test-hydrate-app></test-hydrate-app>`, () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
declarations: [SomeComponent],
providers: [
{provide: DOCUMENT, useFactory: () => document},
{provide: ApplicationRef, useClass: ApplicationRefPatched},
provideClientHydration(),
provideHttpClient(),
provideHttpClientTesting(),
],
});
const appRef = TestBed.inject(ApplicationRef);
appRef.bootstrap(SomeComponent);
}));
it(`should use cached HTTP calls`, () => {
makeRequestAndExpectOne('/test-1', 'foo');
// Do the same call, this time it should served from cache.
makeRequestAndExpectNone('/test-1');
});
});
describe('withNoHttpTransferCache', () => {
beforeEach(withBody(
`<!--${SSR_CONTENT_INTEGRITY_MARKER}--><test-hydrate-app></test-hydrate-app>`, () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
declarations: [SomeComponent],
providers: [
{provide: DOCUMENT, useFactory: () => document},
{provide: ApplicationRef, useClass: ApplicationRefPatched},
provideClientHydration(withNoHttpTransferCache()),
provideHttpClient(),
provideHttpClientTesting(),
],
});
const appRef = TestBed.inject(ApplicationRef);
appRef.bootstrap(SomeComponent);
}));
it(`should not cached HTTP calls`, () => {
makeRequestAndExpectOne('/test-1', 'foo');
// Do the same call, this time should pass through as cache is disabled.
makeRequestAndExpectOne('/test-1', 'foo');
});
});
});