/** * @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 '@angular/localize/init'; import {CommonModule, DOCUMENT, isPlatformServer, NgComponentOutlet, NgFor, NgIf, NgTemplateOutlet, PlatformLocation} from '@angular/common'; import {MockPlatformLocation} from '@angular/common/testing'; import {afterRender, ApplicationRef, Component, ComponentRef, ContentChildren, createComponent, destroyPlatform, Directive, ElementRef, EnvironmentInjector, ErrorHandler, getPlatform, inject, Injectable, Input, NgZone, PLATFORM_ID, Provider, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef, ViewEncapsulation, ɵsetDocument, ɵwhenStable as whenStable, ɵwithI18nHydration as withI18nHydration} from '@angular/core'; import {Console} from '@angular/core/src/console'; import {HydrationStatus, readHydrationInfo, SSR_CONTENT_INTEGRITY_MARKER} from '@angular/core/src/hydration/utils'; import {getComponentDef} from '@angular/core/src/render3/definition'; import {NoopNgZone} from '@angular/core/src/zone/ng_zone'; import {TestBed} from '@angular/core/testing'; import {bootstrapApplication, HydrationFeature, HydrationFeatureKind, provideClientHydration} from '@angular/platform-browser'; import {provideRouter, RouterOutlet, Routes} from '@angular/router'; import {provideServerRendering} from '../public_api'; import {renderApplication} from '../src/utils'; /** * The name of the attribute that contains a slot index * inside the TransferState storage where hydration info * could be found. */ const NGH_ATTR_NAME = 'ngh'; const EMPTY_TEXT_NODE_COMMENT = 'ngetn'; const TEXT_NODE_SEPARATOR_COMMENT = 'ngtns'; const SKIP_HYDRATION_ATTR_NAME = 'ngSkipHydration'; const SKIP_HYDRATION_ATTR_NAME_LOWER_CASE = SKIP_HYDRATION_ATTR_NAME.toLowerCase(); const TRANSFER_STATE_TOKEN_ID = '__nghData__'; const NGH_ATTR_REGEXP = new RegExp(` ${NGH_ATTR_NAME}=".*?"`, 'g'); const EMPTY_TEXT_NODE_REGEXP = new RegExp(``, 'g'); const TEXT_NODE_SEPARATOR_REGEXP = new RegExp(``, 'g'); /** * Drop utility attributes such as `ng-version`, `ng-server-context` and `ngh`, * so that it's easier to make assertions in tests. */ function stripUtilAttributes(html: string, keepNgh: boolean): string { html = html.replace(/ ng-version=".*?"/g, '') .replace(/ ng-server-context=".*?"/g, '') .replace(/ ng-reflect-(.*?)=".*?"/g, '') .replace(/ _nghost(.*?)=""/g, '') .replace(/ _ngcontent(.*?)=""/g, ''); if (!keepNgh) { html = html.replace(NGH_ATTR_REGEXP, '') .replace(EMPTY_TEXT_NODE_REGEXP, '') .replace(TEXT_NODE_SEPARATOR_REGEXP, ''); } return html; } function getComponentRef(appRef: ApplicationRef): ComponentRef { return appRef.components[0]; } /** * Extracts a portion of HTML located inside of the `` element. * This content belongs to the application view (and supporting TransferState * scripts) rendered on the server. */ function getAppContents(html: string): string { const result = stripUtilAttributes(html, true).match(/(.*?)<\/body>/s); return result ? result[1] : html; } /** * Converts a static HTML to a DOM structure. * * @param html the rendered html in test * @param doc the document object * @returns a div element containing a copy of the app contents */ function convertHtmlToDom(html: string, doc: Document): HTMLElement { const contents = getAppContents(html); const container = doc.createElement('div'); container.innerHTML = contents; return container; } function stripSsrIntegrityMarker(input: string): string { return input.replace(``, ''); } function stripTransferDataScript(input: string): string { return input.replace(/