Similar to interpolation, we do not want to completely remove whitespace
nodes that are siblings of an expansion.
For example, the following template
```html
<div>
  <strong>items left<strong> {count, plural, =1 {item} other {items}}
</div>
```
was being collapsed to
```html
<div><strong>items left<strong>{count, plural, =1 {item} other {items}}</div>
```
which results in the text looking like
```
items left4
```
instead it should be collapsed to
```html
<div><strong>items left<strong> {count, plural, =1 {item} other {items}}</div>
```
which results in the text looking like
```
items left 4
```
---
**Analysis of the code and manual testing has shown that this does not cause
the generated ids to change, so there is no breaking change here.**
PR Close #31962
		
	
			
		
			
				
	
	
		
			141 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google Inc. 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 * as html from '../../src/ml_parser/ast';
 | |
| import {HtmlParser} from '../../src/ml_parser/html_parser';
 | |
| import {PRESERVE_WS_ATTR_NAME, removeWhitespaces} from '../../src/ml_parser/html_whitespaces';
 | |
| import {TokenizeOptions} from '../../src/ml_parser/lexer';
 | |
| 
 | |
| import {humanizeDom} from './ast_spec_utils';
 | |
| 
 | |
| {
 | |
|   describe('removeWhitespaces', () => {
 | |
| 
 | |
|     function parseAndRemoveWS(template: string, options?: TokenizeOptions): any[] {
 | |
|       return humanizeDom(removeWhitespaces(new HtmlParser().parse(template, 'TestComp', options)));
 | |
|     }
 | |
| 
 | |
|     it('should remove blank text nodes', () => {
 | |
|       expect(parseAndRemoveWS(' ')).toEqual([]);
 | |
|       expect(parseAndRemoveWS('\n')).toEqual([]);
 | |
|       expect(parseAndRemoveWS('\t')).toEqual([]);
 | |
|       expect(parseAndRemoveWS('    \t    \n ')).toEqual([]);
 | |
|     });
 | |
| 
 | |
|     it('should remove whitespaces (space, tab, new line) between elements', () => {
 | |
|       expect(parseAndRemoveWS('<br>  <br>\t<br>\n<br>')).toEqual([
 | |
|         [html.Element, 'br', 0],
 | |
|         [html.Element, 'br', 0],
 | |
|         [html.Element, 'br', 0],
 | |
|         [html.Element, 'br', 0],
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should remove whitespaces from child text nodes', () => {
 | |
|       expect(parseAndRemoveWS('<div><span> </span></div>')).toEqual([
 | |
|         [html.Element, 'div', 0],
 | |
|         [html.Element, 'span', 1],
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should remove whitespaces from the beginning and end of a template', () => {
 | |
|       expect(parseAndRemoveWS(` <br>\t`)).toEqual([
 | |
|         [html.Element, 'br', 0],
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should convert &ngsp; to a space and preserve it', () => {
 | |
|       expect(parseAndRemoveWS('<div><span>foo</span>&ngsp;<span>bar</span></div>')).toEqual([
 | |
|         [html.Element, 'div', 0],
 | |
|         [html.Element, 'span', 1],
 | |
|         [html.Text, 'foo', 2],
 | |
|         [html.Text, ' ', 1],
 | |
|         [html.Element, 'span', 1],
 | |
|         [html.Text, 'bar', 2],
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should replace multiple whitespaces with one space', () => {
 | |
|       expect(parseAndRemoveWS('\n\n\nfoo\t\t\t')).toEqual([[html.Text, ' foo ', 0]]);
 | |
|       expect(parseAndRemoveWS('   \n foo  \t ')).toEqual([[html.Text, ' foo ', 0]]);
 | |
|     });
 | |
| 
 | |
|     it('should not replace  ', () => {
 | |
|       expect(parseAndRemoveWS(' ')).toEqual([[html.Text, '\u00a0', 0]]);
 | |
|     });
 | |
| 
 | |
|     it('should not replace sequences of  ', () => {
 | |
|       expect(parseAndRemoveWS('  foo  ')).toEqual([
 | |
|         [html.Text, '\u00a0\u00a0foo\u00a0\u00a0', 0]
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should not replace single tab and newline with spaces', () => {
 | |
|       expect(parseAndRemoveWS('\nfoo')).toEqual([[html.Text, '\nfoo', 0]]);
 | |
|       expect(parseAndRemoveWS('\tfoo')).toEqual([[html.Text, '\tfoo', 0]]);
 | |
|     });
 | |
| 
 | |
|     it('should preserve single whitespaces between interpolations', () => {
 | |
|       expect(parseAndRemoveWS(`{{fooExp}} {{barExp}}`)).toEqual([
 | |
|         [html.Text, '{{fooExp}} {{barExp}}', 0],
 | |
|       ]);
 | |
|       expect(parseAndRemoveWS(`{{fooExp}}\t{{barExp}}`)).toEqual([
 | |
|         [html.Text, '{{fooExp}}\t{{barExp}}', 0],
 | |
|       ]);
 | |
|       expect(parseAndRemoveWS(`{{fooExp}}\n{{barExp}}`)).toEqual([
 | |
|         [html.Text, '{{fooExp}}\n{{barExp}}', 0],
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should preserve whitespaces around interpolations', () => {
 | |
|       expect(parseAndRemoveWS(` {{exp}} `)).toEqual([
 | |
|         [html.Text, ' {{exp}} ', 0],
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should preserve whitespaces around ICU expansions', () => {
 | |
|       expect(parseAndRemoveWS(`<span> {a, b, =4 {c}} </span>`, {tokenizeExpansionForms: true}))
 | |
|           .toEqual([
 | |
|             [html.Element, 'span', 0],
 | |
|             [html.Text, ' ', 1],
 | |
|             [html.Expansion, 'a', 'b', 1],
 | |
|             [html.ExpansionCase, '=4', 2],
 | |
|             [html.Text, ' ', 1],
 | |
|           ]);
 | |
|     });
 | |
| 
 | |
|     it('should preserve whitespaces inside <pre> elements', () => {
 | |
|       expect(parseAndRemoveWS(`<pre><strong>foo</strong>\n<strong>bar</strong></pre>`)).toEqual([
 | |
|         [html.Element, 'pre', 0],
 | |
|         [html.Element, 'strong', 1],
 | |
|         [html.Text, 'foo', 2],
 | |
|         [html.Text, '\n', 1],
 | |
|         [html.Element, 'strong', 1],
 | |
|         [html.Text, 'bar', 2],
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it('should skip whitespace trimming in <textarea>', () => {
 | |
|       expect(parseAndRemoveWS(`<textarea>foo\n\n  bar</textarea>`)).toEqual([
 | |
|         [html.Element, 'textarea', 0],
 | |
|         [html.Text, 'foo\n\n  bar', 1],
 | |
|       ]);
 | |
|     });
 | |
| 
 | |
|     it(`should preserve whitespaces inside elements annotated with ${PRESERVE_WS_ATTR_NAME}`,
 | |
|        () => {
 | |
|          expect(parseAndRemoveWS(`<div ${PRESERVE_WS_ATTR_NAME}><img> <img></div>`)).toEqual([
 | |
|            [html.Element, 'div', 0],
 | |
|            [html.Element, 'img', 1],
 | |
|            [html.Text, ' ', 1],
 | |
|            [html.Element, 'img', 1],
 | |
|          ]);
 | |
|        });
 | |
|   });
 | |
| }
 |