mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
Commit a0b2d36415 (diff-3975e0ee5aa3e06ecbcd76f5fa5134612f7fd2e6802ca7d370973bd410aab55cR25-R31) changed the serialization phase logic so
that when the state is empty the script tag is not added to the document. As a side effect, this caused the `toJson` not called which caused the `onSerialize` callbacks also not to be called.
These callbacks are used to provide the value for a key when `toJson` is called. Example: https://github.com/ngrx/platform/issues/101#issuecomment-351998548
Closes #47172
PR Close #47888
86 lines
3.1 KiB
TypeScript
86 lines
3.1 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 {Component, NgModule,} from '@angular/core';
|
|
import {BrowserModule, makeStateKey, TransferState} from '@angular/platform-browser';
|
|
import {renderModule, ServerModule} from '@angular/platform-server';
|
|
|
|
describe('transfer_state', () => {
|
|
const defaultExpectedOutput =
|
|
'<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER" ng-server-context="other">Works!</app><script id="transfer-state" type="application/json">{&q;test&q;:10}</script></body></html>';
|
|
|
|
it('adds transfer script tag when using renderModule', async () => {
|
|
const STATE_KEY = makeStateKey<number>('test');
|
|
|
|
@Component({selector: 'app', template: 'Works!'})
|
|
class TransferComponent {
|
|
constructor(private transferStore: TransferState) {
|
|
this.transferStore.set(STATE_KEY, 10);
|
|
}
|
|
}
|
|
|
|
@NgModule({
|
|
bootstrap: [TransferComponent],
|
|
declarations: [TransferComponent],
|
|
imports: [BrowserModule.withServerTransition({appId: 'transfer'}), ServerModule],
|
|
})
|
|
class TransferStoreModule {
|
|
}
|
|
|
|
const output = await renderModule(TransferStoreModule, {document: '<app></app>'});
|
|
expect(output).toBe(defaultExpectedOutput);
|
|
});
|
|
|
|
it('cannot break out of <script> tag in serialized output', async () => {
|
|
const STATE_KEY = makeStateKey<string>('testString');
|
|
|
|
@Component({selector: 'esc-app', template: 'Works!'})
|
|
class EscapedComponent {
|
|
constructor(private transferStore: TransferState) {
|
|
this.transferStore.set(STATE_KEY, '</script><script>alert(\'Hello&\' + "World");');
|
|
}
|
|
}
|
|
@NgModule({
|
|
bootstrap: [EscapedComponent],
|
|
declarations: [EscapedComponent],
|
|
imports: [BrowserModule.withServerTransition({appId: 'transfer'}), ServerModule],
|
|
})
|
|
class EscapedTransferStoreModule {
|
|
}
|
|
|
|
const output =
|
|
await renderModule(EscapedTransferStoreModule, {document: '<esc-app></esc-app>'});
|
|
expect(output).toBe(
|
|
'<html><head></head><body><esc-app ng-version="0.0.0-PLACEHOLDER" ng-server-context="other">Works!</esc-app>' +
|
|
'<script id="transfer-state" type="application/json">' +
|
|
'{&q;testString&q;:&q;&l;/script&g;&l;script&g;' +
|
|
'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}</script></body></html>');
|
|
});
|
|
|
|
it('adds transfer script tag when setting state during onSerialize', async () => {
|
|
const STATE_KEY = makeStateKey<number>('test');
|
|
|
|
@Component({selector: 'app', template: 'Works!'})
|
|
class TransferComponent {
|
|
constructor(private transferStore: TransferState) {
|
|
this.transferStore.onSerialize(STATE_KEY, () => 10);
|
|
}
|
|
}
|
|
|
|
@NgModule({
|
|
bootstrap: [TransferComponent],
|
|
declarations: [TransferComponent],
|
|
imports: [BrowserModule.withServerTransition({appId: 'transfer'}), ServerModule],
|
|
})
|
|
class TransferStoreModule {
|
|
}
|
|
|
|
const output = await renderModule(TransferStoreModule, {document: '<app></app>'});
|
|
expect(output).toBe(defaultExpectedOutput);
|
|
});
|
|
});
|