angular-cli/packages/angular/ssr/test/app-engine_spec.ts
Alan Agius 2640bf7a68 fix(@angular/ssr): correct route extraction and error handling
This commit introduces the following changes:
- Disallows paths starting with a slash to match Angular router behavior.
- Errors are now stored and displayed at a later stage, improving UX by avoiding unnecessary stack traces that are not useful in this context.
2024-09-16 21:22:10 +02:00

195 lines
6.8 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.dev/license
*/
// The compiler is needed as tests are in JIT.
/* eslint-disable import/no-unassigned-import */
import '@angular/compiler';
/* eslint-enable import/no-unassigned-import */
import { Component } from '@angular/core';
import { destroyAngularServerApp, getOrCreateAngularServerApp } from '../src/app';
import { AngularAppEngine } from '../src/app-engine';
import { setAngularAppEngineManifest } from '../src/manifest';
import { RenderMode } from '../src/routes/route-config';
import { setAngularAppTestingManifest } from './testing-utils';
describe('AngularAppEngine', () => {
let appEngine: AngularAppEngine;
describe('Localized app', () => {
beforeAll(() => {
destroyAngularServerApp();
setAngularAppEngineManifest({
// Note: Although we are testing only one locale, we need to configure two or more
// to ensure that we test a different code path.
entryPoints: new Map(
['it', 'en'].map((locale) => [
locale,
async () => {
@Component({
standalone: true,
selector: `app-home-${locale}`,
template: `Home works ${locale.toUpperCase()}`,
})
class HomeComponent {}
setAngularAppTestingManifest(
[{ path: 'home', component: HomeComponent }],
[{ path: '**', renderMode: RenderMode.Server }],
locale,
);
return {
ɵgetOrCreateAngularServerApp: getOrCreateAngularServerApp,
ɵdestroyAngularServerApp: destroyAngularServerApp,
};
},
]),
),
basePath: '',
staticPathsHeaders: new Map([
[
'/about',
[
['Cache-Control', 'no-cache'],
['X-Some-Header', 'value'],
],
],
]),
});
appEngine = new AngularAppEngine();
});
describe('render', () => {
it('should return null for requests to unknown pages', async () => {
const request = new Request('https://example.com/unknown/page');
const response = await appEngine.render(request);
expect(response).toBeNull();
});
it('should return null for requests with unknown locales', async () => {
const request = new Request('https://example.com/es/home');
const response = await appEngine.render(request);
expect(response).toBeNull();
});
it('should return a rendered page with correct locale', async () => {
const request = new Request('https://example.com/it/home');
const response = await appEngine.render(request);
expect(await response?.text()).toContain('Home works IT');
});
it('should correctly render the content when the URL ends with "index.html" with correct locale', async () => {
const request = new Request('https://example.com/it/home/index.html');
const response = await appEngine.render(request);
expect(await response?.text()).toContain('Home works IT');
});
it('should return null for requests to unknown pages in a locale', async () => {
const request = new Request('https://example.com/it/unknown/page');
const response = await appEngine.render(request);
expect(response).toBeNull();
});
it('should return null for requests to file-like resources in a locale', async () => {
const request = new Request('https://example.com/it/logo.png');
const response = await appEngine.render(request);
expect(response).toBeNull();
});
});
describe('getPrerenderHeaders', () => {
it('should return headers for a known path without index.html', () => {
const request = new Request('https://example.com/about');
const headers = appEngine.getPrerenderHeaders(request);
expect(Object.fromEntries(headers.entries())).toEqual({
'Cache-Control': 'no-cache',
'X-Some-Header': 'value',
});
});
it('should return headers for a known path with index.html', () => {
const request = new Request('https://example.com/about/index.html');
const headers = appEngine.getPrerenderHeaders(request);
expect(Object.fromEntries(headers.entries())).toEqual({
'Cache-Control': 'no-cache',
'X-Some-Header': 'value',
});
});
it('should return no headers for unknown paths', () => {
const request = new Request('https://example.com/unknown/path');
const headers = appEngine.getPrerenderHeaders(request);
expect(headers).toHaveSize(0);
});
});
});
describe('Non-localized app', () => {
beforeAll(() => {
destroyAngularServerApp();
setAngularAppEngineManifest({
entryPoints: new Map([
[
'',
async () => {
@Component({
standalone: true,
selector: 'app-home',
template: `Home works`,
})
class HomeComponent {}
setAngularAppTestingManifest(
[{ path: 'home', component: HomeComponent }],
[{ path: '**', renderMode: RenderMode.Server }],
);
return {
ɵgetOrCreateAngularServerApp: getOrCreateAngularServerApp,
ɵdestroyAngularServerApp: destroyAngularServerApp,
};
},
],
]),
basePath: '',
staticPathsHeaders: new Map(),
});
appEngine = new AngularAppEngine();
});
it('should return null for requests to file-like resources', async () => {
const request = new Request('https://example.com/logo.png');
const response = await appEngine.render(request);
expect(response).toBeNull();
});
it('should return null for requests to unknown pages', async () => {
const request = new Request('https://example.com/unknown/page');
const response = await appEngine.render(request);
expect(response).toBeNull();
});
it('should return a rendered page for known paths', async () => {
const request = new Request('https://example.com/home');
const response = await appEngine.render(request);
expect(await response?.text()).toContain('Home works');
});
it('should correctly render the content when the URL ends with "index.html"', async () => {
const request = new Request('https://example.com/home/index.html');
const response = await appEngine.render(request);
expect(await response?.text()).toContain('Home works');
});
});
});