Skip to content

Commit

Permalink
refactor(core): restructure hydration test files (angular#58196)
Browse files Browse the repository at this point in the history
This re-organizes the hydration tests to make it easier to add new tests. Incremental hydration can have tests in a separate file after this groundwork is landed.

PR Close angular#58196
  • Loading branch information
thePunderWoman authored and AndrewKushnir committed Oct 14, 2024
1 parent d6a8d35 commit d8b5f4e
Show file tree
Hide file tree
Showing 6 changed files with 566 additions and 431 deletions.
3 changes: 3 additions & 0 deletions packages/platform-server/test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ ts_library(
jasmine_node_test(
name = "test",
bootstrap = ["//tools/testing:node"],
data = [
"//packages/core/primitives/event-dispatch:contract_bundle_min",
],
deps = [
":test_lib",
],
Expand Down
24 changes: 21 additions & 3 deletions packages/platform-server/test/dom_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export function hydrate(
return bootstrapApplication(component, {providers});
}

export function render(doc: Document, html: string) {
export function insertDomInDocument(doc: Document, html: string) {
// Get HTML contents of the `<app>`, create a DOM element and append it into the body.
const container = convertHtmlToDom(html, doc);

Expand All @@ -125,6 +125,24 @@ export function render(doc: Document, html: string) {
Array.from(container.childNodes).forEach((node) => doc.body.appendChild(node));
}

/**
* This prepares the environment before hydration begins.
*
* @param doc the document object
* @param html the server side rendered DOM string to be hydrated
* @returns a promise with the application ref
*/
export function prepareEnvironment(doc: Document, html: string) {
insertDomInDocument(doc, html);
globalThis.document = doc;
const scripts = doc.getElementsByTagName('script');
for (const script of Array.from(scripts)) {
if (script?.textContent?.startsWith('window.__jsaction_bootstrap')) {
eval(script.textContent);
}
}
}

/**
* This bootstraps an application with existing html and enables hydration support
* causing hydration to be invoked.
Expand All @@ -134,7 +152,7 @@ export function render(doc: Document, html: string) {
* @param envProviders the environment providers
* @returns a promise with the application ref
*/
export async function renderAndHydrate(
export async function prepareEnvironmentAndHydrate(
doc: Document,
html: string,
component: Type<unknown>,
Expand All @@ -143,7 +161,7 @@ export async function renderAndHydrate(
hydrationFeatures?: HydrationFeature<HydrationFeatureKind>[];
},
): Promise<ApplicationRef> {
render(doc, html);
prepareEnvironment(doc, html);
return hydrate(doc, component, options);
}

Expand Down
49 changes: 25 additions & 24 deletions packages/platform-server/test/event_replay_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ import {EventPhase} from '@angular/core/primitives/event-dispatch';
import {
getAppContents,
hydrate,
renderAndHydrate,
render as renderHtml,
prepareEnvironment,
prepareEnvironmentAndHydrate,
resetTViewsFor,
} from './dom_utils';
import {getDocument} from '@angular/core/src/render3/interfaces/document';
import {serializeDocument} from '../src/domino_adapter';

/**
* Represents the <script> tag added by the build process to inject
Expand Down Expand Up @@ -70,7 +72,6 @@ function withStrictErrorHandler() {
}

describe('event replay', () => {
let doc: Document;
const originalDocument = globalThis.document;
const originalWindow = globalThis.window;

Expand All @@ -81,7 +82,6 @@ describe('event replay', () => {

beforeEach(() => {
if (getPlatform()) destroyPlatform();
doc = TestBed.inject(DOCUMENT);
});

afterAll(() => {
Expand All @@ -91,7 +91,6 @@ describe('event replay', () => {
});

afterEach(() => {
doc.body.outerHTML = '<body></body>';
window._ejsas = {};
});

Expand Down Expand Up @@ -125,17 +124,6 @@ describe('event replay', () => {
});
}

function render(doc: Document, html: string) {
renderHtml(doc, html);
globalThis.document = doc;
const scripts = doc.getElementsByTagName('script');
for (const script of Array.from(scripts)) {
if (script?.textContent?.startsWith('window.__jsaction_bootstrap')) {
eval(script.textContent);
}
}
}

it('should work for elements with local refs', async () => {
const onClickSpy = jasmine.createSpy();

Expand All @@ -151,7 +139,9 @@ describe('event replay', () => {
}
const html = await ssr(AppComponent);
const ssrContents = getAppContents(html);
render(doc, ssrContents);
const doc = getDocument();

prepareEnvironment(doc, ssrContents);
resetTViewsFor(AppComponent);
const btn = doc.getElementById('btn')!;
btn.click();
Expand All @@ -171,7 +161,7 @@ describe('event replay', () => {
template: `
<div class="card">
<button id="inner-button" (click)="onClick()"></button>
<ng-content></ng-content>
<ng-content></ng-content>
</div>
`,
})
Expand All @@ -196,13 +186,16 @@ describe('event replay', () => {
}
const html = await ssr(AppComponent);
const ssrContents = getAppContents(html);
render(doc, ssrContents);
const doc = getDocument();

prepareEnvironment(doc, ssrContents);
resetTViewsFor(AppComponent);
const outer = doc.getElementById('outer-button')!;
const inner = doc.getElementById('inner-button')!;
outer.click();
inner.click();
const appRef = await hydrate(doc, AppComponent, {
envProviders: [{provide: PLATFORM_ID, useValue: 'browser'}],
hydrationFeatures: [withEventReplay()],
});
expect(outerOnClickSpy).toHaveBeenCalledBefore(innerOnClickSpy);
Expand All @@ -225,7 +218,8 @@ describe('event replay', () => {
const docContents = `<html><head></head><body>${EVENT_DISPATCH_SCRIPT}<app></app></body></html>`;
const html = await ssr(SimpleComponent, {doc: docContents});
const ssrContents = getAppContents(html);
render(doc, ssrContents);
const doc = getDocument();
prepareEnvironment(doc, ssrContents);
const el = doc.getElementById('1')!;
expect(el.hasAttribute('jsaction')).toBeTrue();
expect((el.firstChild as Element).hasAttribute('jsaction')).toBeTrue();
Expand Down Expand Up @@ -276,11 +270,14 @@ describe('event replay', () => {
const docContents = `<html><head></head><body>${EVENT_DISPATCH_SCRIPT}<app></app></body></html>`;
const html = await ssr(SimpleComponent, {doc: docContents});
const ssrContents = getAppContents(html);
render(doc, ssrContents);
const doc = getDocument();

prepareEnvironment(doc, ssrContents);
resetTViewsFor(SimpleComponent);
const bottomEl = doc.getElementById('bottom')!;
bottomEl.click();
const appRef = await hydrate(doc, SimpleComponent, {
envProviders: [{provide: PLATFORM_ID, useValue: 'browser'}],
hydrationFeatures: [withEventReplay()],
});
expect(onClickSpy).toHaveBeenCalledTimes(2);
Expand Down Expand Up @@ -308,7 +305,8 @@ describe('event replay', () => {
const docContents = `<html><head></head><body>${EVENT_DISPATCH_SCRIPT}<app></app></body></html>`;
const html = await ssr(SimpleComponent, {doc: docContents});
const ssrContents = getAppContents(html);
render(doc, ssrContents);
const doc = getDocument();
prepareEnvironment(doc, ssrContents);
resetTViewsFor(SimpleComponent);
const bottomEl = doc.getElementById('bottom')!;
bottomEl.click();
Expand Down Expand Up @@ -344,11 +342,13 @@ describe('event replay', () => {
const docContents = `<html><head></head><body>${EVENT_DISPATCH_SCRIPT}<app></app></body></html>`;
const html = await ssr(SimpleComponent, {doc: docContents});
const ssrContents = getAppContents(html);
render(doc, ssrContents);
const doc = getDocument();
prepareEnvironment(doc, ssrContents);
resetTViewsFor(SimpleComponent);
const bottomEl = doc.getElementById('bottom')!;
bottomEl.click();
await hydrate(doc, SimpleComponent, {
envProviders: [{provide: PLATFORM_ID, useValue: 'browser'}],
hydrationFeatures: [withEventReplay()],
});
const replayedEvent = currentEvent;
Expand Down Expand Up @@ -396,7 +396,8 @@ describe('event replay', () => {
expect(hasEventDispatchScript(ssrContents)).toBeFalse();

resetTViewsFor(SimpleComponent);
await renderAndHydrate(doc, ssrContents, SimpleComponent, {
const doc = getDocument();
await prepareEnvironmentAndHydrate(doc, ssrContents, SimpleComponent, {
envProviders: [
{provide: PLATFORM_ID, useValue: 'browser'},
// This ensures that there are no errors while bootstrapping an application
Expand Down
Loading

0 comments on commit d8b5f4e

Please sign in to comment.