| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | /** | 
					
						
							|  |  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  |  * | 
					
						
							|  |  |  |  |  * 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
 | 
					
						
							|  |  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-02-25 15:26:10 -08:00
										 |  |  |  | import {AST} from '../../../src/expression_parser/ast'; | 
					
						
							|  |  |  |  | import {Lexer} from '../../../src/expression_parser/lexer'; | 
					
						
							|  |  |  |  | import {Parser} from '../../../src/expression_parser/parser'; | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | import * as i18n from '../../../src/i18n/i18n_ast'; | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | import * as o from '../../../src/output/output_ast'; | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  | import {ParseSourceSpan} from '../../../src/parse_util'; | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | import * as t from '../../../src/render3/r3_ast'; | 
					
						
							|  |  |  |  | import {I18nContext} from '../../../src/render3/view/i18n/context'; | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  | import {serializeI18nMessageForGetMsg} from '../../../src/render3/view/i18n/get_msg_utils'; | 
					
						
							|  |  |  |  | import {serializeIcuNode} from '../../../src/render3/view/i18n/icu_serializer'; | 
					
						
							|  |  |  |  | import {serializeI18nMessageForLocalize} from '../../../src/render3/view/i18n/localize_utils'; | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  | import {I18nMeta, parseI18nMeta} from '../../../src/render3/view/i18n/meta'; | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  | import {formatI18nPlaceholderName} from '../../../src/render3/view/i18n/util'; | 
					
						
							| 
									
										
										
										
											2020-10-29 09:45:01 +00:00
										 |  |  |  | import {LEADING_TRIVIA_CHARS} from '../../../src/render3/view/template'; | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | import {parseR3 as parse} from './util'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-25 15:26:10 -08:00
										 |  |  |  | const expressionParser = new Parser(new Lexer()); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  | const i18nOf = (element: t.Node&{i18n?: i18n.I18nMeta}) => element.i18n!; | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | describe('I18nContext', () => { | 
					
						
							|  |  |  |  |   it('should support i18n content collection', () => { | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     const ref = o.variable('ref'); | 
					
						
							|  |  |  |  |     const ast = new i18n.Message([], {}, {}, '', '', ''); | 
					
						
							|  |  |  |  |     const ctx = new I18nContext(5, ref, 0, null, ast); | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // basic checks
 | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     expect(ctx.isRoot).toBe(true); | 
					
						
							|  |  |  |  |     expect(ctx.isResolved).toBe(true); | 
					
						
							|  |  |  |  |     expect(ctx.id).toBe(0); | 
					
						
							|  |  |  |  |     expect(ctx.ref).toBe(ref); | 
					
						
							|  |  |  |  |     expect(ctx.index).toBe(5); | 
					
						
							|  |  |  |  |     expect(ctx.templateIndex).toBe(null); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const tree = parse('<div i18n>A {{ valueA }} <div> B </div><p *ngIf="visible"> C </p></div>'); | 
					
						
							|  |  |  |  |     const [boundText, element, template] = (tree.nodes[0] as t.Element).children; | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // data collection checks
 | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     expect(ctx.placeholders.size).toBe(0); | 
					
						
							|  |  |  |  |     ctx.appendBoundText(i18nOf(boundText));       // interpolation
 | 
					
						
							|  |  |  |  |     ctx.appendElement(i18nOf(element), 1);        // open tag
 | 
					
						
							|  |  |  |  |     ctx.appendElement(i18nOf(element), 1, true);  // close tag
 | 
					
						
							|  |  |  |  |     ctx.appendTemplate(i18nOf(template), 2);      // open + close tags
 | 
					
						
							|  |  |  |  |     expect(ctx.placeholders.size).toBe(5); | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // binding collection checks
 | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     expect(ctx.bindings.size).toBe(0); | 
					
						
							| 
									
										
										
										
											2019-07-16 12:18:32 -07:00
										 |  |  |  |     ctx.appendBinding(expressionParser.parseInterpolation('{{ valueA }}', '', 0) as AST); | 
					
						
							|  |  |  |  |     ctx.appendBinding(expressionParser.parseInterpolation('{{ valueB }}', '', 0) as AST); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     expect(ctx.bindings.size).toBe(2); | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support nested contexts', () => { | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     const template = `
 | 
					
						
							|  |  |  |  |       <div i18n> | 
					
						
							|  |  |  |  |         A {{ valueA }} | 
					
						
							|  |  |  |  |         <div>A</div> | 
					
						
							|  |  |  |  |         <b *ngIf="visible"> | 
					
						
							|  |  |  |  |           B {{ valueB }} | 
					
						
							|  |  |  |  |           <div>B</div> | 
					
						
							|  |  |  |  |           C {{ valueC }} | 
					
						
							|  |  |  |  |         </b> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     `;
 | 
					
						
							|  |  |  |  |     const tree = parse(template); | 
					
						
							|  |  |  |  |     const root = tree.nodes[0] as t.Element; | 
					
						
							|  |  |  |  |     const [boundTextA, elementA, templateA] = root.children; | 
					
						
							|  |  |  |  |     const elementB = (templateA as t.Template).children[0] as t.Element; | 
					
						
							|  |  |  |  |     const [boundTextB, elementC, boundTextC] = (elementB as t.Element).children; | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     // simulate I18nContext for a given template
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |     const ctx = new I18nContext(1, o.variable('ctx'), 0, null, root.i18n!); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // set data for root ctx
 | 
					
						
							|  |  |  |  |     ctx.appendBoundText(i18nOf(boundTextA)); | 
					
						
							| 
									
										
										
										
											2019-07-16 12:18:32 -07:00
										 |  |  |  |     ctx.appendBinding(expressionParser.parseInterpolation('{{ valueA }}', '', 0) as AST); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     ctx.appendElement(i18nOf(elementA), 0); | 
					
						
							|  |  |  |  |     ctx.appendTemplate(i18nOf(templateA), 1); | 
					
						
							|  |  |  |  |     ctx.appendElement(i18nOf(elementA), 0, true); | 
					
						
							|  |  |  |  |     expect(ctx.bindings.size).toBe(1); | 
					
						
							|  |  |  |  |     expect(ctx.placeholders.size).toBe(5); | 
					
						
							|  |  |  |  |     expect(ctx.isResolved).toBe(false); | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // create child context
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |     const childCtx = ctx.forkChildContext(2, 1, (templateA as t.Template).i18n!); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     expect(childCtx.bindings.size).toBe(0); | 
					
						
							|  |  |  |  |     expect(childCtx.isRoot).toBe(false); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // set data for child context
 | 
					
						
							|  |  |  |  |     childCtx.appendElement(i18nOf(elementB), 0); | 
					
						
							|  |  |  |  |     childCtx.appendBoundText(i18nOf(boundTextB)); | 
					
						
							| 
									
										
										
										
											2019-07-16 12:18:32 -07:00
										 |  |  |  |     childCtx.appendBinding(expressionParser.parseInterpolation('{{ valueB }}', '', 0) as AST); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     childCtx.appendElement(i18nOf(elementC), 1); | 
					
						
							|  |  |  |  |     childCtx.appendElement(i18nOf(elementC), 1, true); | 
					
						
							|  |  |  |  |     childCtx.appendBoundText(i18nOf(boundTextC)); | 
					
						
							| 
									
										
										
										
											2019-07-16 12:18:32 -07:00
										 |  |  |  |     childCtx.appendBinding(expressionParser.parseInterpolation('{{ valueC }}', '', 0) as AST); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     childCtx.appendElement(i18nOf(elementB), 0, true); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     expect(childCtx.bindings.size).toBe(2); | 
					
						
							|  |  |  |  |     expect(childCtx.placeholders.size).toBe(6); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // ctx bindings and placeholders are not shared,
 | 
					
						
							|  |  |  |  |     // so root bindings and placeholders do not change
 | 
					
						
							|  |  |  |  |     expect(ctx.bindings.size).toBe(1); | 
					
						
							|  |  |  |  |     expect(ctx.placeholders.size).toBe(5); | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // reconcile
 | 
					
						
							|  |  |  |  |     ctx.reconcileChildContext(childCtx); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // verify placeholders
 | 
					
						
							|  |  |  |  |     const expected = new Map([ | 
					
						
							|  |  |  |  |       ['INTERPOLATION', '<27>0<EFBFBD>'], ['START_TAG_DIV', '<27>#0<>|<7C>#1:1<>'], | 
					
						
							|  |  |  |  |       ['START_BOLD_TEXT', '<27>*1:1<><31>#0:1<>'], ['CLOSE_BOLD_TEXT', '<27>/#0:1<><31>/*1:1<>'], | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |       ['CLOSE_TAG_DIV', '<27>/#0<>|<7C>/#1:1<>'], ['INTERPOLATION_1', '<27>0:1<>'], ['INTERPOLATION_2', '<27>1:1<>'] | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     ]); | 
					
						
							|  |  |  |  |     const phs = ctx.getSerializedPlaceholders(); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |     expected.forEach((value, key) => { | 
					
						
							|  |  |  |  |       expect(phs.get(key)!.join('|')).toEqual(value); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // placeholders are added into the root ctx
 | 
					
						
							|  |  |  |  |     expect(phs.size).toBe(expected.size); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // root context is considered resolved now
 | 
					
						
							|  |  |  |  |     expect(ctx.isResolved).toBe(true); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // bindings are not merged into root ctx
 | 
					
						
							|  |  |  |  |     expect(ctx.bindings.size).toBe(1); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should support templates based on <ng-template>', () => { | 
					
						
							|  |  |  |  |     const template = `
 | 
					
						
							|  |  |  |  |       <ng-template i18n> | 
					
						
							|  |  |  |  |         Level A | 
					
						
							|  |  |  |  |         <ng-template> | 
					
						
							|  |  |  |  |           Level B | 
					
						
							|  |  |  |  |           <ng-template> | 
					
						
							|  |  |  |  |             Level C | 
					
						
							|  |  |  |  |           </ng-template> | 
					
						
							|  |  |  |  |         </ng-template> | 
					
						
							|  |  |  |  |       </ng-template> | 
					
						
							|  |  |  |  |     `;
 | 
					
						
							|  |  |  |  |     const tree = parse(template); | 
					
						
							|  |  |  |  |     const root = tree.nodes[0] as t.Template; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const [textA, templateA] = root.children; | 
					
						
							|  |  |  |  |     const [textB, templateB] = (templateA as t.Template).children; | 
					
						
							|  |  |  |  |     const [textC] = (templateB as t.Template).children; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // simulate I18nContext for a given template
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |     const ctxLevelA = new I18nContext(0, o.variable('ctx'), 0, null, root.i18n!); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // create Level A context
 | 
					
						
							|  |  |  |  |     ctxLevelA.appendTemplate(i18nOf(templateA), 1); | 
					
						
							|  |  |  |  |     expect(ctxLevelA.placeholders.size).toBe(2); | 
					
						
							|  |  |  |  |     expect(ctxLevelA.isResolved).toBe(false); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // create Level B context
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |     const ctxLevelB = ctxLevelA.forkChildContext(0, 1, (templateA as t.Template).i18n!); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     ctxLevelB.appendTemplate(i18nOf(templateB), 1); | 
					
						
							|  |  |  |  |     expect(ctxLevelB.isRoot).toBe(false); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // create Level 2 context
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |     const ctxLevelC = ctxLevelB.forkChildContext(0, 1, (templateB as t.Template).i18n!); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     expect(ctxLevelC.isRoot).toBe(false); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // reconcile
 | 
					
						
							|  |  |  |  |     ctxLevelB.reconcileChildContext(ctxLevelC); | 
					
						
							|  |  |  |  |     ctxLevelA.reconcileChildContext(ctxLevelB); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // verify placeholders
 | 
					
						
							|  |  |  |  |     const expected = new Map( | 
					
						
							|  |  |  |  |         [['START_TAG_NG-TEMPLATE', '<27>*1:1<>|<7C>*1:2<>'], ['CLOSE_TAG_NG-TEMPLATE', '<27>/*1:2<>|<7C>/*1:1<>']]); | 
					
						
							|  |  |  |  |     const phs = ctxLevelA.getSerializedPlaceholders(); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |     expected.forEach((value, key) => { | 
					
						
							|  |  |  |  |       expect(phs.get(key)!.join('|')).toEqual(value); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // placeholders are added into the root ctx
 | 
					
						
							|  |  |  |  |     expect(phs.size).toBe(expected.size); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // root context is considered resolved now
 | 
					
						
							|  |  |  |  |     expect(ctxLevelA.isResolved).toBe(true); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | describe('Utils', () => { | 
					
						
							|  |  |  |  |   it('formatI18nPlaceholderName', () => { | 
					
						
							|  |  |  |  |     const cases = [ | 
					
						
							|  |  |  |  |       // input, output
 | 
					
						
							|  |  |  |  |       ['', ''], ['ICU', 'icu'], ['ICU_1', 'icu_1'], ['ICU_1000', 'icu_1000'], | 
					
						
							|  |  |  |  |       ['START_TAG_NG-CONTAINER', 'startTagNgContainer'], | 
					
						
							|  |  |  |  |       ['START_TAG_NG-CONTAINER_1', 'startTagNgContainer_1'], ['CLOSE_TAG_ITALIC', 'closeTagItalic'], | 
					
						
							|  |  |  |  |       ['CLOSE_TAG_BOLD_1', 'closeTagBold_1'] | 
					
						
							|  |  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |     cases.forEach(([input, output]) => { | 
					
						
							|  |  |  |  |       expect(formatI18nPlaceholderName(input)).toEqual(output); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |   describe('metadata serialization', () => { | 
					
						
							|  |  |  |  |     it('parseI18nMeta()', () => { | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |       expect(parseI18nMeta('')).toEqual(meta()); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('desc')).toEqual(meta('', '', 'desc')); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('desc@@id')).toEqual(meta('id', '', 'desc')); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('meaning|desc')).toEqual(meta('', 'meaning', 'desc')); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('meaning|desc@@id')).toEqual(meta('id', 'meaning', 'desc')); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('@@id')).toEqual(meta('id', '', '')); | 
					
						
							| 
									
										
										
										
											2019-12-03 08:36:56 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('\n   ')).toEqual(meta()); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('\n   desc\n   ')).toEqual(meta('', '', 'desc')); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('\n   desc@@id\n   ')).toEqual(meta('id', '', 'desc')); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('\n   meaning|desc\n   ')).toEqual(meta('', 'meaning', 'desc')); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('\n   meaning|desc@@id\n   ')).toEqual(meta('id', 'meaning', 'desc')); | 
					
						
							|  |  |  |  |       expect(parseI18nMeta('\n   @@id\n   ')).toEqual(meta('id', '', '')); | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     it('serializeI18nHead()', () => { | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta(), [literal('')], [], []).serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({cooked: '', raw: '', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('', '', 'desc'), [literal('')], [], []).serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({cooked: ':desc:', raw: ':desc:', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('id', '', 'desc'), [literal('')], [], []).serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({cooked: ':desc@@id:', raw: ':desc@@id:', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect( | 
					
						
							|  |  |  |  |           o.localizedString(meta('', 'meaning', 'desc'), [literal('')], [], []).serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: ':meaning|desc:', | 
					
						
							|  |  |  |  |             raw: ':meaning|desc:', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							|  |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal('')], [], []) | 
					
						
							|  |  |  |  |                  .serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: ':meaning|desc@@id:', | 
					
						
							|  |  |  |  |             raw: ':meaning|desc@@id:', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							|  |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('id', '', ''), [literal('')], [], []).serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({cooked: ':@@id:', raw: ':@@id:', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       // Escaping colons (block markers)
 | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('id:sub_id', 'meaning', 'desc'), [literal('')], [], []) | 
					
						
							|  |  |  |  |                  .serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: ':meaning|desc@@id:sub_id:', | 
					
						
							|  |  |  |  |             raw: ':meaning|desc@@id\\:sub_id:', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							|  |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('id', 'meaning:sub_meaning', 'desc'), [literal('')], [], []) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |                  .serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: ':meaning:sub_meaning|desc@@id:', | 
					
						
							|  |  |  |  |             raw: ':meaning\\:sub_meaning|desc@@id:', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							|  |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('id', 'meaning', 'desc:sub_desc'), [literal('')], [], []) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |                  .serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: ':meaning|desc:sub_desc@@id:', | 
					
						
							|  |  |  |  |             raw: ':meaning|desc\\:sub_desc@@id:', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							|  |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal('message source')], [], []) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |                  .serializeI18nHead()) | 
					
						
							|  |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: ':meaning|desc@@id:message source', | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |             raw: ':meaning|desc@@id:message source', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal(':message source')], [], []) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |                  .serializeI18nHead()) | 
					
						
							|  |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: ':meaning|desc@@id::message source', | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |             raw: ':meaning|desc@@id::message source', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('', '', ''), [literal('message source')], [], []) | 
					
						
							|  |  |  |  |                  .serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: 'message source', | 
					
						
							|  |  |  |  |             raw: 'message source', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							|  |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('', '', ''), [literal(':message source')], [], []) | 
					
						
							|  |  |  |  |                  .serializeI18nHead()) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({ | 
					
						
							|  |  |  |  |             cooked: ':message source', | 
					
						
							|  |  |  |  |             raw: '\\:message source', | 
					
						
							|  |  |  |  |             range: jasmine.any(ParseSourceSpan) | 
					
						
							|  |  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     it('serializeI18nPlaceholderBlock()', () => { | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString(meta('', '', ''), [literal(''), literal('')], [literal('')], []) | 
					
						
							|  |  |  |  |                  .serializeI18nTemplatePart(1)) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({cooked: '', raw: '', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString( | 
					
						
							|  |  |  |  |                   meta('', '', ''), [literal(''), literal('')], | 
					
						
							|  |  |  |  |                   [new o.LiteralPiece('abc', {} as any)], []) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |                  .serializeI18nTemplatePart(1)) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({cooked: ':abc:', raw: ':abc:', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect( | 
					
						
							|  |  |  |  |           o.localizedString(meta('', '', ''), [literal(''), literal('message')], [literal('')], []) | 
					
						
							|  |  |  |  |               .serializeI18nTemplatePart(1)) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({cooked: 'message', raw: 'message', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString( | 
					
						
							|  |  |  |  |                   meta('', '', ''), [literal(''), literal('message')], [literal('abc')], []) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |                  .serializeI18nTemplatePart(1)) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual( | 
					
						
							|  |  |  |  |               {cooked: ':abc:message', raw: ':abc:message', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect( | 
					
						
							|  |  |  |  |           o.localizedString(meta('', '', ''), [literal(''), literal(':message')], [literal('')], []) | 
					
						
							|  |  |  |  |               .serializeI18nTemplatePart(1)) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual({cooked: ':message', raw: '\\:message', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       expect(o.localizedString( | 
					
						
							|  |  |  |  |                   meta('', '', ''), [literal(''), literal(':message')], [literal('abc')], []) | 
					
						
							| 
									
										
										
										
											2019-11-15 16:25:59 +00:00
										 |  |  |  |                  .serializeI18nTemplatePart(1)) | 
					
						
							| 
									
										
										
										
											2020-09-21 12:45:19 +01:00
										 |  |  |  |           .toEqual( | 
					
						
							|  |  |  |  |               {cooked: ':abc::message', raw: ':abc::message', range: jasmine.any(ParseSourceSpan)}); | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:17:56 +01:00
										 |  |  |  |     function meta(customId?: string, meaning?: string, description?: string): I18nMeta { | 
					
						
							|  |  |  |  |       return {customId, meaning, description}; | 
					
						
							| 
									
										
										
										
											2019-09-13 12:46:05 +01:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  | describe('serializeI18nMessageForGetMsg', () => { | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |   const serialize = (input: string): string => { | 
					
						
							|  |  |  |  |     const tree = parse(`<div i18n>${input}</div>`); | 
					
						
							|  |  |  |  |     const root = tree.nodes[0] as t.Element; | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |     return serializeI18nMessageForGetMsg(root.i18n as i18n.Message); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |  |   it('should serialize plain text for `GetMsg()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('Some text')).toEqual('Some text'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   it('should serialize text with interpolation for `GetMsg()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}')) | 
					
						
							|  |  |  |  |         .toEqual('Some text {$interpolation} and {$interpolation_1}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 12:26:48 +01:00
										 |  |  |  |   it('should serialize interpolation with named placeholder for `GetMsg()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}')) | 
					
						
							|  |  |  |  |         .toEqual('{$placeholderName}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   it('should serialize content with HTML tags for `GetMsg()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('A <span>B<div>C</div></span> D')) | 
					
						
							|  |  |  |  |         .toEqual('A {$startTagSpan}B{$startTagDiv}C{$closeTagDiv}{$closeTagSpan} D'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   it('should serialize simple ICU for `GetMsg()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{age, plural, 10 {ten} other {other}}')) | 
					
						
							|  |  |  |  |         .toEqual('{VAR_PLURAL, plural, 10 {ten} other {other}}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize nested ICUs for `GetMsg()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize( | 
					
						
							|  |  |  |  |                '{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}')) | 
					
						
							|  |  |  |  |         .toEqual( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {one} 2 {two} other {2+}}} other {other}}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize ICU with nested HTML for `GetMsg()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{age, plural, 10 {<b>ten</b>} other {<div class="A">other</div>}}')) | 
					
						
							|  |  |  |  |         .toEqual( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}other{CLOSE_TAG_DIV}}}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize ICU with nested HTML containing further ICUs for `GetMsg()`', () => { | 
					
						
							|  |  |  |  |     expect( | 
					
						
							|  |  |  |  |         serialize( | 
					
						
							|  |  |  |  |             '{gender, select, male {male} female {female} other {other}}<div>{gender, select, male {male} female {female} other {other}}</div>')) | 
					
						
							|  |  |  |  |         .toEqual('{$icu}{$startTagDiv}{$icu}{$closeTagDiv}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | describe('serializeI18nMessageForLocalize', () => { | 
					
						
							|  |  |  |  |   const serialize = (input: string) => { | 
					
						
							| 
									
										
										
										
											2020-10-29 09:45:01 +00:00
										 |  |  |  |     const tree = parse(`<div i18n>${input}</div>`, {leadingTriviaChars: LEADING_TRIVIA_CHARS}); | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |     const root = tree.nodes[0] as t.Element; | 
					
						
							|  |  |  |  |     return serializeI18nMessageForLocalize(root.i18n as i18n.Message); | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize plain text for `$localize()`', () => { | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |     expect(serialize('Some text')) | 
					
						
							|  |  |  |  |         .toEqual({messageParts: [literal('Some text')], placeHolders: []}); | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize text with interpolation for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }} done')).toEqual({ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       messageParts: [literal('Some text '), literal(' and '), literal(' done')], | 
					
						
							|  |  |  |  |       placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')], | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |   it('should compute source-spans when serializing text with interpolation for `$localize()`', | 
					
						
							|  |  |  |  |      () => { | 
					
						
							|  |  |  |  |        const {messageParts, placeHolders} = | 
					
						
							|  |  |  |  |            serialize('Some text {{ valueA }} and {{ valueB + valueC }} done'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |        expect(messageParts[0].text).toEqual('Some text '); | 
					
						
							|  |  |  |  |        expect(messageParts[0].sourceSpan.toString()).toEqual('Some text '); | 
					
						
							|  |  |  |  |        expect(messageParts[1].text).toEqual(' and '); | 
					
						
							|  |  |  |  |        expect(messageParts[1].sourceSpan.toString()).toEqual(' and '); | 
					
						
							|  |  |  |  |        expect(messageParts[2].text).toEqual(' done'); | 
					
						
							|  |  |  |  |        expect(messageParts[2].sourceSpan.toString()).toEqual(' done'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |        expect(placeHolders[0].text).toEqual('INTERPOLATION'); | 
					
						
							|  |  |  |  |        expect(placeHolders[0].sourceSpan.toString()).toEqual('{{ valueA }}'); | 
					
						
							|  |  |  |  |        expect(placeHolders[1].text).toEqual('INTERPOLATION_1'); | 
					
						
							|  |  |  |  |        expect(placeHolders[1].sourceSpan.toString()).toEqual('{{ valueB + valueC }}'); | 
					
						
							|  |  |  |  |      }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   it('should serialize text with interpolation at start for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{{ valueA }} and {{ valueB + valueC }} done')).toEqual({ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       messageParts: [literal(''), literal(' and '), literal(' done')], | 
					
						
							|  |  |  |  |       placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')], | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize text with interpolation at end for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}')).toEqual({ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       messageParts: [literal('Some text '), literal(' and '), literal('')], | 
					
						
							|  |  |  |  |       placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')], | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize only interpolation for `$localize()`', () => { | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |     expect(serialize('{{ valueB + valueC }}')).toEqual({ | 
					
						
							|  |  |  |  |       messageParts: [literal(''), literal('')], | 
					
						
							|  |  |  |  |       placeHolders: [placeholder('INTERPOLATION')] | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-06 12:26:48 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize interpolation with named placeholder for `$localize()`', () => { | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |     expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}')).toEqual({ | 
					
						
							|  |  |  |  |       messageParts: [literal(''), literal('')], | 
					
						
							|  |  |  |  |       placeHolders: [placeholder('PLACEHOLDER_NAME')] | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize content with HTML tags for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('A <span>B<div>C</div></span> D')).toEqual({ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       messageParts: [literal('A '), literal('B'), literal('C'), literal(''), literal(' D')], | 
					
						
							|  |  |  |  |       placeHolders: [ | 
					
						
							|  |  |  |  |         placeholder('START_TAG_SPAN'), placeholder('START_TAG_DIV'), placeholder('CLOSE_TAG_DIV'), | 
					
						
							|  |  |  |  |         placeholder('CLOSE_TAG_SPAN') | 
					
						
							|  |  |  |  |       ] | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |   it('should compute source-spans when serializing content with HTML tags for `$localize()`', | 
					
						
							|  |  |  |  |      () => { | 
					
						
							|  |  |  |  |        const {messageParts, placeHolders} = serialize('A <span>B<div>C</div></span> D'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |        expect(messageParts[0].text).toEqual('A '); | 
					
						
							|  |  |  |  |        expect(messageParts[0].sourceSpan.toString()).toEqual('A '); | 
					
						
							|  |  |  |  |        expect(messageParts[1].text).toEqual('B'); | 
					
						
							|  |  |  |  |        expect(messageParts[1].sourceSpan.toString()).toEqual('B'); | 
					
						
							|  |  |  |  |        expect(messageParts[2].text).toEqual('C'); | 
					
						
							|  |  |  |  |        expect(messageParts[2].sourceSpan.toString()).toEqual('C'); | 
					
						
							|  |  |  |  |        expect(messageParts[3].text).toEqual(''); | 
					
						
							|  |  |  |  |        expect(messageParts[3].sourceSpan.toString()).toEqual(''); | 
					
						
							|  |  |  |  |        expect(messageParts[4].text).toEqual(' D'); | 
					
						
							| 
									
										
										
										
											2020-10-29 09:45:01 +00:00
										 |  |  |  |        expect(messageParts[4].sourceSpan.toString()).toEqual('D'); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |        expect(placeHolders[0].text).toEqual('START_TAG_SPAN'); | 
					
						
							|  |  |  |  |        expect(placeHolders[0].sourceSpan.toString()).toEqual('<span>'); | 
					
						
							|  |  |  |  |        expect(placeHolders[1].text).toEqual('START_TAG_DIV'); | 
					
						
							|  |  |  |  |        expect(placeHolders[1].sourceSpan.toString()).toEqual('<div>'); | 
					
						
							|  |  |  |  |        expect(placeHolders[2].text).toEqual('CLOSE_TAG_DIV'); | 
					
						
							|  |  |  |  |        expect(placeHolders[2].sourceSpan.toString()).toEqual('</div>'); | 
					
						
							|  |  |  |  |        expect(placeHolders[3].text).toEqual('CLOSE_TAG_SPAN'); | 
					
						
							|  |  |  |  |        expect(placeHolders[3].sourceSpan.toString()).toEqual('</span>'); | 
					
						
							|  |  |  |  |      }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-28 21:01:31 +00:00
										 |  |  |  |   it('should create the correct source-spans when there are two placeholders next to each other', | 
					
						
							|  |  |  |  |      () => { | 
					
						
							|  |  |  |  |        const {messageParts, placeHolders} = serialize('<b>{{value}}</b>'); | 
					
						
							|  |  |  |  |        expect(messageParts[0].text).toEqual(''); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(messageParts[0].sourceSpan)).toEqual('"" (10-10)'); | 
					
						
							|  |  |  |  |        expect(messageParts[1].text).toEqual(''); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(messageParts[1].sourceSpan)).toEqual('"" (13-13)'); | 
					
						
							|  |  |  |  |        expect(messageParts[2].text).toEqual(''); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(messageParts[2].sourceSpan)).toEqual('"" (22-22)'); | 
					
						
							|  |  |  |  |        expect(messageParts[3].text).toEqual(''); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(messageParts[3].sourceSpan)).toEqual('"" (26-26)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |        expect(placeHolders[0].text).toEqual('START_BOLD_TEXT'); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(placeHolders[0].sourceSpan)).toEqual('"<b>" (10-13)'); | 
					
						
							|  |  |  |  |        expect(placeHolders[1].text).toEqual('INTERPOLATION'); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(placeHolders[1].sourceSpan)).toEqual('"{{value}}" (13-22)'); | 
					
						
							|  |  |  |  |        expect(placeHolders[2].text).toEqual('CLOSE_BOLD_TEXT'); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(placeHolders[2].sourceSpan)).toEqual('"</b>" (22-26)'); | 
					
						
							|  |  |  |  |      }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 09:45:01 +00:00
										 |  |  |  |   it('should create the correct placeholder source-spans when there is skipped leading whitespace', | 
					
						
							|  |  |  |  |      () => { | 
					
						
							|  |  |  |  |        const {messageParts, placeHolders} = serialize('<b>   {{value}}</b>'); | 
					
						
							|  |  |  |  |        expect(messageParts[0].text).toEqual(''); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(messageParts[0].sourceSpan)).toEqual('"" (10-10)'); | 
					
						
							|  |  |  |  |        expect(messageParts[1].text).toEqual('   '); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(messageParts[1].sourceSpan)).toEqual('"   " (13-16)'); | 
					
						
							|  |  |  |  |        expect(messageParts[2].text).toEqual(''); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(messageParts[2].sourceSpan)).toEqual('"" (25-25)'); | 
					
						
							|  |  |  |  |        expect(messageParts[3].text).toEqual(''); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(messageParts[3].sourceSpan)).toEqual('"" (29-29)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |        expect(placeHolders[0].text).toEqual('START_BOLD_TEXT'); | 
					
						
							| 
									
										
										
										
											2021-01-21 21:48:42 +01:00
										 |  |  |  |        expect(humanizeSourceSpan(placeHolders[0].sourceSpan)).toEqual('"<b>" (10-13)'); | 
					
						
							| 
									
										
										
										
											2020-10-29 09:45:01 +00:00
										 |  |  |  |        expect(placeHolders[1].text).toEqual('INTERPOLATION'); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(placeHolders[1].sourceSpan)).toEqual('"{{value}}" (16-25)'); | 
					
						
							|  |  |  |  |        expect(placeHolders[2].text).toEqual('CLOSE_BOLD_TEXT'); | 
					
						
							|  |  |  |  |        expect(humanizeSourceSpan(placeHolders[2].sourceSpan)).toEqual('"</b>" (25-29)'); | 
					
						
							|  |  |  |  |      }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   it('should serialize simple ICU for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{age, plural, 10 {ten} other {other}}')).toEqual({ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |       messageParts: [literal('{VAR_PLURAL, plural, 10 {ten} other {other}}')], | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |       placeHolders: [] | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize nested ICUs for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize( | 
					
						
							|  |  |  |  |                '{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}')) | 
					
						
							|  |  |  |  |         .toEqual({ | 
					
						
							|  |  |  |  |           messageParts: [ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |             literal( | 
					
						
							|  |  |  |  |                 '{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {one} 2 {two} other {2+}}} other {other}}') | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |           ], | 
					
						
							|  |  |  |  |           placeHolders: [] | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-14 12:20:32 +01:00
										 |  |  |  |   it('should serialize ICU with embedded HTML for `$localize()`', () => { | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |     expect(serialize('{age, plural, 10 {<b>ten</b>} other {<div class="A">other</div>}}')).toEqual({ | 
					
						
							|  |  |  |  |       messageParts: [ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |         literal( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}other{CLOSE_TAG_DIV}}}') | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |       ], | 
					
						
							|  |  |  |  |       placeHolders: [] | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-14 12:20:32 +01:00
										 |  |  |  |   it('should serialize ICU with embedded interpolation for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{age, plural, 10 {<b>ten</b>} other {{{age}} years old}}')).toEqual({ | 
					
						
							|  |  |  |  |       messageParts: [ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |         literal( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{INTERPOLATION} years old}}') | 
					
						
							| 
									
										
										
										
											2020-08-14 12:20:32 +01:00
										 |  |  |  |       ], | 
					
						
							|  |  |  |  |       placeHolders: [] | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   it('should serialize ICU with nested HTML containing further ICUs for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect( | 
					
						
							|  |  |  |  |         serialize( | 
					
						
							|  |  |  |  |             '{gender, select, male {male} female {female} other {other}}<div>{gender, select, male {male} female {female} other {other}}</div>')) | 
					
						
							|  |  |  |  |         .toEqual({ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |           messageParts: [literal(''), literal(''), literal(''), literal(''), literal('')], | 
					
						
							|  |  |  |  |           placeHolders: [ | 
					
						
							|  |  |  |  |             placeholder('ICU'), placeholder('START_TAG_DIV'), placeholder('ICU'), | 
					
						
							|  |  |  |  |             placeholder('CLOSE_TAG_DIV') | 
					
						
							|  |  |  |  |           ] | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |         }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-14 12:20:32 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize nested ICUs with embedded interpolation for `$localize()`', () => { | 
					
						
							|  |  |  |  |     expect( | 
					
						
							|  |  |  |  |         serialize( | 
					
						
							|  |  |  |  |             '{age, plural, 10 {ten {size, select, 1 {{{ varOne }}} 2 {{{ varTwo }}} other {2+}}} other {other}}')) | 
					
						
							|  |  |  |  |         .toEqual({ | 
					
						
							|  |  |  |  |           messageParts: [ | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  |             literal( | 
					
						
							|  |  |  |  |                 '{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {{INTERPOLATION}} 2 {{INTERPOLATION_1}} other {2+}}} other {other}}') | 
					
						
							| 
									
										
										
										
											2020-08-14 12:20:32 +01:00
										 |  |  |  |           ], | 
					
						
							|  |  |  |  |           placeHolders: [] | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | describe('serializeIcuNode', () => { | 
					
						
							|  |  |  |  |   const serialize = (input: string) => { | 
					
						
							|  |  |  |  |     const tree = parse(`<div i18n>${input}</div>`); | 
					
						
							|  |  |  |  |     const rooti18n = (tree.nodes[0] as t.Element).i18n as i18n.Message; | 
					
						
							|  |  |  |  |     return serializeIcuNode(rooti18n.nodes[0] as i18n.Icu); | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize a simple ICU', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{age, plural, 10 {ten} other {other}}')) | 
					
						
							|  |  |  |  |         .toEqual('{VAR_PLURAL, plural, 10 {ten} other {other}}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-14 12:20:32 +01:00
										 |  |  |  |   it('should serialize a nested ICU', () => { | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |     expect(serialize( | 
					
						
							|  |  |  |  |                '{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}')) | 
					
						
							|  |  |  |  |         .toEqual( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {one} 2 {two} other {2+}}} other {other}}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-10-18 10:08:51 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 18:02:17 +01:00
										 |  |  |  |   it('should serialize ICU with nested HTML', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{age, plural, 10 {<b>ten</b>} other {<div class="A">other</div>}}')) | 
					
						
							|  |  |  |  |         .toEqual( | 
					
						
							|  |  |  |  |             '{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}other{CLOSE_TAG_DIV}}}'); | 
					
						
							| 
									
										
										
										
											2018-10-12 14:34:38 -07:00
										 |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-14 12:20:32 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   it('should serialize an ICU with embedded interpolations', () => { | 
					
						
							|  |  |  |  |     expect(serialize('{age, select, 10 {ten} other {{{age}} years old}}')) | 
					
						
							|  |  |  |  |         .toEqual('{VAR_SELECT, select, 10 {ten} other {{INTERPOLATION} years old}}'); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-07-16 12:18:32 -07:00
										 |  |  |  | }); | 
					
						
							| 
									
										
										
										
											2020-08-31 16:27:04 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | function literal(text: string, span: any = jasmine.any(ParseSourceSpan)): o.LiteralPiece { | 
					
						
							|  |  |  |  |   return new o.LiteralPiece(text, span); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function placeholder(name: string, span: any = jasmine.any(ParseSourceSpan)): o.PlaceholderPiece { | 
					
						
							|  |  |  |  |   return new o.PlaceholderPiece(name, span); | 
					
						
							| 
									
										
										
										
											2020-10-28 21:01:31 +00:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | function humanizeSourceSpan(span: ParseSourceSpan): string { | 
					
						
							|  |  |  |  |   return `"${span.toString()}" (${span.start.offset}-${span.end.offset})`; | 
					
						
							|  |  |  |  | } |