| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @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 {escapeRegExp} from '@angular/core/src/facade/lang'; | 
					
						
							| 
									
										
										
										
											2016-09-27 17:12:25 -07:00
										 |  |  | import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  | import {MessageBundle} from '../../../src/i18n/message_bundle'; | 
					
						
							|  |  |  | import {Xtb} from '../../../src/i18n/serializers/xtb'; | 
					
						
							| 
									
										
										
										
											2016-08-01 12:19:09 -07:00
										 |  |  | import {HtmlParser} from '../../../src/ml_parser/html_parser'; | 
					
						
							|  |  |  | import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config'; | 
					
						
							| 
									
										
										
										
											2016-08-01 14:43:20 -07:00
										 |  |  | import {serializeNodes} from '../../ml_parser/ast_serializer_spec'; | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function main(): void { | 
					
						
							|  |  |  |   describe('XTB serializer', () => { | 
					
						
							|  |  |  |     let serializer: Xtb; | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |     let htmlParser: HtmlParser; | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |     function loadAsText(template: string, xtb: string): {[id: string]: string} { | 
					
						
							|  |  |  |       let messageBundle = new MessageBundle(htmlParser, [], {}); | 
					
						
							|  |  |  |       messageBundle.updateFromTemplate(template, 'url', DEFAULT_INTERPOLATION_CONFIG); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const asAst = serializer.load(xtb, 'url', messageBundle); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       let asText: {[id: string]: string} = {}; | 
					
						
							| 
									
										
										
										
											2016-08-01 14:43:20 -07:00
										 |  |  |       Object.keys(asAst).forEach(id => { asText[id] = serializeNodes(asAst[id]).join(''); }); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return asText; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       htmlParser = new HtmlParser(); | 
					
						
							|  |  |  |       serializer = new Xtb(htmlParser, DEFAULT_INTERPOLATION_CONFIG); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     describe('load', () => { | 
					
						
							| 
									
										
										
										
											2016-07-29 13:07:01 -07:00
										 |  |  |       it('should load XTB files with a doctype', () => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         const HTML = `<div i18n>bar</div>`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-29 13:07:01 -07:00
										 |  |  |         const XTB = `<?xml version="1.0" encoding="UTF-8"?>
 | 
					
						
							|  |  |  | <!DOCTYPE translationbundle [<!ELEMENT translationbundle (translation)*> | 
					
						
							|  |  |  | <!ATTLIST translationbundle lang CDATA #REQUIRED> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <!ELEMENT translation (#PCDATA|ph)*> | 
					
						
							|  |  |  | <!ATTLIST translation id CDATA #REQUIRED> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <!ELEMENT ph EMPTY> | 
					
						
							|  |  |  | <!ATTLIST ph name CDATA #REQUIRED> | 
					
						
							|  |  |  | ]> | 
					
						
							|  |  |  | <translationbundle> | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |   <translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226">rab</translation> | 
					
						
							| 
									
										
										
										
											2016-07-29 13:07:01 -07:00
										 |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         expect(loadAsText(HTML, XTB)).toEqual({'28a86c8a00ae573b2bac698d6609316dc7b4a226': 'rab'}); | 
					
						
							| 
									
										
										
										
											2016-07-29 13:07:01 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       it('should load XTB files without placeholders', () => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         const HTML = `<div i18n>bar</div>`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-29 13:07:01 -07:00
										 |  |  |         const XTB = `<?xml version="1.0" encoding="UTF-8"?>
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | <translationbundle> | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |   <translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226">rab</translation> | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         expect(loadAsText(HTML, XTB)).toEqual({'28a86c8a00ae573b2bac698d6609316dc7b4a226': 'rab'}); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should load XTB files with placeholders', () => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         const HTML = `<div i18n><p>bar</p></div>`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-29 13:07:01 -07:00
										 |  |  |         const XTB = `<?xml version="1.0" encoding="UTF-8"?>
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | <translationbundle> | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |   <translation id="7de4d8ff1e42b7b31da6204074818236a9a5317f"><ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/></translation> | 
					
						
							|  |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         expect(loadAsText(HTML, XTB)).toEqual({ | 
					
						
							|  |  |  |           '7de4d8ff1e42b7b31da6204074818236a9a5317f': '<p>rab</p>' | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should replace ICU placeholders with their translations', () => { | 
					
						
							|  |  |  |         const HTML = `<div i18n>-{ count, plural, =0 {<p>bar</p>}}-</div>`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 21:26:03 -07:00
										 |  |  |         const XTB = `<?xml version="1.0" encoding="UTF-8" ?>
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  | <translationbundle> | 
					
						
							|  |  |  |   <translation id="eb404e202fed4846e25e7d9ac1fcb719fe4da257">*<ph name="ICU"/>*</translation> | 
					
						
							|  |  |  |   <translation id="fc92b9b781194a02ab773129c8c5a7fc0735efd7">{ count, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation> | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         expect(loadAsText(HTML, XTB)).toEqual({ | 
					
						
							|  |  |  |           'eb404e202fed4846e25e7d9ac1fcb719fe4da257': `*{ count, plural, =1 {<p>rab</p>}}*`, | 
					
						
							|  |  |  |           'fc92b9b781194a02ab773129c8c5a7fc0735efd7': `{ count, plural, =1 {<p>rab</p>}}`, | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should load complex XTB files', () => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         const HTML = `
 | 
					
						
							|  |  |  | <div i18n>foo <b>bar</b> {{ a + b }}</div> | 
					
						
							|  |  |  | <div i18n>{ count, plural, =0 {<p>bar</p>}}</div> | 
					
						
							|  |  |  | <div i18n="m|d">foo</div> | 
					
						
							|  |  |  | <div i18n>{ count, plural, =0 {{ sex, gender, other {<p>bar</p>}} }}</div>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 21:26:03 -07:00
										 |  |  |         const XTB = `<?xml version="1.0" encoding="UTF-8" ?>
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | <translationbundle> | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |   <translation id="7103b4b13b616270a0044efade97d8b4f96f2ca6"><ph name="INTERPOLATION"/><ph name="START_BOLD_TEXT"/>rab<ph name="CLOSE_BOLD_TEXT"/> oof</translation> | 
					
						
							|  |  |  |   <translation id="fc92b9b781194a02ab773129c8c5a7fc0735efd7">{ count, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation> | 
					
						
							|  |  |  |   <translation id="db3e0a6a5a96481f60aec61d98c3eecddef5ac23">oof</translation> | 
					
						
							|  |  |  |   <translation id="e3bf2d706c3da16ce05658e07f62f0519f7c561c">{ count, plural, =1 {{ sex, gender, male {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}} }}</translation> | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         expect(loadAsText(HTML, XTB)).toEqual({ | 
					
						
							|  |  |  |           '7103b4b13b616270a0044efade97d8b4f96f2ca6': `{{ a + b }}<b>rab</b> oof`, | 
					
						
							|  |  |  |           'fc92b9b781194a02ab773129c8c5a7fc0735efd7': `{ count, plural, =1 {<p>rab</p>}}`, | 
					
						
							|  |  |  |           'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': `oof`, | 
					
						
							|  |  |  |           'e3bf2d706c3da16ce05658e07f62f0519f7c561c': | 
					
						
							|  |  |  |               `{ count, plural, =1 {{ sex, gender, male {<p>rab</p>}} }}`, | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('errors', () => { | 
					
						
							|  |  |  |       it('should throw on nested <translationbundle>', () => { | 
					
						
							|  |  |  |         const XTB = | 
					
						
							|  |  |  |             '<translationbundle><translationbundle></translationbundle></translationbundle>'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         expect(() => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |           loadAsText('', XTB); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |         }).toThrowError(/<translationbundle> elements can not be nested/); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should throw when a <translation> has no id attribute', () => { | 
					
						
							| 
									
										
										
										
											2016-07-29 13:07:01 -07:00
										 |  |  |         const XTB = `<translationbundle>
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   <translation></translation> | 
					
						
							|  |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         expect(() => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |           loadAsText('', XTB); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |         }).toThrowError(/<translation> misses the "id" attribute/); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should throw when a placeholder has no name attribute', () => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         const HTML = '<div i18n>give me a message</div>'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-29 13:07:01 -07:00
										 |  |  |         const XTB = `<translationbundle>
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |   <translation id="8de97c6a35252d9409dcaca0b8171c952740b28c"><ph /></translation> | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         expect(() => { loadAsText(HTML, XTB); }).toThrowError(/<ph> misses the "name" attribute/); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should throw when a placeholder is not present in the source message', () => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         const HTML = `<div i18n>bar</div>`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const XTB = `<?xml version="1.0" encoding="UTF-8"?>
 | 
					
						
							|  |  |  | <translationbundle> | 
					
						
							|  |  |  |   <translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226"><ph name="UNKNOWN"/></translation> | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         expect(() => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |           loadAsText(HTML, XTB); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |         }).toThrowError(/The placeholder "UNKNOWN" does not exists in the source message/); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should throw when the translation results in invalid html', () => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |       const HTML = `<div i18n><p>bar</p></div>`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const XTB = `<?xml version="1.0" encoding="UTF-8"?>
 | 
					
						
							|  |  |  | <translationbundle> | 
					
						
							|  |  |  |   <translation id="7de4d8ff1e42b7b31da6204074818236a9a5317f">rab<ph name="CLOSE_PARAGRAPH"/></translation> | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | </translationbundle>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(() => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         loadAsText(HTML, XTB); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       }).toThrowError(/xtb parse errors:\nUnexpected closing tag "p"/); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should throw on unknown tags', () => { | 
					
						
							|  |  |  |       const XTB = `<what></what>`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(() => { | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |         loadAsText('', XTB); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       }).toThrowError(new RegExp(escapeRegExp(`Unexpected tag ("[ERROR ->]<what></what>")`))); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  |     it('should throw when trying to save an xtb file', | 
					
						
							|  |  |  |        () => { expect(() => { serializer.write({}); }).toThrowError(/Unsupported/); }); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  | } |